Na początek musimy wygenerować nową aplikację. Tutaj z pomocą przychodzi mechanizm archetypów (a ściślej mówiąc Archetype Plugin). Poprzez wywołanie poniższego polecenia stworzymy kompletną strukturę prostego projektu wraz z podstawowym plikiem pom.xm
mvn archetype:generate -DgroupId=hibernate_spring.example -DartifactId=hibernate-spring-derby-app -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
Zanim dokonamy importu projektu do Eclipse proponuję wykonać dodatkowy krok i utworzyć w wygenerowanym projekcie katalog src/main/resources
.
W celu zapewnienia sobie dalszej komfortowej pracy instalujemy plugin m2e dostępny w Eclipse Marketplace
Następnie importujemy projekt do Eclipse (File→Import…)
i edytujemy plik pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>hibernate-spring.example</groupId> <artifactId>hibernate_spring-derby-app</artifactId> <packaging>jar</packaging> <version>1.0-SNAPSHOT</version> <name>hibernate-spring-derby-app</name> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <!-- Shared version number properties --> <org.springframework.version>3.1.1.RELEASE</org.springframework.version> <org.slf4j.version>1.6.1</org.slf4j.version> <org.apache.derby.version>10.8.1.2</org.apache.derby.version> <org.hibernate.version>3.6.7.Final</org.hibernate.version> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.8.1</version> <scope>test</scope> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifactId> <version>${org.hibernate.version}</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.6.1</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.16</version> </dependency> <dependency> <groupId>javassist</groupId> <artifactId>javassist</artifactId> <version>3.12.0.GA</version> </dependency> <dependency> <groupId>org.apache.derby</groupId> <artifactId>derby</artifactId> <version>${org.apache.derby.version}</version> </dependency> <dependency> <groupId>org.apache.derby</groupId> <artifactId>derbytools</artifactId> <version>${org.apache.derby.version}</version> </dependency> <!-- Core utilities used by other modules. Define this if you use Spring Utility APIs (org.springframework.core.*/org.springframework.util.*) --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${org.springframework.version}</version> </dependency> <!-- Application Context (depends on spring-core, spring-expression, spring-aop, spring-beans) This is the central artifact for Spring's Dependency Injection Container and is generally always defined --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${org.springframework.version}</version> <exclusions> <exclusion> <artifactId>commons-logging</artifactId> <groupId>commons-logging</groupId> </exclusion> </exclusions> </dependency> <!-- Support for testing Spring applications with tools such as JUnit and TestNG This artifact is generally always defined with a 'test' scope for the integration testing framework and unit testing stubs --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>${org.springframework.version}</version> <scope>test</scope> </dependency> <!-- JDBC Data Access Library (depends on spring-core, spring-beans, spring-context, spring-tx) Define this if you use Spring's JdbcTemplate API (org.springframework.jdbc.*) --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>${org.springframework.version}</version> </dependency> <!-- Object-to-Relation-Mapping (ORM) integration with Hibernate, JPA, and iBatis. (depends on spring-core, spring-beans, spring-context, spring-tx) Define this if you need ORM (org.springframework.orm.*) --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-orm</artifactId> <version>${org.springframework.version}</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <compilerVersion>1.7</compilerVersion> </configuration> </plugin> </plugins> </build> </project>
Następnie w pliku src/main/resourcs/beans.xml
dodajemy konfigurację
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:lang="http://www.springframework.org/schema/lang" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-3.0.xsd"> <!-- DataSource Property --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName"> <value>org.apache.derby.jdbc.EmbeddedDriver</value> </property> <property name="url"> <!-- Tryb "w pamięci". Po usunięciu podprotokołu 'memory' baza będzie wykorzystywała domyślny tryb dyskowy --> <value>jdbc:derby:memory:db/db.derby;create=true</value> </property> </bean> <bean id="hibernateProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean"> <property name="properties"> <props> <prop key="hibernate.dialect">org.hibernate.dialect.DerbyDialect</prop> <prop key="hibernate.show_sql">true</prop> <prop key="hibernate.cache.provider_class">org.hibernate.cache.NoCacheProvider</prop> <!-- https://jira.springsource.org/browse/SPR-4207 <prop key="hibernate.current_session_context_class">thread</prop> --> <prop key="hibernate.hbm2ddl.auto">create-drop</prop> </props> </property> </bean> <!-- Hibernate SessionFactory --> <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> <property name="dataSource"> <ref local="dataSource" /> </property> <property name="hibernateProperties"> <ref bean="hibernateProperties" /> </property> <!-- <property name="exposeTransactionAwareSessionFactory"> <value>false</value> </property> --> </bean> <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory"> <ref local="sessionFactory" /> </property> </bean> </beans>
W celu sprawdzenia, czy jak dotąd wszystko działa napiszemy test jednostkowy.
package hibernate_spring.example; import org.hibernate.SessionFactory; import org.hibernate.classic.Session; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.transaction.annotation.Transactional; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = { "/beans.xml" }) public class AppTest { @Autowired SessionFactory sessionFactory; @Test @Transactional public void testSetup() { Session session = sessionFactory.getCurrentSession(); } }
W tym momencie workspace’a Eclipse wygląda mniej więcej tak
Czas na model domeny! Ten jest bardzo prosty, bo składa się z zaledwie dwóch bytów Student i Przedmiot połączonych relacją wiele-do-wielu (Baz danych 101). Pliki mapowań wyglądają następująco
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="hibernate_spring.example.Course" table="COURSE"> <meta attribute="class-description"> Informacje o przedmiocie </meta> <id name="courseId" type="long" column="COURSE_ID"> <generator class="native" /> </id> <property name="courseName" type="string" column="COURSE_NAME" /> <set name="students" inverse="true" table="STUDENT_COURSE"> <key column="COURSE_ID" /> <many-to-many column="STUDENT_ID" class="hibernate_spring.example.Student" /> </set> </class> </hibernate-mapping>
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="hibernate_spring.example.Student" table="STUDENT"> <meta attribute="class-description">Informacje o studencie</meta> <id name="studentId" type="long" column="STUDENT_ID"> <generator class="native" /> </id> <property name="studentName" type="string" length="100" not-null="true" column="STUDENT_NAME" /> <set name="courses" table="STUDENT_COURSE" cascade="all"> <key column="STUDENT_ID" /> <many-to-many column="COURSE_ID" class="hibernate_spring.example.Course" /> </set> </class> </hibernate-mapping>
Klasy POJO też są niezwykle proste
package hibernate_spring.example; import java.io.Serializable; import java.util.HashSet; import java.util.Set; public class Student implements Serializable { private Long studentId; private String studentName; private Set<Course> courses = new HashSet<Course>(0); public Student() { } public Student(String studentName) { this.studentName = studentName; } public Long getStudentId() { return studentId; } public void setStudentId(Long studentId) { this.studentId = studentId; } public String getStudentName() { return studentName; } public void setStudentName(String studentName) { this.studentName = studentName; } public Set<Course> getCourses() { return courses; } public void setCourses(Set<Course> courses) { this.courses = courses; } }
package hibernate_spring.example; import java.io.Serializable; import java.util.HashSet; import java.util.Set; public class Course implements Serializable { private Long courseId; private String courseName; private Set<Student> students = new HashSet<Student>(0); public Course() { } public Course(String courseName) { this.courseName = courseName; } public Set<Student> getStudents() { return students; } public void setStudents(Set<Student> students) { this.students = students; } public Long getCourseId() { return courseId; } public void setCourseId(Long courseId) { this.courseId = courseId; } public String getCourseName() { return courseName; } public void setCourseName(String courseName) { this.courseName = courseName; } }
Teraz musimy tylko zmodyfikować konfigurację beana faktorii sesji tak by wczytywał stworzone właśnie pliki mapowań
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> <property name="dataSource"> <ref local="dataSource" /> </property> <!-- <property name="exposeTransactionAwareSessionFactory"> <value>false</value> </property> --> <property name="hibernateProperties"> <ref bean="hibernateProperties" /> </property> <property name="mappingLocations"> <list> <value>classpath*:**/*.hbm.xml</value> </list> </property> </bean>
Do końca brakuje już niewiele – czas na DAO
public interface CourseDao { void store(Course newCourse); void delete(Course course); Collection<Course> findAll(); Course findById(long courseId); Course findByName(String courseName); } public interface StudentDao { void store(Student student); void delete(Student student); Student findById(long id); Collection<Student> findAll(); Student findByName(String studentName); }
i wykorzystującą je usługę CourseService
public interface CourseService { void enrollStudent(Course course, Student student); void disenrollStudent(Course course, Student student); Course createCourse(String courseName); Student createStudent(String studentName); void deleteCourse(Course course); void deleteStudent(Student student); Student findStudentByName(String studentName); Course findCourseByName(String courseName); }
Implementacja jest bardzo prosta i w związku z tym zostanie pominięta dzięki czemu ten długi wpis nie przybierze monstrualnych rozmiarów.