Home >> Spring Framework
I have tried to make this example as simple as possible by using some of the
support classes from Spring Framework, those can be used along with Hibernate for
easy way of handling Hibernate's Session and SessionFactory, along with JTA Transaction.
In this example, I am specifically using SpringFramework 3.0.0 version
and Hibernate 3.2 version.
I am referring about following packages from SpringFramework and going to pick up
some of those classes from these packages from Spring's API distribution:
org.springframework.orm
org.springframework.orm.hibernate3
org.springframework.orm.hibernate3.annotation
org.springframework.orm.hibernate3.support
In
org.springframework.orm.hibernate3 package,
LocalSessionFactoryBean class can be used and configured
to create Hibernate's SessionFactory within the Spring's Context.
This can be done just by using/ picking up hibernate.cgf.xml file
from classpath, as shown below:
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="hibernateDAO" class="example.dao.HibernateDAO">
<property name="sessionFactory" ref="sessionFactoryBean"/>
</bean>
<bean id="sessionFactoryBean"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="configLocation"
value="classpath:config/hibernate.cfg.xml"/>
<!-- This commented section shows another way of doing configuration
for those same settings from hibernate.cfg.xml file as follows:
<property name="hibernateProperties">
<props>
<prop key="hibernate.connection.datasource">java:MysqlDS</prop>
<prop key="hibernate.connection.pool_size">2</prop>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.transaction.factory_class">
org.hibernate.transaction.JTATransactionFactory
</prop>
<prop key="hibernate.transaction.manager_lookup_class">
org.hibernate.transaction.JBossTransactionManagerLookup
</prop>
<prop key="hibernate.current_session_context_class">jta</prop>
</props>
</property>
<property name="mappingResources">
<array>
<value>
example/businessobject/Account.hbm.xml
</value>
</array>
</property>
</bean>
-->
</beans>
|
The configuration settings for Hibernate I have used in this example
is as follows:
hibernate.cfg.xml
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<!-- This is provide AS IS without any Guarantee of any kind -->
<session-factory>
<!-- Database connection settings -->
<property name="connection.datasource">
java:MysqlDS
</property>
<!-- JDBC connection pool (use the built-in) -->
<property name="connection.pool_size">1</property>
<!-- SQL dialect -->
<property name="dialect">
org.hibernate.dialect.MySQLDialect
</property>
<property name="transaction.factory_class">
org.hibernate.transaction.JTATransactionFactory
</property>
<property name="transaction.manager_lookup_class">
org.hibernate.transaction.JBossTransactionManagerLookup
</property>
<property name="current_session_context_class">
jta
</property>
<!-- Echo all executed SQL to stdout -->
<property name="show_sql">true</property>
<mapping resource="example/businessobject/Account.hbm.xml"/>
</session-factory>
</hibernate-configuration>
|
With these settings for Hibernate, I am trying to use JTA Transaction
that is getting propagated from the Session Bean (using Container managed
transaction / CMT) to the service, dao POJOs to all database interactions
involved. In case this common/same JTA transaction encounters some exception,
then all operations, those are performed within the boundary of this
transaction, are bound to rollback, or else, on successful execution/completion
of database SQL/ DML commands from this example, this global transaction is
going to be committed.
Now let us go through the HibernateDAO.java file from this example:
HibernateDAO.java
package example.dao;
import java.io.Serializable;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.orm.hibernate3.LocalSessionFactoryBean;
/**
* This is provide AS IS without any Guarantee of any kind
* Author: Guddu from IQTF
* Date: 25th June 2010
*/
public class HibernateDAO {
private SessionFactory sessionFactory = null;
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
protected Object saveObject(Object obj) {
return sessionFactory.getCurrentSession().save(obj);
}
protected void updateObject(String id, Object obj) {
sessionFactory.getCurrentSession().update(id, obj);
}
protected void deleteObject(Object obj) {
sessionFactory.getCurrentSession().delete(obj);
}
protected Object fetch(Class clz, Serializable id) {
return sessionFactory.getCurrentSession().get(clz, id);
}
/**
* This method is just to help in printing
* out the Transaction that is used across
* the operations.
* @return Session
*/
protected Session getCurrentSession() {
return sessionFactory.getCurrentSession();
}
}
|
Above method in bold, setSessionFactory is being used in the
applicationContext.xml file and thereby using Spring to inject
instance of LocalSessionFactoryBean to beused as Hibernate's
SessionFactory instance for this example.
In this example AccountDAO is used to abstract service POJO
from directly using HibernateDAO. AccountDAO is creating the
Spring's Application Context within a static block to be
called for once, and I am using Spring's ClassPathXmlApplicationContext
class. This class is supplied with the relative path and
applicationContext.xml file as argument for its constructor.
AccountDAO.java
package example.dao;
import org.hibernate.Session;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import example.businessobject.Account;
/**
* This is provide AS IS without any Guarantee of any kind
* Author: Guddu from IQTF
* Date: 15th April 2009
*/
public class AccountDAO implements ExampleDAO {
private static HibernateDAO hibernateDAO = null;
static {
try {
ClassPathXmlApplicationContext clsCtx =
new ClassPathXmlApplicationContext("config/applicationContext.xml");
hibernateDAO = (HibernateDAO) clsCtx.getBean("hibernateDAO");
System.out.println("Hibernate DAO instance: "+hibernateDAO);
}catch(Exception ex) {
ex.printStackTrace();
}
}
public void delete(Account account) throws DataAccessException {
hibernateDAO.deleteObject(account);
}
public String save(Account account) throws DataAccessException {
return (String) hibernateDAO.saveObject(account);
}
public void update(Account account) throws DataAccessException {
hibernateDAO.updateObject(account.getAccountNumber(), account);
}
public Account fetch(Account account) throws DataAccessException {
return (Account) hibernateDAO.fetch(Account.class, account.getAccountNumber());
}
public Session getSessionUsed() {
return hibernateDAO.getCurrentSession();
}
}
|
AccountService (this example's service implementation) class uses
AccountDAO instance directly (not using Spring's Dependency Injection)
just for no specific reason, but it can use Spring's application context
in order to manage these POJOs such as Service, DAO classes etc.
AccountService.java
package example.service;
import java.math.BigDecimal;
import example.businessobject.Account;
import example.dao.AccountDAO;
import example.dao.DataAccessException;
import example.exception.AccountException;
/**
* This is provide AS IS without any Guarantee of any kind
* Author: Guddu from IQTF
* Date: 15th April 2009
*/
public class AccountService {
public String createAccount(Account account) throws AccountException {
String acctNumber;
try {
acctNumber = new AccountDAO().save(account);
} catch (DataAccessException e) {
throw new AccountException(e);
}
return acctNumber;
}
/**
* Assuming that all sorts of validations are already
* performed and these accounts those are passed as
* method parameters are ready for amount transfer.
* @param acc1
* @param acc2
* @param amt
* @return
* @throws BusinessException
*/
public boolean tranferAmount(Account acc1, Account acc2, BigDecimal amt)
throws BusinessException {
boolean status = false;
try {
//This log print is to just see the transaction instance
//being used.
System.out.println("Starting with Transaction :"
+ new AccountDAO().getSessionUsed().getTransaction());
Account accnt1 = new AccountDAO().fetch(acc1);
Account accnt2 = new AccountDAO().fetch(acc2);
BigDecimal bgDec = accnt1.getBalanceAmount();
accnt1.setBalanceAmount(bgDec.subtract(amt));
accnt2.setBalanceAmount(accnt2.getBalanceAmount().add(amt));
new AccountDAO().update(accnt1);
new AccountDAO().update(accnt2);
status = true;
//This log print is to just see the transaction instance
//being used.
System.out.println("Commiting with Transaction :"
+ new AccountDAO().getSessionUsed().getTransaction());
} catch (DataAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return status;
}
}
|
This example uses some of the user-defined exceptions such as
AccountException, DataAccessException etc.
And the Stateless Session Bean is nothing but a facade with
some of the fine-grained methods(createAccount/manageAccount/deleteAccount)
and coarse-grained method (transferAmount) defined within the
remote interface, such as
ExampleFacade.java
package example.facade;
import java.math.BigDecimal;
import java.rmi.RemoteException;
import javax.ejb.EJBObject;
import example.businessobject.Account;
/**
* This is provide AS IS without any Guarantee of any kind
* Author: Guddu from IQTF
* Date: 15th April 2009
*/
public interface ExampleFacade extends EJBObject {
public String createAccount(Account account) throws RemoteException;
public void manageAccount(Account account) throws RemoteException;
public void deleteAccount(Account account) throws RemoteException;
public boolean transferAmount(Account acc1, Account acc2,
BigDecimal amt)
throws RemoteException;
}
|
The implementation class file for this interface looks like following:
ExampleFacadeBean.java
package example.facade;
import java.math.BigDecimal;
import java.rmi.RemoteException;
import javax.ejb.EJBException;
import javax.ejb.SessionBean;
import javax.ejb.SessionContext;
import javax.transaction.HeuristicMixedException;
import javax.transaction.HeuristicRollbackException;
import javax.transaction.NotSupportedException;
import javax.transaction.RollbackException;
import javax.transaction.SystemException;
import javax.transaction.UserTransaction;
import example.businessobject.Account;
import example.exception.AccountException;
import example.service.AccountService;
import example.service.BusinessException;
/**
* This is provide AS IS without any Guarantee of any kind
* Author: Guddu from IQTF
* Date: 15th April 2009
*/
public class ExampleFacadeBean implements SessionBean {
private SessionContext ctx;
public String createAccount(Account account) throws RemoteException
{
String acctNumber = null;
try {
acctNumber = new AccountService().createAccount(account);
} catch (AccountException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalStateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return acctNumber;
}
public void manageAccount(Account account) throws RemoteException
{
}
public void deleteAccount(Account account) throws RemoteException
{
}
public boolean transferAmount(Account acc1, Account acc2,
BigDecimal amt)
throws RemoteException {
boolean status = false;
try {
status = new AccountService().tranferAmount(acc1, acc2, amt);
} catch (BusinessException e) {
status = false;
throw new RemoteException("Some Exception Happened",e);
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return status;
}
public void ejbCreate() throws EJBException, RemoteException {
// TODO Auto-generated method stub
}
public void ejbActivate() throws EJBException, RemoteException {
// TODO Auto-generated method stub
}
public void ejbPassivate() throws EJBException, RemoteException {
// TODO Auto-generated method stub
}
public void ejbRemove() throws EJBException, RemoteException {
// TODO Auto-generated method stub
}
public void setSessionContext(SessionContext arg0) throws EJBException,
RemoteException {
// TODO Auto-generated method stub
this.ctx = arg0;
}
}
|
Corresponding home interface is as follows:
ExampleFacadeHome.java
package example.facade;
import java.rmi.RemoteException;
import javax.ejb.CreateException;
import javax.ejb.EJBHome;
/**
* This is provide AS IS without any Guarantee of any kind
* Author: Guddu from IQTF
* Date: 15th April 2009
*/
public interface ExampleFacadeHome extends EJBHome {
public ExampleFacade create() throws CreateException,
RemoteException;
}
|
For deploying this session bean, I required two XML descriptor
files within the META-INF folder:
ejb-jar.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE ejb-jar PUBLIC '-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN'
'http://java.sun.com/dtd/ejb-jar_2_0.dtd'>
<ejb-jar>
<display-name>ExampleAccount</display-name>
<enterprise-beans>
<session>
<ejb-name>ExampleFacade</ejb-name>
<home>example.facade.ExampleFacadeHome</home>
<remote>example.facade.ExampleFacade</remote>
<ejb-class>example.facade.ExampleFacadeBean
</ejb-class>
<session-type>Stateless</session-type>
<transaction-type>Container</transaction-type>
</session>
</enterprise-beans>
<assembly-descriptor>
<container-transaction>
<method>
<ejb-name>ExampleFacade</ejb-name>
<method-name>manageAccount</method-name>
</method>
<method>
<ejb-name>ExampleFacade</ejb-name>
<method-name>createAccount</method-name>
</method>
<method>
<ejb-name>ExampleFacade</ejb-name>
<method-name>deleteAccount</method-name>
</method>
<method>
<ejb-name>ExampleFacade</ejb-name>
<method-name>transferAmount</method-name>
</method>
<trans-attribute>Required</trans-attribute>
</container-transaction>
</assembly-descriptor>
</ejb-jar>
|
and
jboss.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE jboss PUBLIC
"-//JBoss//DTD JBOSS 4.0//EN"
"http://www.jboss.org/j2ee/dtd/jboss_4_0.dtd">
<jboss>
<enterprise-beans>
<session>
<ejb-name>ExampleFacade</ejb-name>
<jndi-name>example-facade</jndi-name>
</session>
</enterprise-beans>
</jboss>
|
I have used JBoss application server 4.0.5 version and
assembled all these example's compiled code into a single
JAR file as namely "example.jar". After creating some of the
pre-requisites such as the Data Source configuration aspects of
this application serve, database table etc. and proceeded towards
deployment of this JAR file onto the application container
by copying it to the deploy folder.
Please refer JBoss's official documentation for more details,
as this example doesn't intend to cover JBoss specific
EJB application deployment in details.
The domain class used in this example is Account.java
Account.java
package example.businessobject;
import java.io.Serializable;
import java.math.BigDecimal;
/**
* This is provide AS IS without any Guarantee of any kind
* Author: Guddu from IQTF
* Date: 15th April 2009
*/
public class Account implements Serializable {
private String accountNumber;
private String holderName;
private String accountType;
private BigDecimal balanceAmount;
private boolean accountActiveStatus;
public boolean isAccountActiveStatus() {
return accountActiveStatus;
}
public void setAccountActiveStatus(boolean accountActiveStatus) {
this.accountActiveStatus = accountActiveStatus;
}
public String getAccountNumber() {
return accountNumber;
}
public void setAccountNumber(String accountNumber) {
this.accountNumber = accountNumber;
}
public String getAccountType() {
return accountType;
}
public void setAccountType(String accountType) {
this.accountType = accountType;
}
public BigDecimal getBalanceAmount() {
return balanceAmount;
}
public void setBalanceAmount(BigDecimal balanceAmount) {
this.balanceAmount = balanceAmount;
}
public String getHolderName() {
return holderName;
}
public void setHolderName(String holderName) {
this.holderName = holderName;
}
}
|
and the Hibernate specific HBM XML file used is Account.hbm.xml
file as follows:
Account.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="example.businessobject">
<class name="Account" table="account_info">
<id name="accountNumber" access="property" column="account_number"/>
<property name="holderName" access="property"
column="holder_name"/>
<property name="accountType" access="property"
column="account_type"/>
<property name="balanceAmount" access="property"
column="account_balance"/>
<property name="accountActiveStatus" access="property"
column="account_active_status"/>
</class>
</hibernate-mapping>
|
As far as any Technical issues are concerned, if you need any further
assitance from me, please do write in the comment section below,
Generally I would try to find some time daily to check for any
questions asked and would like to try and answer those to the best of
my knowledge and understanding on the very topic only.
If anything missed out , please let me know at
techienjoy at yahoo . com