Tuesday, February 17, 2009

Spring2 JPA Hibernate Tutorial

About this tutorial

This tutorial helps you to set up a simple application using Spring 2, JPA and Hibernate. It guides you to learn the steps involved in installing, configuring, developing, testing and deploying. It covers only the basic functionality of Spring 2, JPA and Hibernate.

Prerequisite

This tutorial requires knowledge or work experience in the following areas.

· Java

· Java Enterprise Environment (J2EE)

· Spring and Hibernate

· Relational Database Management System (RDBMS)

System Requirements

In order to develop this sample project, the following software is needed.

About the project

This tutorial creates a very simple project. The functionality of this application is to store, retrieve and delete the address information in the Database.


Spring

Spring is a collection of frameworks that provides features to develop distributed applications. It provides the security, transaction, mailing, look up services (JNDI) and other services. The framework is not vendor specific. It can be developed and deployed in standalone and web applications.

The Spring consists of Spring core, Aspect, context, Data Access, ORM and Web, Web MVC. It supports other frameworks to blend in with it. For example, the hibernate can be plugged in for data access and struts for web access. The hibernate is used for data access in this tutorial.

Hibernate

Hibernate is an ORM (Object Relational Mapping) framework to manage the data access. It provides consistent way of accessing and managing data in the relational database. The framework is independent from the database and it hides the complexity of creating the database specific queries.

Java Persistence API

The JPA is an object oriented approach with annotation to the data persistence. It is introduced as part of EE 5.0. But it can be used in java SE also.

In this tutorial, we will be using the power of Spring, Hibernate and JPA.



Setup Workspace

Creating the workspace is the first step in any project. Point your Eclipse IDE to the workspace folder once it is created. And create a Java project for DAO (Data Access Object) and Web project. The IDE automatically creates the EAR projects and associates the DAO and Web projects with it.

Required libraries

The Data access project should be configured to have all the required spring jar files. The jar files are normally located under spring-framework\dist\modules\ or spring-framwork\lib\.

Design domain model

The Address domain consists of the following attributes Id, Number and Street. You can use the following SQL script to create the table in the DB2.

   CREATE TABLE ADDRESS (
      ID INTEGER NOT NULL,
      NUM INTEGER,
      STNAME VARCHAR(25),
      PRIMARY KEY (ID)
   );

Define the Java POJO for the above table.

import javax.persistence.Column;

import javax.persistence.Entity;

import javax.persistence.Id;

/**

* @author Elangovan Mohan

*

*/

@Entity // Indicates that it is a table.

