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.
Related
I have Spring 4 with Hibernate 4. My problem is that I want to have transaction on imported data. I parse JSON and next store it to DB. But when something will go wrong (server will go down). I don't want to have part of data in DB.
I have method:
#Transactional
private void processJson()
in which I call in loop:
recipientGroupSellInService.saveOrUpdate(perDay);
it looks much like this:
#Transactional
private void processJson(){
for(int i : iSet){
recipientGroupSellInService.saveOrUpdate(perDay[i]);
}
}
save or update:
#Transactional
public Boolean saveOrUpdate(T model) {
getSession().saveOrUpdate(model);
return true;
}
so after calling saveOrUpdate data is stored to DB, and not roll back on general failure of method processJson.
What should I look after or change to have "real transaction" on method processJson?
The transaction is started in processJson(). Since your saveOrUpdate() is using the regular #Transactional, it will join the previously started transaction.
If there is a problem (i.e. a RuntimeException), anything made by or from processJson() will be rolled back.
Are you claiming that you're seeing behaviour where partial data is written?
Have you configured the PlatformTransactionManager? (either from xml or from Configuration)
Eg:
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
I just have a new project to maintain, and using Hibernate+Spring. I wrote a DeliveryInfoServiceImpl which have a method to query for some entity but not have any update or save operation, but I have an error:
Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here
Unless I add #Transactional on the method or the class.
My questions are :
Why I have to add #Transactional though I am only executing select query?
Does adding #Transactional means enable transaction support ,which may have more unnecessary overhead when I am only using "select" query.
Below is my code snippet:
#Override
public List<UnavailableRestaurantBean> getUnavailableRestaurantBean(String custAddress, List<Long> dishesId) {
List<Dish> dishes = getDishByIds(dishesId);//exception here
....
}
private List<Dish> getDishByIds(List<Long> ids){
return deliveryInfoDao.findByIds(Dish.class,ids);
}
And I have below transaction manager config:
<tx:annotation-driven transaction-manager="myTxManager" />
<bean id="myTxManager" name="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="mySessionFactory">
</property>
</bean>
I am having an issue in saving and retrieving objects in database in just one request.
I want to clear the cache of our hibernate session to get the updated entity in our database.
My code looks like this:
public class SampleController{
protected ModelAndView onSubmit(HttpServletRequest request, HttpServletResponse response, Object command, BindException errors)
throws Exception {
myServiceOne.doAllotsOfSaving(parameters);
//some code enhancements to remove cache in hibernate session
//without affecting the session of other user logged in.
//some fields in MyEntity class contains the old values but the actual data in database is already updated
MyEntity entity = myServiceTwo.getMyEntityByOrderNo(orderNo);
}
}
--configurations
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:hibernate.cfg.xml" />
<property name="hibernateProperties">
<ref local="hibernateProperties"/>
</property>
<property name="entityInterceptor">
<ref bean="auditLogInterceptor" />
</property>
</bean>
<bean id="myServiceOne" class="com.test.service.impl.MyServiceOneImpl">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<bean id="myServiceTwo" class="com.test.service.impl.MyServiceTwoImpl">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
--configurations
Does java web application contains only one hibernate session and how to clear this hibernate session?
No any hibernate based application must use multiple sessions. Each of these sessions must be closed when they perform their task. Hibernate can manage sessions for you if you configure the same in hibernate's configuration file.
However you should have only one instance of SessionFactory per application.
To clear the session you can call the session.clear() method. It clears the session level cache.
without affecting the session of other user logged in
Since you have a web application, you have a different thread for each user for database transactions. This means each user will have a different hibernate session, so you won't have to worry about this. If by some means you're using same session for all the users, you're doing it wrong and results can be catastrophic. After some time you'll get an OutOfMemoryError because of session level cache.
You must note that you cannot disable hibernate session level cache. For this purpose you may use StatelessSession.
Session factory is long live multithreaded object.
Usually one session factory should be created for one database.
A Session is used to get a physical connection with a database. The Session object is lightweight and designed to be instantiated each time an interaction is needed with the database.
The main function of the Session is to offer CRUD operations for instances of mapped entity classes. Instances may exist in one of the following three states at a given point in time:
transient: A new instance of a a persistent class which is not associated with a Session and has no representation in the database and no identifier value is considered transient by Hibernate.
persistent: You can make a transient instance persistent by associating it with a Session. A persistent instance has a representation in the database, an identifier value and is associated with a Session.
detached: Once we close the Hibernate Session, the persistent instance will become a detached instance.
I am using Spring and Hibernate in my application and using Spring Transaction.
So I have a service layer with annotation #Transaction on methods and DAO layer having methods for database query.
#Transactional(readOnly = false)
public void get(){
}
The issue is when I want to save an object in the database,then I have to use session.flush() at the end of DAO layer method. Why?
I think if I have annotated #Transaction, then Spring should automatically commit the transaction on completion of the service method.
DAO layer :
public BaseEntity saveEntity(BaseEntity entity) throws Exception {
try {
Session session = sessionFactory.getCurrentSession();
session.saveOrUpdate(entity);
session.flush();
} catch (HibernateException he) {
throw new Exception("Failed to save entity " + entity);
}
return entity;
}
Service layer :
#Transactional(readOnly = false)
public BaseEntity saveEntity(BaseEntity entity) throws Exception {
return dao.saveEntity(entity);
}
spring config :
<context:property-placeholder properties-ref="deployProperties" />
<tx:annotation-driven transaction-manager="transactionManager" />
<!-- Activate Spring Data JPA repository support -->
<jpa:repositories base-package="com" />
<!-- Declare a datasource that has pooling capabilities-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close"
p:driverClass="${app.jdbc.driverClassName}"
p:jdbcUrl="${app.jdbc.url}"
p:user="${app.jdbc.username}"
p:password="${app.jdbc.password}"
p:acquireIncrement="5"
p:idleConnectionTestPeriod="60"
p:maxPoolSize="100"
p:maxStatements="50"
p:minPoolSize="10" />
<!-- Declare a JPA entityManagerFactory -->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
p:persistenceXmlLocation="classpath*:META-INF/persistence.xml"
p:persistenceUnitName="hibernatePersistenceUnit"
p:dataSource-ref="dataSource"
p:jpaVendorAdapter-ref="hibernateVendor"/>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"
p:dataSource-ref="dataSource" p:configLocation="${hibernate.config}"
p:packagesToScan="com" />
<!-- Specify our ORM vendor -->
<bean id="hibernateVendor" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"
p:showSql="false"/>
<!-- Declare a transaction manager-->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"
p:entityManagerFactory-ref="entityManagerFactory"/>
Yes, if you have #Transactional for your DAO method then you need not flush the session manually, hibernate will take care of flushing the session as part of committing the transaction if the operations in the method are successful.
Check this link to know on how #Transactional works - Spring - #Transactional - What happens in background?
By default, hibernate stacks its queries so they can be optimized when they are finally executed onto the database.
The whole point of flush is to flush this stack and execute it in your transaction onto the database. Your leaving the "safe" house of the JVM and execute your query on a big strange database.
This is why you can't select something you've just saved without a flush. It's simply not in the database yet.
The meaning of commit is to end the transaction and make changes of the database visible for others. Once commit has been executed there's no return possible anymore.
Frankly I'm not exactly sure if it is a best practice but for normal CRUD operations you should be able to add flush into your DAO layer.
This way you don't need to worry about it into the service layer.
If you want java to optimize your transaction then you'll have to add it into your service layer. But remember that you don't need to solve performance issues when there aren't any! Flushes all over your code into the service layer is not good for the code readability. Keep it simple and stupid ;)
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).