I am using Struts2.3 + Spring 3.2.6 + Hibernate 3.X for my web application.
I am using annotations to manage the transaction.
My DAO class is as below.
#Transactional(readOnly = true, rollbackFor={Exception.class})
public class CustomerDAOImpl extends HibernateDaoSupport implements CustomerDAO{
//adds the customer
#Transactional(propagation=Propagation.REQUIRED, rollbackFor = {Exception.class})
public void addCustomer(Customer customer){
Session session = getSession();
System.out.println("M1() Hash Code: --->"+session.hashCode()+ " Thread id: "+Thread.currentThread().getId());
//session.save(customer);
}
//return all the customers in list
// #Transactional(propagation=Propagation.REQUIRED, rollbackFor = {Exception.class})
#Transactional(readOnly = true)
public List<Customer> listCustomer(){
Session session = getSession();
System.out.println("M2() Hash Code: --->"+session.hashCode()+ " Thread id: "+Thread.currentThread().getId());
return null; //logic to get the list based on condition
}
These methods will be called from service layer and it is like below;
customerDAO.addCustomer(customer);
customerDAO.listCustomer();
I am getting different sessions for same thread when the above code is executed.
Output:
M1() Hash Code: --->5026724 Thread id: 21
M2() Hash Code: --->8899550 Thread id: 21
Due to this, if any exception comes in method2() the data which is persisted using method1() is not rollback.
Please let me know, how to get the single session based on thread (if i go for my own HibernateFactory i lose transaction management by Spring) with out loosing spring transaction management.
If you are using Hibernate make your DAO class to extend HibernateDaoSupport. Now you can get session from getHibernateTemplate() method. That way you can have the session managed by spring-hibernate
You can try this -
appContext.xml
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="submit*" propagation="REQUIRED" read-only="false" rollback-for="Exception"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="appControllerTransactionPointCuts"
expression="execution(* com.test.customer.bo.Custom.*(..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="appControllerTransactionPointCuts" />
</aop:config>
Remove -
<tx:annotation-driven transaction-manager="transactionManager" />
from appcontext.xml
Now AOP should manage transactions.
How Transactions work -
Suppose you are persisting objects in multiple DAO methods and you want them all to happen in one transaction, then you have to apply transaction at the service layer method where the DAO methods are getting called. For e., in my case the service layer is com.test.customer.bo.Custom.*
If you are not doing so, then each of your DAO method will be executed in a separate transaction and will get persisted to database if no exception occurs. If exception occur in method2() of DAO layer it will not rollback method1() as it is already committed.
Related
I have a service that returns a persisted Schedule object based on an input: date. I have multiple threads calling this method and I would like to ensure uniqueness. I don't want to catch a constraint violation and give any user an error. What I need is: if one thread calls the method and a Schedule object doesn't exist for that date, that thread creates it and the other threads wait. Before I have been using Spring, just plain Hibernate in my web applicaiton I have done it the following way:
I've used a ManagedSessionContext with hibernate. I was starting a transaction in the beginning of each web request and commiting it in the end. For the synchronized blocks I've commited the current transaction right away in a synchronized block and started a new one to finish the request. My isolation level was READ_COMMITED and it worked great.
public synchronized Schedule getSchedule(Date date) {
Schedule schedule = dao.getSchedule(date);
if (schedule == null) {
schedule = createSchedule(date);
dao.save(schedule);
HibernateUtils.getCurrentSession().getTransaction().commit();
HibernateUtils.getCurrentSession().getTransaction().begin();
}
return schedule;
}
Now I am using Spring with this project and I'm using aop for transaction management. I have removed the manual transaction code. I'm testing with multiple threads and I'm getting a deadlock
Caused by: java.sql.SQLException: Lock wait timeout exceeded; try restarting transaction
My Transaction AOP:
<tx:advice id="txAdvice">
<tx:attributes >
<tx:method name="getSchedule" isolation="SERIALIZABLE" />
<tx:method name="*" />
</tx:attributes>
</tx:advice>
#Autowire
private HibernateTransactionManager txManager;
Wrap the method body with:
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setName("transactionName");
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
TransactionStatus ts = txManager.getTransaction(def);
//Body Code
txManager.commit(ts);
My application is based on Hibernate 3.2 and Spring 2.5. Here is the transaction management related snippet from the application context:
<tx:annotation-driven transaction-manager="txManager"/>
<bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
<property name="nestedTransactionAllowed" value="true"/>
</bean>
<bean id="transactionTemplate" classs="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="txManager"/>
</bean>
<bean class="org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor"/>
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="configLocation" value="classpath:/hibernate.cfg.xml"></property>
</bean>
For all the DAO's there are relevant Service class and the transactions are handled there using #Transactional on each method in the service layer. However there is a scenario now that a method in DAO say "parse()" is called from the service layer. In the service layer I specified #Transactional(readOnly=false). This parse method in the DAO calls another method say "save()" in the same DAO which stores a large number of rows (around 5000) in the database. Now the save method is called in a loop from the parse function. Now the issue is that after around 100 calls to the "save" method.. i sometimes get a OutOfMemory Exception or sometimes the program stops responding.
For now these are the changes which I have made to the save method:
Session session = getHibernateTemplate().getSessionFactory().openSession();
Transaction tx = session.beginTransaction();
int counter = 0;
if(books!=null && !books.isEmpty()){
for (Iterator iterator = books.iterator(); iterator
.hasNext();) {
Book book = (Book) iterator.next();
session.save(book);
counter++;
if(counter % 20==0) {
session.flush();
session.clear();
}
}
}
tx.commit();
session.close();
This is the only method in my application where I start a transaction like this and commit it at the end of method. Otherwise I normally just call getHibernateTemplate.save(). I am not sure whether I should perform transaction management for this save method separately in the DAO by placing #Transactional(readOnly=false, PROPOGATION=NEW) on save(), or is this approach okay?
Also I have updated the hibernate.jdbc.batch_size to 20 in the hibernate.cfg configuration file.
Any suggestions?
For the batch insertion with hibernate, the best practice is StatelessSession, it doesn`t cache any states of your entity, you will not encounter OutOfMemory, the code like:
if (books == null || books.isEmpty) {
return;
}
StatelessSession session = getHibernateTemplate().getSessionFactory().openStatelessSession();
Transaction tx = session.beginTransaction();
for (Book each : books) {
session.insert(book);
}
tx.commit();
session.close();
And the Transaction of StatelessSession is independent from the current transaction context.
You only need the bit with flushing and clearing the session. Leave transaction management to Spring. Use sessionFactory.getCurrentSession() to reach the session that Spring has already opened for you. Also, Spring's recent recommmendation is to avoid HibernateTemplate and work directly with Hibernate's API. Inject SessionFactory to your dao-bean.
I would refactor parse in a way it doesn't call save directly but takes some callback from service layer. Service layer would pass its transactional method with save call as this callback.
It may not work exactly as decribed in your case but from this short description this would be something I'd try.
I have a Generic Repository with a method named saveList(). The purpose of this method is to take a List and persist it in "chunks" of 500 objects. Unfortunately, I'm getting a "TransactionException: Transaction not successfully started" when I get to the commit.
Everything I've seen says that this is a result of the Spring Transaction Manager. Unfortunately, for this particular method, I need to manually control the transaction.
Relevant code is below:
// from generic non-abstract repository
#Transactional
public void saveList(List<T> objectList) {
Session session = sessionFactory.getCurrentSession();
Transaction tx = session.beginTransaction();
int i = 1;
for (T obj : objectList) {
session.save(obj);
//sessionFactory.getCurrentSession().save(obj);
i++;
if (i % 500 == 0) {
session.flush();
//sessionFactory.getCurrentSession().flush();
}
}
if (!tx.wasCommitted()) {
tx.commit();
}
//sessionFactory.getCurrentSession().getTransaction().commit();
}
Configuration from applicationContext.xml:
<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
Any assistance is appreciated.
You're using both declarative and programmatic transaction demarcation. Get rid of the session.beginTransaction() and related method calls. Only use #Transactional.
Use openSession method to get session object and close it when you finished your work. It will work perfectly. For Example
Session session = sessionFactory.openSession();
If you must use a manual transaction, then get rid of the declarative #Transactional option at the top.
I don't really see the point of doing this, since the session is not cleared between the flushes (and will thus need as much memory as if you let Hibernate flush at the end of the transaction), but if you really want to do this, and just want a new transaction just for this method, just annotate the method with
#Transactional(propagation = Propagation.REQUIRES_NEW)
and forget about the Hibernate transaction management.
This will make Spring pause the current transaction (if any), start a new transaction, execute your method and commit/rollback the new transaction, and then resume the paused transaction (if any).
I have a batch interface class that creates a hibernate Session like this:
Session session = SessionFactoryUtils.getSession(sessionFactory, true);
TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session));
Then it calls a service to get a list of objects from the DB, and in the batch class it iterates through that list and for each object it makes a service call to do some processing against the object.
Say on the 2nd object some nullpointerexception occurs somewhere, and i catch the exception in my batch class. Then i try to process the 3rd object and in the service when it tries to save the object, by calling HibernateDaoSupport.getSession(false).save(object) - it actually tries to insert it (and i get an error because a record already exists) instead of updating it.
This only happens if the previous object failed. If an exception occurs does it do something w/ that Hibernate session? Any ideas whats going on?
I have the following in my app context.xml
<bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="*" rollback-for="Throwable"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="serviceMethods" expression="execution(* com.company.service..*.*(..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="serviceMethods" />
</aop:config>
Session.save is supposed to assign an identifier to a transient instance and persisting it by doing an insert. I'm rather surprised that it doesn't do this in your first service call.
And since a NPE occurs in your service call, the transaction is rolled back (since you configured it with <tx:method name="*" rollback-for="Throwable"/>), which could explain some differences between the succeeding calls and the failing ones. I don't know what your two first lines of code are supposed to do, but as Bozho said, you should not bind resources to the TransactionSynchronizationManager yourself. It's Spring's responsibility.
Let spring create your session and manage your transactions. The TransactionSynchornizationManager is something that you should not normally use.
Greetings I am developing a non-webapplication using Spring+Hibernate.
My question is how the HibernateDaoSupport handles lazy-loading , because after a call do DAO , the Session is closed.
Take a look at following psuedo-code:
DAO is like:
CommonDao extends HibernateDaoSupport{
Family getFamilyById(String id);
SubFamily getSubFamily(String familyid,String subfamilyid);
}
Domain model is like:
Family{
private List<SubFamily> subfamiles;
public List<SubFamily> getSubFamiles();
}
SubFamily{
private Family family;
public Family getFamily();
}
In the application I get DAO from app-context and want to following operations.Is this possible to do with lazy-loading because AFAIK after every method (getFamilyById() , getSubFamily() ) the session is closed.
CommonDAO dao=//get bean from application context;
Family famA=dao.getFamilyById(familyid);
//
//Do some stuff
List<SubFamily> childrenA=fam.getSubFamiles();
SubFamily asubfamily=dao.getSubFamily(id,subfamilyid);
//
//Do some other stuff
Family famB=asubfamily.getFamily();
My question is how the HibernateDaoSupport handles lazy-loading , because after a call to DAO, the Session is closed.
The DAOs don't create/close a Session for each call, they are not responsible for this and this is usually done using the "Open Session in View" pattern (Spring provide a filter or an interceptor for this). But this is for web apps.
In a swing app, one solution is to use long session. You'll have to decide well-defined points at which to close the session to release memory. For small applications, this is usually straightforward and will work. For bigger (i.e. real life apps), the right solution is to use one session per frame/internal frame/dialog. It's harder to manage but will scale.
Some threads you might want to read:
hibernate LazyInitializationException in rich client app
Spring/Hibernate long session support or best practice?
Keep hibernate session open in swing client? (this one is IMHO the most interesting, especially #9)
And the Hibernate and Swing demo app
If you are already using Spring you can make use of its Transaction-Declaration. Using this you are able to declare a transaction for a specific method. This keeps the Sessio open for the complete tx.
Declare the Transaction Pointcut
<!-- this is the service object that we want to make transactional -->
<bean id="SomeService" class="x.y.service.SomeService"/>
<tx:advice id="txAdvice" transaction-manager="txManager">
<!-- the transactional semantics... -->
<tx:attributes>
<!-- all methods starting with 'get' are read-only -->
<tx:method name="get*" read-only="true"/>
<!-- other methods use the default transaction settings (see below) -->
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<!-- ensure that the above transactional advice runs for any execution
of an operation defined by the FooService interface -->
<aop:config>
<aop:pointcut id="MyServicePointcut" expression="execution(* x.y.service.SomeService.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="SomeServiceOperation"/>
</aop:config>
<bean id="txManager" class="org.springframework.orm.hibernate.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
Now you can do this lazy keeping the Session open for the complete Method.
public class SomeService()
{
public Family getFamily()
{
Family famA=dao.getFamilyById(familyid);
//Do some stuff
List<SubFamily> childrenA=fam.getSubFamiles();
SubFamily asubfamily=dao.getSubFamily(id,subfamilyid);
//Do some other stuff
return asubfamily.getFamily();
}
}
See Chapter 9 of Springs Tx Reference for further details.