Friday, July 25, 2014

JPA + Spring + hibernate Integration with JPA DAO Pattern

The target is to use JPQL in the code, use hibernate as underlying jpa provider, and use spring to manage transaction.


  • This is the project structure:





















  • 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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>wx</groupId>
<artifactId>jpa1</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>

<name>jpa1</name>
<url>http://maven.apache.org</url>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>3.2.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>3.2.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>3.2.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>3.2.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>4.2.14.Final</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>3.2.10.RELEASE</version>
</dependency>
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>persistence-api</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.30</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.0.1</version>
</dependency>

</dependencies>
</project>

  • spring configuration file 
<?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:context="http://www.springframework.org/schema/context"
xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/jdbc
http://www.springframework.org/schema/jdbc/spring-jdbc-3.1.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd">

<bean id="txManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="emf" />
</bean>

<tx:annotation-driven transaction-manager="txManager" />

<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost/testdb1" />
<property name="username" value="root" />
<property name="password" value="" />
</bean>

<bean id="emf"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
</property>
<property name="packagesToScan" value="wx.jpa1.entity" />
<property name="jpaProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.format_sql">true</prop>
</props>
</property>
</bean>

<context:component-scan base-package="wx.jpa1" />

</beans>

----- persistence.xml is not required since spring v3.1
----- use spring container to manage transaction propagation
----- use org.springframework.orm.jpa.JpaTransactionManager
  • create database and table in mysql
create database testdb1;

use testdb1;

create table student(
stud_id int not null auto_increment,
name varchar(50) not null,
age int,
depart varchar(20),
primary key (stud_id)
)
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8;

  • entity class with jpa annotation
package wx.jpa1.entity;

import java.io.Serializable;

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

import org.apache.commons.lang3.builder.ToStringBuilder;

/**
 * student entity object
 * 
 */
@Entity
@Table(name = "student")
public class StudentEO implements Serializable {

private static final long serialVersionUID = 1492053620157953609L;
private Long studId;
private String name;
private int age;
private String depart;

/*
* As an entity object, the default constructor is a must.
*/
public StudentEO() {

}

@Id
@Column(name = "stud_id")
@GeneratedValue(strategy = GenerationType.AUTO)
public Long getStudId() {
return studId;
}

public void setStudId(Long studId) {
this.studId = studId;
}

@Column(name = "name")
public String getName() {
return name;
}

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

@Column(name = "age")
public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

@Column(name = "depart")
public String getDepart() {
return depart;
}

public void setDepart(String depart) {
this.depart = depart;
}

@Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
}

}

  • DAO tier

------------interface IGenericDao ------------

package wx.jpa1.dao;

import java.io.Serializable;
import java.util.List;

public interface IGenericDao<T, PK extends Serializable> {

public T find(PK id);

public void save(T entity);

public T update(T entity);

public void delete(PK id);

public List<T> findAll();

public List<T> findByNamedQuery(String queryName, Object... objects);

}

---------------- implement above interface with jpa ----------

package wx.jpa1.dao;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

public class GenericDaoJpaImpl<T, PK extends Serializable> implements
IGenericDao<T, PK> {

protected Class<T> entityClass;

@PersistenceContext
protected EntityManager entityManager;

@SuppressWarnings("unchecked")
public GenericDaoJpaImpl() {
if (entityClass == null) {
this.entityClass = (Class<T>) ((ParameterizedType) getClass()
.getGenericSuperclass()).getActualTypeArguments()[0];
}
}

public T find(PK id) {
return this.entityManager.find(entityClass, id);
}

public void save(T entity) {
this.entityManager.persist(entity);
}

public T update(T entity) {
return this.entityManager.merge(entity);
}

public void delete(PK id) {
this.entityManager.remove(this.entityManager.getReference(entityClass,
id));
}

@SuppressWarnings("unchecked")
public List<T> findAll() {
return this.entityManager.createQuery(
"select h from " + this.entityClass.getName() + " h")
.getResultList();
}

public List<T> findByNamedQuery(String queryName, Object... objects) {
// TODO Auto-generated method stub
return null;
}
}

---------------- for particular DAO interface----
package wx.jpa1.dao;

import java.util.List;

import wx.jpa1.entity.StudentEO;

public interface IStudentDao extends IGenericDao<StudentEO, Long> {

public List<StudentEO> findByDepart(String depart);
}

---------------- implement above dao interface -------

package wx.jpa1.dao;

import java.util.List;

import org.springframework.stereotype.Repository;

import wx.jpa1.entity.StudentEO;

@Repository("studentDao")
public class StudentDaoImpl extends GenericDaoJpaImpl<StudentEO, Long>
implements IStudentDao {

public List<StudentEO> findByDepart(String depart) {
// TODO
return null;
}

}

  • Service tier
------------- service interface -----------------
package wx.jpa1.service;

import java.util.List;

import wx.jpa1.entity.StudentEO;

public interface IStudentService {
public void registerStudent(StudentEO student);
public List<StudentEO> findAll();
}

------------- implement above interface -------------