public class Address {

@Id // Primary key.

private int id;

@Column(name = "NUM") //Integer column.

private int number;

@Column(name = "STNAME") //String column.

private String street;

/**

* @return the id

*/

public int getId() {

return id;

}

/**

* @param id the id to set

*/

public void setId(int id) {

this.id = id;

}

/**

* @return the number

*/

public int getNumber() {

return number;

}

/**

* @param number the number to set

*/

public void setNumber(int number) {

this.number = number;

}

/**

* @return the street

*/

public String getStreet() {

return street;

}

/**

* @param street the street to set

*/

public void setStreet(String street) {

this.street = street;

}

Define the service

The service interface provides the data access functionality. It uses the JPA EntityManager to manage the address data. The EntityManager implementation is injected by spring during the server startup.

import javax.persistence.EntityManager;

import javax.persistence.PersistenceContext;

import com.springtutorial.vo.Address;

/**

* @author Elangovan Mohan

*

*/

public class EmployeeServiceImpl implements IEmployeeService {

@PersistenceContext(unitName="application2Jpa") EntityManager entityManager; // The Unit name is defined at persistence.xml.

@Override

public void delete(Address address) {

Address addressToDelete=(Address) entityManager.getReference(Address.class, address.getId());

entityManager.remove(addressToDelete);

}

@Override

public Address findByID(Integer addressId) {

Address address=(Address) entityManager.find(Address.class, addressId);

return address;

}

@Override

public Address save(Address address) {

entityManager.persist(address);

return address;

}

Configure persistence.xml

The persistence.xml is required as per JPA implementation. It describes the persistence unit related information. The Persistence.xml resides under the <>/src/META-INF folder.




org.hibernate.ejb.HibernatePersistence
com.springtutorial.vo.Address

























Configure spring application-services.xml

The spring creates and instantiates the beans during the server startup based on the configuration details defined in the service xml. The spring creates the entity manager proxy with entity manager, JPA vendor adapter, data source and persistence unit information. The transaction boundaries can be also declared for the bean.

version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xmlns:aop="http://www.springframework.org/schema/aop"

xmlns:tx="http://www.springframework.org/schema/tx"

xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans.xsd

http://www.springframework.org/schema/tx

http://www.springframework.org/schema/tx/spring-tx-2.0.xsd

http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">

<bean

class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />

<bean id="myBaseTransaction" abstract="true" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">

<property name="transactionManager"><ref local="transactionManager"/>property>

<property name="transactionAttributes">

<props>

<prop key="*">PROPAGATION_REQUIREDprop>

props>

property>

bean>

<bean id="employeeService" parent="myBaseTransaction">

<property name="target">

<bean class="com.springtutorial.dao.EmployeeServiceImpl">

bean>

property>

bean>

<bean id="entityManagerFactory" class=

"org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">

<property name="dataSource" ref="dataSource"/>

<property name="jpaVendorAdapter">

<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">

<property name="showSql" value="true"/>

<property name="generateDdl" value="false"/>

<property name="databasePlatform" value="org.hibernate.dialect.HSQLDialect"/>

bean>

property>

<property name="persistenceXmlLocation">

<value>classpath:/META-INF/persistence.xmlvalue>

property>

<property name="persistenceUnitName" value="application2Jpa" />

bean>

<bean id="dataSource"

class="org.springframework.jdbc.datasource.DriverManagerDataSource">

<property name="driverClassName" value="com.ibm.db2.jcc.DB2Driver"/>

<property name="url" value="jdbc:db2://LENOVO-38F85E74:50000/SAMPLE"/>

<property name="username" value="admin" />

<property name="password" value="password" />

bean>

<bean id="transactionManager"

class="org.springframework.orm.jpa.JpaTransactionManager">

<property name="entityManagerFactory" ref="entityManagerFactory"/>

<property name="dataSource" ref="dataSource"/>

bean>

beans>


Test Case

The AbstractJPATests can be used to test the service implementation class. The method getConfigLocations informs the Spring runtime to load the application-services.xml. The spring injects the EmployeeServiceImpl proxy at runtime.

import org.springframework.test.jpa.AbstractJpaTests;

import com.springtutorial.dao.EmployeeServiceImpl;

import com.springtutorial.vo.Address;

/**

* @author Elangovan Mohan

*

*/

public class EmployeeServiceTest extends AbstractJpaTests {

public EmployeeServiceImpl employeeService;

/**

* @param employeeService the employeeService to set

*/

public void setEmployeeService(EmployeeServiceImpl employeeService) {

this.employeeService = employeeService;

}

protected String[] getConfigLocations() {

return new String[] { "classpath:/com/springtutorial/dao/application-services.xml" };

}

protected void onSetUpInTransaction() {

Address employee=new Address();

employee.setId(300);

employee.setNumber(3);

employee.setStreet("Southside Blvd");

employeeService.save(employee);

}

public void testAdd() {

// Address employee=new Address();

// employee.setId(300);

// employee.setNumber(3);

// employee.setStreet("Southside Blvd, Jacksonville,FL");

// employeeService.save(employee);

// Address address=employeeService.findByID(new Long(100));

// assertNotNull(address);

}

Test service implementation

The AbstractJPATests automatically rolls back all the data upon test completion. It makes sure that the data is not persisted in the database. This provides opportunity to test the methods multiple times.

References:

https://www6.software.ibm.com/developerworks/education/j-spring2/section3.html

http://struts.sourceforge.net/struts-spring/index.html

http://www.hibernate.org/hib_docs/entitymanager/reference/en/html/

http://docs.huihoo.com/spring/2.0.x/en/transaction.html#transaction-declarative

http://forum.hibernate.org/viewtopic.php?p=2400801

http://www.netbeans.org/kb/60/web/web-jpa-part2.html

Problems and Troubleshooting

  1. Server timeout

The default time out is 50ms for the most of the freeware app servers such as jboss or tomcat. You need to increase the time out period at the server console. The time out parameter is configurable in Eclipse 3.4.1 or above versions.

  1. Jar compatibility

Make sure all the jar files are compatible. For example, slf4j-api-1.5.6.jar and slf4j-log4j12-1.5.6.jar requires log4j-1.2.15.jar.

  1. java.lang.ClassNotFoundException: <>

It is the most common problem that you come across when you run the test case. You need to make sure all the compile time and runtime jars are configured at the class path and Java EE module dependencies.

  1. org.hibernate.LazyInitializationException: could not initialize proxy - no Session

This happens when the EntityManager is not configured correctly. You need to make sure the persistence unit name is defined in the dao and persistence.xml.

5. Can not convert object[your object, proxoy] … happens during the injection….

The possible cause could be the implementation class is configured for the dependency injection at your client code. All you need to do is change the implementation class to the interface.




No comments: