hibernate session.flush with spring #transactional - java

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 ;)

Related

Avoid Transaction rollback in Spring

Assuming I have the next code:
#Autowired
private IManager1 manager1;
#Autowired
private IManager2 manager2;
#Autowired
private IManager3 manager3;
#Transactional
public void run() {
manager1.doStuff();
manager2.registerStuffDone();
manager3.doStuff();
manager2.registerStuffDone();
manager1.doMoreStuff();
manager2.registerStuffDone();
}
If any exception is launched I want to rollback everything done by the "doStuff()" methods, but I don't want to rollback the data recorded by the "registerStuffDone()" method.
I've been reading the propagation options for #Transactional annotation, but I don't understand how to use them properly.
Every manager internally uses hiberante to commit the changes:
#Autowired
private IManager1Dao manager1Dao;
#Transactional
public void doStuff() {
manager1Dao.doStuff();
}
Where the dao looks like this:
#PersistenceContext
protected EntityManager entityManager;
public void doStuff() {
MyObject whatever = doThings();
entityManager.merge(whatever);
}
This is my applicationContext configuration:
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSourcePool" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"/>
</property>
</bean>
<bean id="entityManager" class="org.springframework.orm.jpa.support.SharedEntityManagerBean">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
Ideas?
You need 2 transactions, one for the stuff to be committed and one for the stuff to be rolled back.
#Transactional(propagation = Propagation.REQUIRES_NEW, noRollbackFor={Exception1.class, Exception2.class})
public void registerStuffDone()() {
//code
}
Your run method will then use the first transaction and that will be rolled back, but the registerStuffDone method will start a second transaction which will be commited.
You are using declarative transaction and want to control like program sense. For this reason, you need more practice and deep understanding about Spring transaction definition such as PROPAGATION, ISOLATION etc...
Programmatic transaction management: This means that you have manage the transaction with the help of programming. That gives you extreme flexibility, but it is difficult to maintain.
VsDeclarative transaction management: This means you separate transaction management from the business code. You only use annotations or XML based configuration to manage the transactions.
Perhaps, alternative way for your questions by Programmatic transaction management.
/** DataSourceTransactionManager */
#Autowired
private PlatformTransactionManager txManager;
public void run() {
try {
// Start a manual transaction.
TransactionStatus status = getTransactionStatus();
manager1.doStuff();
manager2.registerStuffDone();
manager3.doStuff();
manager2.registerStuffDone();
manager1.doMoreStuff();
manager2.registerStuffDone();
//your condition
txManager.commit(status);
//your condition
txManager.rollback(status);
} catch (YourException e) {
}
}
/**
* getTransactionStatus
*
* #return TransactionStatus
*/
private TransactionStatus getTransactionStatus() {
DefaultTransactionDefinition dtd = new DefaultTransactionDefinition();
dtd.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
dtd.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
dtd.setReadOnly(false);
return txManager.getTransaction(dtd);
}
Note: It does not mean you need to always use one approach like Programmatic transaction management. I prefer mixed approach. Please use easy way like Declarative transaction for simple database services, otherwise, just control with Programmatic transaction in your services will save your logic easily.

Spring MVC 4 service #tTransactional doesn't work

