דוגמה לאינטגרציה של Spring Hibernate (Spring 4 + Hibernate 3 ו-Hibernate 4)

אביב הוא אחד מה-Frameworks הנפוצים ביותר ב-Java EE, ו־Hibernate הוא ה-Framework הפופולרי ביותר לניהול ORM. זו הסיבה שלמערכות מידע עסקיות רבות משתמשות בשילוב Spring Hibernate. לאחרונה כתבתי הרבה עבור Spring Tutorial ו־Hibernate Tutorial, ולכן פוסט עבור spring hibernate integration היה חייב מזמן.

Spring Hibernate

היום במדריך זה, נשתמש ב-Spring 4 ונשלב אותו עם Hibernate 3 ולאחר מכן נעדכן את אותו הפרויקט להשתמש ב-Hibernate 4. מאחר ויש הרבה גרסאות של Spring ו-Hibernate כאחד והתקן Spring ORM תומך בשני Hibernate 3 ו-Hibernate 4, נכון שאני מפרט את כל התלותים שהשתמשתי בהם בפרויקט שלי. שים לב ששם לבתי כי כל גרסאות ה-Spring וה-Hibernate אינן תמיכות, וגרסאות הבאות עבורי עבדו, אז אני חושב שהן תואמות. אם אתה משתמש בגרסאות אחרות ומקבל java.lang.NoClassDefFoundError, זה אומר שהן אינן תואמות. לעיתים רבות זה קורה משום שכיתות ה-Hibernate מועברות מחבילה אחת לאחרת, וזה גורם לשגיאה זו. לדוגמה, הכיתה org.hibernate.engine.FilterDefinition מועברת ל-org.hibernate.engine.spi.FilterDefinition בגרסאות ה-Hibernate האחרונות.

  • גרסת ה-Spring Framework: 4.0.3.RELEASE
  • גרסת Hibernate Core ו-Hibernate EntityManager: 3.6.9.Final ו-4.3.5.Final
  • גרסת Spring ORM: 4.0.3.RELEASE

הגדרת מסד נתונים

I am using MySQL database for my project, so below setup.sql script will create the necessary table for this example.

CREATE TABLE `Person` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(20) NOT NULL DEFAULT '',
  `country` varchar(20) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
commit;

מבנה פרויקט לדוגמת שילוב Spring Hibernate

התמונה למטה מראה את מבנה הפרויקט הסופי, נעבור דרך כל אחד מהרכיבים בנפרד.

תלותי Maven

נתחיל על ידי הסתכלות בקובץ ה-pom.xml שלנו לכל התלותים הדרושים וגרסאותיהם.