package wx.jpa1.service;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import wx.jpa1.dao.IStudentDao;
import wx.jpa1.entity.StudentEO;

@Service("studentService")
@Transactional
public class StudentServiceImpl implements IStudentService {

@Autowired
@Qualifier("studentDao")
private IStudentDao studentDao;

public IStudentDao getStudentDao() {
return studentDao;
}

public void setStudentDao(IStudentDao studentDao) {
this.studentDao = studentDao;
}

public void registerStudent(StudentEO student) {
studentDao.save(student);
}

public List<StudentEO> findAll() {
return studentDao.findAll();
}

}


-------------------- junit test -----------------

package wx.jpa1;

import java.util.List;

import org.junit.After;
import org.junit.Before;
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.transaction.TransactionConfiguration;
import org.springframework.transaction.annotation.Transactional;

import wx.jpa1.entity.StudentEO;
import wx.jpa1.service.IStudentService;

@RunWith(org.springframework.test.context.junit4.SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:app-context.xml")
@Transactional
@TransactionConfiguration(defaultRollback=false)
public class IStudentServiceTest {

@Autowired
IStudentService studentService;

@Before
public void setUp() throws Exception {

}

@After
public void tearDown() throws Exception {
studentService = null;
}

@Test
public void testRegisterStudent() {
StudentEO student = new StudentEO();
student.setAge(20);
student.setDepart("Computer");
student.setName("Adam");
studentService.registerStudent(student);
}
@Test
public void testQueryStudent() {
List<StudentEO> result = studentService.findAll();
System.out.println(result);
}
}

-------------- test result ------

mysql> select * from student;
+-------------+----------------------------------------------------+-------------+----------------------+
| stud_id     | name                                               | age         | depart               |
+-------------+----------------------------------------------------+-------------+----------------------+
|           5 | Adam                                               |          20 | Computer             |
|           6 | Adam                                               |          20 | Computer             |
|           7 | Adam                                               |          20 | Computer             |
|           8 | Adam                                               |          20 | Computer             |
|          11 | John                                               |          20 | Computer             |
+-------------+----------------------------------------------------+-------------+----------------------+
5 rows in set (0.00 sec)

Jul 25, 2014 12:08:16 PM org.springframework.test.context.transaction.TransactionalTestExecutionListener startNewTransaction
INFO: Began transaction (1): transaction manager [org.springframework.orm.jpa.JpaTransactionManager@1b6aa42e]; rollback [false]
Hibernate: 
    select
        studenteo0_.stud_id as stud_id1_0_,
        studenteo0_.age as age2_0_,
        studenteo0_.depart as depart3_0_,
        studenteo0_.name as name4_0_ 
    from
        student studenteo0_
[wx.jpa1.entity.StudentEO@13ef45e0[studId=5,name=Adam,age=20,depart=Computer], wx.jpa1.entity.StudentEO@2d0c94a7[studId=6,name=Adam,age=20,depart=Computer], wx.jpa1.entity.StudentEO@14f3770c[studId=7,name=Adam,age=20,depart=Computer], wx.jpa1.entity.StudentEO@7c0cbf92[studId=8,name=Adam,age=20,depart=Computer]]
Jul 25, 2014 12:08:16 PM org.springframework.test.context.transaction.TransactionalTestExecutionListener endTransaction
INFO: Committed transaction after test execution for test context [TestContext@22dca7d0 testClass = IStudentServiceTest, testInstance = wx.jpa1.IStudentServiceTest@448d5a91, testMethod = testQueryStudent@IStudentServiceTest, testException = [null], mergedContextConfiguration = [MergedContextConfiguration@1c3508c0 testClass = IStudentServiceTest, locations = '{classpath:app-context.xml}', classes = '{}', contextInitializerClasses = '[]', activeProfiles = '{}', contextLoader = 'org.springframework.test.context.support.DelegatingSmartContextLoader', parent = [null]]]
Jul 25, 2014 12:08:16 PM org.springframework.test.context.transaction.TransactionalTestExecutionListener startNewTransaction
INFO: Began transaction (2): transaction manager [org.springframework.orm.jpa.JpaTransactionManager@1b6aa42e]; rollback [false]
Hibernate: 
    insert 
    into
        student
        (age, depart, name) 
    values
        (?, ?, ?)
Jul 25, 2014 12:08:16 PM org.springframework.test.context.transaction.TransactionalTestExecutionListener endTransaction
INFO: Committed transaction after test execution for test context [TestContext@22dca7d0 testClass = IStudentServiceTest, testInstance = wx.jpa1.IStudentServiceTest@f292738, testMethod = testRegisterStudent@IStudentServiceTest, testException = [null], mergedContextConfiguration = [MergedContextConfiguration@1c3508c0 testClass = IStudentServiceTest, locations = '{classpath:app-context.xml}', classes = '{}', contextInitializerClasses = '[]', activeProfiles = '{}', contextLoader = 'org.springframework.test.context.support.DelegatingSmartContextLoader', parent = [null]]]

No comments:

Post a Comment