1. Spring MVC application-context.xml
<tx:annotation-driven/>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="username" value="test"/>
<property name="password" value="test"/>
<property name="url" value="jdbc:mysql://localhost:13306/test"/>
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
2. Service impl class
#Override
#Transactional
public void deleteCommentAndFiles(int commentId) {
int deletedCommentCount = commentDAO.deleteComment(commentId);
int deletedFileCount = fileDAO.deleteFiles(commentId);
if (deletedCommentCount != 1) {
throw new IncorrectUpdateSemanticsDataAccessException("Deleted comment not 1 [deletedCount : " + deletedCommentCount);
}
if (deletedFileCount != 1) {
throw new IncorrectUpdateSemanticsDataAccessException("Deleted file not 1 [deletedCount : " + deletedCommentCount);
}
}
3. Test Case
#Test
public void rollbackT() {
boolean hasException = false;
int sizeBeforDelete = commentDAO.selectCountByArticle(1);
try {
commentService.deleteCommentAndFiles(1);
} catch (RuntimeException e) {
hasException = true;
}
Assert.assertTrue(hasException);
Assert.assertEquals(sizeBeforDelete, commentDAO.selectCountByArticle(1));
}
in Test case
first Assert.assertTrue(hasException); is passed but
Assert.assertEquals(sizeBeforDelete, commentDAO.selectCountByArticle(1)) this case fail Expected : 1 but Actual : 0
this second TC fail mean Exception is occur but doesn't rollback delete comment
deleteCommentAndFiles method throw exception but doesn't rollback
Im trying to use #Transactional(propagation=Propagation.REQUIRED, rollbackFor={IncorrectUpdateSemanticsDataAccessException.class})
but same doesn't work
why #Transactional annotaion doesn't work?
I've also had the same issue. I moved the #transactional to the controller in order to works.
#EnableTransactionManagement and only looks for #Transactional on beans in the same application context they are defined in. This means that, if you put annotation driven configuration in a WebApplicationContext for a DispatcherServlet, it only checks for #Transactional beans in your controllers, and not your services. See Section 21.2, “The DispatcherServlet” for more information.
The database has to support transactions. In case of MySQL you need to create a table with type of InnoDB, BDB or Genini.
Per default myisam does not support transactions.
I didn't see your main test class, but i assume you use the default spring configuration for junit :
By default, a test is launched on his own implicit transaction. when you call your service, it start a "sub" logical transaction (which is a part of the test transaction, because you didn't use require_new for the propagation). when this transaction failed, the main transaction is marked for rollback, but the rollback is not done until the main transaction has finished, when the test end
If you want to test a rollback, you shouldn't use this implicit transaction, launched by the test framework, or you can use TestTransaction.end() before your assertions to force this transaction to be committed or rollback.

"No Hibernate Session bound to thread" when querying unless adding #Transactional annotation

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>

Spring Declarative transaction management failed with jdbc template

I am using Spring 3.2.2 version,
I have a issue when I am using #Transactional with jdbc template,
My XML configuration is below:
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager"
p:sessionFactory-ref="sessionFactory" />
<bean id="reportTransactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="reportDataSource" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" order="0"/>
<tx:annotation-driven transaction-manager="reportTransactionManager" order="1"/>
Code :
#Transactional(value = "reportTransactionManager", rollbackFor = Exception.class)
public ReportResponse deleteAndSave(input) throws ReportServiceException {
service1.saveData(input);
service2.saveData(input);
service3.saveData(input);
service4.saveData(input);
service5.saveData(input);
service6.saveData(input);
service7.saveData(input);
} catch (Exception exception) {
_LOGGER.debug("Caught exception all KPI changes should be reverted back for scenario id {}", scenarioId);
_LOGGER.error(ExceptionUtils.getFullStackTrace(exception));
throw new ReportServiceException(exception.getMessage(), exception);
}
return reportResponse;
}
So issues is whenever any service fails everthing should roll back but in my case if suppose service7 failed all services are roll backed except one service i.e service4 only,
I thought may be transaction is not propagating to service4 api, then I tested using
TransactionSynchronizationManager.getCurrentTransactionName();
TransactionSynchronizationManager.isSynchronizationActive();
TransactionSynchronizationManager.isCurrentTransactionReadOnly();
TransactionSynchronizationManager.isActualTransactionActive();
Its returning me the transaction and its details properly, but still not rollbacking
I surprised that why only that service api is failed to rollback , Can any one please suggest me on this.
Note : Apart from deleteAndSave api in root method, no other services/ repository classes does not have any #transaction becoz by default spring will propagate.
I dont have anyissue with hibernate transactionManager bcoz these services involved with reportTransactionManager manager only.

Batch Insertions with Hibernate & Spring

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.

Categories

Resources