<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>org.springframework.samples</groupId>
	<artifactId>SpringHibernateExample</artifactId>
	<version>0.0.1-SNAPSHOT</version>

	<properties>

		<!-- Generic properties -->
		<java.version>1.6</java.version>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

		<!-- Spring -->
		<spring-framework.version>4.0.3.RELEASE</spring-framework.version>

		<!-- Hibernate / JPA -->
		<!-- <hibernate.version>4.3.5.Final</hibernate.version> -->
		<hibernate.version>3.6.9.Final</hibernate.version>

		<!-- Logging -->
		<logback.version>1.0.13</logback.version>
		<slf4j.version>1.7.5</slf4j.version>

	</properties>

	<dependencies>
		<!-- Spring and Transactions -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>${spring-framework.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-tx</artifactId>
			<version>${spring-framework.version}</version>
		</dependency>

		<!-- Spring ORM support -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-orm</artifactId>
			<version>${spring-framework.version}</version>
		</dependency>

		<!-- Logging with SLF4J & LogBack -->
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-api</artifactId>
			<version>${slf4j.version}</version>
			<scope>compile</scope>
		</dependency>
		<dependency>
			<groupId>ch.qos.logback</groupId>
			<artifactId>logback-classic</artifactId>
			<version>${logback.version}</version>
			<scope>runtime</scope>
		</dependency>

		<!-- Hibernate -->
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-entitymanager</artifactId>
			<version>${hibernate.version}</version>
		</dependency>
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-core</artifactId>
			<version>${hibernate.version}</version>
		</dependency>

		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>5.1.9</version>
		</dependency>
		<dependency>
			<groupId>commons-dbcp</groupId>
			<artifactId>commons-dbcp</artifactId>
			<version>1.4</version>
		</dependency>
	</dependencies>
</project>

התלותים החשובות לפרויקט אינטגרציה של Spring ו-Hibernate הם:

  • spring-context ו-spring-tx עבור פונקציות ה-Spring הבסיסיות. שים לב שאני משתמש בגרסה 4.0.3.RELEASE.
  • תלות spring-orm לתמיכה ב-ORM של Spring, זה דרוש עבור אינטגרציה של היברנייט בפרויקט ה-Spring שלנו.
  • תלות hibernate-entitymanager ו-hibernate-core עבור מסגרת ה-Hibernate. שים לב שהגרסה היא 3.6.9.Final, לשימוש ב-Hibernate 4 כל שנדרש הוא לשנות אותה ל-4.3.5.Final כפי שמסומן בקובץ ה-pom.xml למעלה.
  • mysql-connector-java עבור מנהל MySQL עבור חיבור לבסיס הנתונים.

מודל Class או Entity Bean

אנו יכולים להשתמש במיפוי מבוסס קובץ Hibernate XML וכן במיפוי מבוסס רישום JPA. כאן אני משתמש בסימוני JPA למיפוי מאחר שהיברנייט מספק יישום JPA.

package com.journaldev.model;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

/**
 * Entity bean with JPA annotations
 * Hibernate provides JPA implementation
 * @author pankaj
 *
 */
@Entity
@Table(name="Person")
public class Person {

	@Id
	@Column(name="id")
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	private int id;
	
	private String name;
	
	private String country;

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getCountry() {
		return country;
	}

	public void setCountry(String country) {
		this.country = country;
	}
	
	@Override
	public String toString(){
		return "id="+id+", name="+name+", country="+country;
	}
}

כיתות DAO

נממש שני שיטות בכיתות ה-DAO שלנו, הראשונה לשמור את אובייקט האדם בטבלה והשנייה שתפקידה לאחזר את כל הרשומות מהטבלה ולהחזיר את רשימת האנשים.

package com.journaldev.dao;

import java.util.List;

import com.journaldev.model.Person;

public interface PersonDAO {

	public void save(Person p);
	
	public List<Person> list();
	
}

מימוש כיתת ה-DAO ייראה כך.

package com.journaldev.dao;

import java.util.List;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;

import com.journaldev.model.Person;

public class PersonDAOImpl implements PersonDAO {

	private SessionFactory sessionFactory;

    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }
    
	@Override
	public void save(Person p) {
		Session session = this.sessionFactory.openSession();
		Transaction tx = session.beginTransaction();
		session.persist(p);
		tx.commit();
		session.close();
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<Person> list() {
		Session session = this.sessionFactory.openSession();
		List<Person> personList = session.createQuery("from Person").list();
		session.close();
		return personList;
	}

}

שים לב שזהו המקום היחיד בו אנו משתמשים בכיתות הקשורות ל-Hibernate. התבנית הזו הופכת את המימוש שלנו לגמיש וקל להעברה מטכנולוגיה אחת לאחרת. לדוגמה, אם נרצה להשתמש בספריית iBatis ORM, כל שנדרש הוא לספק מימוש DAO עבור iBatis ולשנות את קובץ התצורה של פענוח הפקודות של Spring. בדוגמה לעיל, אני משתמש בניהול העסקאות של סשן ה-Hibernate. אך יש אפשרות גם להשתמש בניהול העסקאות דקלרטיבי של Spring באמצעות הסימון @Transactional, קרא עוד ב-ניהול העסקאות Spring.

קובץ תצורת חזית של Spring עבור אינטגרציה עם Hibernate 3

נבחן תחילה את הקבצי התצורה של יצירת הפעל שאנו זקוקים להשתמש בהם לאינטגרציה עם Hibernate 3, נחקור לעומק מאוחר יותר.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="https://www.springframework.org/schema/beans"
	xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xmlns:aop="https://www.springframework.org/schema/aop"
	xmlns:tx="https://www.springframework.org/schema/tx"
	xsi:schemaLocation="https://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
		https://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop-4.0.xsd
		https://www.springframework.org/schema/tx https://www.springframework.org/schema/tx/spring-tx-4.0.xsd">

	<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
		destroy-method="close">
		<property name="driverClassName" value="com.mysql.jdbc.Driver" />
		<property name="url" value="jdbc:mysql://localhost:3306/TestDB" />
		<property name="username" value="pankaj" />
		<property name="password" value="pankaj123" />
	</bean>

<!-- Hibernate 3 XML SessionFactory Bean definition-->
<!-- 	<bean id="hibernate3SessionFactory"
		class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
		<property name="dataSource" ref="dataSource" />
		<property name="mappingResources">
			<list>
				<value>person.hbm.xml</value>
			</list>
		</property>
		<property name="hibernateProperties">
			<value>
				hibernate.dialect=org.hibernate.dialect.MySQLDialect
			</value>
		</property>
	</bean> -->

<!-- Hibernate 3 Annotation SessionFactory Bean definition-->
	<bean id="hibernate3AnnotatedSessionFactory"
		class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
		<property name="dataSource" ref="dataSource" />
		<property name="annotatedClasses">
			<list>
				<value>com.journaldev.model.Person</value>
			</list>
		</property>
		<property name="hibernateProperties">
			<props>
				<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
				<prop key="hibernate.current_session_context_class">thread</prop>
				<prop key="hibernate.show_sql">false</prop>
			</props>
		</property>
	</bean>
	
	<bean id="personDAO" class="com.journaldev.dao.PersonDAOImpl">
		<property name="sessionFactory" ref="hibernate3AnnotatedSessionFactory" />
	</bean>
</beans>

ישנם שני אמצעים שבאפשרותנו לספק פרטי חיבור למסד נתונים ל-Hibernate, ראשית על ידי מעבר הכל בתוך hibernateProperties ושנית על ידי יצירת מקור נתונים ואז מעברו ל-Hibernate. אני מעדיף את הגישה השנייה, זו הסיבה להכללת תלות ב-Apache Commons DBCP ליצירת BasicDataSource על ידי הגדרת מאפייני חיבור למסד נתונים. לאינטגרציה בין Spring ו-Hibernate 3, Spring ORM מספקת שתי קבוצות – org.springframework.orm.hibernate3.LocalSessionFactoryBean כאשר המיפויים של Hibernate הם על בסיס XML ו- org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean למיפויים על בסיס הערות. סיפקתי תצורת Bean פשוטה של LocalSessionFactoryBean בהערות, אם אתה משתמש במיפויים על בסיס XML. AnnotationSessionFactoryBean מרחיב את המחלקה LocalSessionFactoryBean, ולכן יש לה את כל המאפיינים הבסיסיים לאינטגרציה עם Hibernate. המאפיינים הם מובנים עצמם ובעיקר קשורים ל-Hibernate, ולכן אני לא אתחיל להסביר עליהם מדי בעומק. אך אם אתה שואל מאיפה באים hibernateProperties, annotatedClasses, עליך להסתכל בקוד המקור של מחלקת ה-Bean. שים לב להגדרת ה-Bean של personDAO, כפי שאמרתי מקודם, אם עלינו לעבור לפריסה אחרת של ORM, עלינו לשנות את מחלקת היישום כאן ולהגדיר כל מאפיינים אחרים שאנו זקוקים.

תוכנית מבחן Spring 4 Hibernate 3

הכנה היא מוכנה כעת, בואו נכתוב תוכנית פשוטה כדי לבדוק את היישום שלנו.

package com.journaldev.main;

import java.util.List;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.journaldev.dao.PersonDAO;
import com.journaldev.model.Person;

public class SpringHibernateMain {

	public static void main(String[] args) {

		ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
		
		PersonDAO personDAO = context.getBean(PersonDAO.class);
		
		Person person = new Person();
		person.setName("Pankaj"); person.setCountry("India");
		
		personDAO.save(person);
		
		System.out.println("Person::"+person);
		
		List list = personDAO.list();
		
		for(Person p : list){
			System.out.println("Person List::"+p);
		}
		//סגירת משאבים
		context.close();	
	}
}

כאשר אנו מפעילים את התוכנית שנכתבה למעלה, אנו מקבלים המון פלט הקשור ל-Hibernate מכיוון שלא הגדרתי את התיעודים בצורה תקינה, אבל זה מחוץ לתחום של המדריך הזה. אך אנו מקבלים את הפלט הבא שנוצר על ידי התוכנית שלנו.

Person::id=3, name=Pankaj, country=India
Person List::id=1, name=Pankaj, country=India
Person List::id=2, name=Pankaj, country=India
Person List::id=3, name=Pankaj, country=India

שינויים באינטגרציה של Spring 4 Hibernate 4

כעת בואו נשנה את היישום שלנו כך שישתמש ב-Hibernate 4 במקום Hibernate 3. למעבר זה, אנו צריכים לבצע רק את השינויים בהגדרה הבאים.

  1. שינוי גרסת ה-Hibernate ל-4.3.5.Final בקובץ pom.xml, כפי שמוצג בהערות למעלה.

  2. שנה את קובץ התצורה של Spring Bean, עד כה אתה בוודאי הבנת שקובץ התצורה של Spring Bean הוא המפתח לאינטגרציה של Spring ומסגרת Hibernate. קובץ התצורה של Spring Bean להלן יעבוד עבור גרסאות Spring 4 ו-Hibernate 4.

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="https://www.springframework.org/schema/beans"
    	xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xmlns:aop="https://www.springframework.org/schema/aop"
    	xmlns:tx="https://www.springframework.org/schema/tx"
    	xsi:schemaLocation="https://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
    		https://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop-4.0.xsd
    		https://www.springframework.org/schema/tx https://www.springframework.org/schema/tx/spring-tx-4.0.xsd">
    
    	<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
    		destroy-method="close">
    		<property name="driverClassName" value="com.mysql.jdbc.Driver" />
    		<property name="url" value="jdbc:mysql://localhost:3306/TestDB" />
    		<property name="username" value="pankaj" />
    		<property name="password" value="pankaj123" />
    	</bean>
    
    
    <!-- Hibernate 4 SessionFactory Bean definition -->
    <bean id="hibernate4AnnotatedSessionFactory"
    		class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
    		<property name="dataSource" ref="dataSource" />
    		<property name="annotatedClasses">
    			<list>
    				<value>com.journaldev.model.Person</value>
    			</list>
    		</property>
    		<property name="hibernateProperties">
    			<props>
    				<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
    				<prop key="hibernate.current_session_context_class">thread</prop>
    				<prop key="hibernate.show_sql">false</prop>
    			</props>
    		</property>
    	</bean>
    
    	
    	<bean id="personDAO" class="com.journaldev.dao.PersonDAOImpl">
    		<property name="sessionFactory" ref="hibernate4AnnotatedSessionFactory" />
    	</bean>
    </beans>
    

    עבור Hibernate 4, אנו צריכים להשתמש בorg.springframework.orm.hibernate4.LocalSessionFactoryBean עבור bean של SessionFactory, Spring ORM איחדה שתי הכיתות עבור Hibernate 3 וכעת יש כיתה אחת בלבד, זה טוב כדי למנוע בלבול. כל התצורות האחרות זהות כמו קודם.

זהו, הפרויקט שלנו הועבר בהצלחה ל-Hibernate 4, נקי, או לא? פשוט שנה את המחלקה SpringHibernateMain כך שתשתמש ב־spring4.xml עבור הגדרת העצים וזה יעבוד בסדר, תקבל את אותה הפלט כמו קודם. באפשרותך להוריד את הפרויקט הסופי מהקישור למטה ולשחק עם עוד הגדרות כדי ללמוד עוד.

הורד פרויקט אינטגרציה של Spring Hibernate

Source:
https://www.digitalocean.com/community/tutorials/spring-hibernate-integration-example-tutorial