how to fix no transaction is in progress in spring batch - spring

when I try persist some data that I recovered from my csv file on my database using my job processor in spring batch this error appear in the console , for my dao i'm using hibernate
I already tried 2 methode but the same probleme !
first :
Session session = factory.getCurrentSession();
session.saveOrUpdate(p);
second :
Session session = factory.openSession();
session.beginTransaction();
session.save(p);
session.getTransaction().commit();
session.close();
data source in my spring xml config :
all my spring xml config here https://pastebin.com/RZPr1GKL
<bean name="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/yassir" />
<property name="username" value="root" />
<property name="password" value="" />
</bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="annotatedClasses">
<list>
<value>tp.entities.Personne</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
</props>
</property>
</bean>
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<tx:annotation-driven />
the error :
javax.persistence.TransactionRequiredException: no transaction is in progress
at org.hibernate.internal.SessionImpl.checkTransactionNeeded(SessionImpl.java:3450)
at org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1418)
at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1414)
...

When we use spring batch especially for tasklet in spring boot project #EnableBatchProcessing annotations will create its own transactions (Spring Batch Transactions) for its default tables.
Hence if you want to perform any insert, update or delete in your application tables(acutal table performing CRUD). We have to explicitly mention JpaTransactionManager instead of default spring batch transactions.
#Configuration
#EnableBatchProcessing
public class MyJob extends DefaultBatchConfigurer {
#Autowired
private DataSource dataSource;
#Bean
#Primary
public JpaTransactionManager jpaTransactionManager() {
final JpaTransactionManager tm = new JpaTransactionManager();
tm.setDataSource(dataSource);
return tm;
}
}
#Primary is most important one to mention else two transaction managers will be loaded. Mention jpaTransactionManager() in your job.
return stepBuilderFactory.get("stepOne")
.transactionManager(jpaTransactionManager())
.<DetailDTO,DetailDTO>chunk(100)
.reader(reportReader)
.processor(reportProcessor)
.writer(reportWriter)
.build();
Error which i got - Caused by: javax.persistence.TransactionRequiredException: no transaction is in progress.
Click Up Vote if this is useful for you.

You configured Spring Batch to use a DataSourceTransactionManager to drive transactions. This transaction manager knows nothing about your Hibernate context. You need to use the HibernateTransactionManager to make the Hibernate Session in your writer participate in Spring Batch managed transactions.
I would also recommend to use the HibernateItemWriter instead of creating a custom writer (PersonneWriter) and manually creating the session and managing transactions.
A similar question/answer can be found here: Spring Batch JpaItemWriter vs HibernateItemWriter and why HibernateTransactionManager is needed when HibernateItemWriter is used
Hope this helps.

Related

Hibernate Transaction Manager not committing data changes

I'm using Hibernate 4 to write data to an H2 embedded in-memory database and there seems to be a problem with transactions. The application already uses Oracle and H2 has been added with a separate DataSource, SessionFactory, and TransactionManager. The original TransactionManager is marked as default and the H2 TransactionManager has the qualifier memTransactions
The following code - specifically the load function - correctly populates the memEvents variable at the end with the written data.
#Repository
#Transactional(transactionManager = "memTransactions")
public class EventMemDaoHibernate implements EventMemDao {
#Autowired
#Qualifier(value = "memSessionFactory")
private SessionFactory memSessionFactory;
#Override
public List<EventMem> getEvents() {
return memSessionFactory.getCurrentSession().createCriteria(EventMem.class).list();
}
#Override
public void load(List<Event> allEvents) {
Session session = memSessionFactory.getCurrentSession();
for (Event e : allEvents) {
EventMem memEvent = new EventMem(e);
session.save(memEvent);
}
List<EventMem> memEvents = getEvents(); // correct
}
}
However the following code produces an empty memEvents list
#Autowired
private EventMemDao eventMemDao;
List<Event> allEvents = eventDao.getAllEvents();
eventMemDao.load(allEvents); // calls the load function shown above
List<EventMem> memEvents = eventMemDao.getEvents(); // empty
I assume this is related to transaction management (e.g.: data is not auto-committed after the call to .save()). However when I tried explicitly beginning and committing a transaction within EventMemDaoHibernate#load, I receive this error:
nested transactions not supported
So, from what I can tell the TransactionManager is working.
My TransactionManager and related bean definitions are shown below.
<bean
id="memTransactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="memSessionFactory" />
<qualifier value="memTransactions"/>
</bean>
<bean id="hDataSource" class="org.h2.jdbcx.JdbcDataSource">
<property name="url" value="jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1;INIT=RUNSCRIPT FROM 'classpath:scripts/init-h2.sql'" />
<property name="user" value="sa" />
<property name="password" value="" />
</bean>
<bean
id="memSessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="hDataSource" />
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.H2Dialect</prop>
</props>
</property>
</bean>
This was due to my configuration error (of course). I didn't fully grasp that the connection URL was evaluated every time a session was opened against H2 and that means init-h2.sql was executed repeatedly. init-h2.sql included a truncate followed by an insert so it was dropping and recreating data every time Hibernate opened a session.

Should I use beginTransaction while using #Transactional?

I am quite confused with Spring and Hibernate transactions. I have the following sample code.
I am wondering if
This is a correct way of retrieval or not.
Should I use getCurrentSession().beginTransaction() as well, should I use it in conjunction with #Transactional at all?
Configuration
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:2000/HiberProject" />
<property name="username" value="jack" />
<property name="password" value="jack" />
</bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"
depends-on="dataSource">
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan" value="com.hiberproject.model" />
<property name="hibernateProperties">
<props>
<prop key="hibernate.format_sql">true</prop>
<prop key="hibernate.use_sql_comments">true</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
</props>
</property>
</bean>
<bean
class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" />
<bean id="transactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
Service
#Service
public class SampleRecordsServiceImpl implements SampleRecordsService{
#Autowired
SampleRecordsRepository sampleRecordsRepository;
#Override
#Transactional(readOnly=true)
public Record retrieveRecord(long id){
return sampleRecordsRepository.retrieveRecord(id);
}
}
Repository
#Repository
public class SampleRecordsRepository implements SampleRecordsRepository{
#Autowired
SessionFactory sessioFactory;
#Override
public Record retrieveRecord(long id){
return (Record) sessionFactory.getCurrentSession().get(Record.class,id);
}
}
The #Transactional annotation itself defines the scope of a single database transaction. The database transaction happens inside the scope of a persistence context.
The persistence context is just a synchronizer object that tracks the state of a limited set of Java objects and makes sure that changes on those objects are eventually persisted back into the database.
For #Transactional annotation you can set propagation attribute, using Propagation you can handle your tarnsaction in different way like Propagation.REQUIRES_NEW(if you need new transaction on every request) . the default propagation is REQUIRED.
session.beginTransaction() will also either begin a new Transaction if one isn't present, or it will use an existing transaction to begin the unit of work specified.
So you should use either one of approach to manage the transaction.
Yes that's Okay to use only #Transactional annotation like this when you use Spring to manage your transaction.
No. You don't need to do that! If you use #Transactional annotation in your service, then Spring takes care of your persistence layer to manage transaction. All you need is to declare persistence layer specific transaction manager in your Spring configuration. So you do not need to manage transaction with hibernate sessions by using session.beginTransaction() together with #Transactional.
For more information please see the documentation of using #Transactional.

Is JTA manager necessary to use transaction features in hibernate 4

I'm following the tutorial here:
http://www.javacodegeeks.com/2013/05/hibernate-4-with-spring.html
to enable the "#Transactional annotation" in my Java web application but failed to make it run properly. Please advise if the JTA manager is really required, and why?
Please note that my webapp is based on Spring 3 + Hibernate 4 + Tomcat 7.
Background and my doubts:
My current web application uses my own custom class (implements HandlerInterceptor) to enable one-hibernatesession-per-request basis. Now I want to improve my application's maintainability by using the "#Transactional annotation" instead since that could save many lines of code.
According to my understanding, the #Transactional basically relies on the AOP concept to ensure the session (Hibernate session) is ready for use in the annotated method. This seems nothing to do with the JTA. But I wonder why can't I make it work on my webapp in Tomcat 7 (without JTA-provider).
After few searches on google, it looks like the JTA is required. This confuses me since this seems to be a very basic functionality that shouldn't have the complicated JTA-provider as a requirement.
Here is the error I got:
org.hibernate.HibernateException: No Session found for current thread
org.springframework.orm.hibernate4.SpringSessionContext.currentSession(SpringSessionContext.java:97)
org.hibernate.internal.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:988)
...
This is the code I use for testing:
....
#Autowired
org.hibernate.SessionFactory sessionFactory;
#Transactional
#RequestMapping(method = RequestMethod.GET)
protected String home() {
Session session = sessionFactory.getCurrentSession(); // I expected the session is good to use now
Province p = (Province) session.get(Province.class, 1L); // This causes no session found error :(
return "home";
}
The spring XML:
....
<tx:annotation-driven/>
<context:component-scan base-package="..."/>
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="java:comp/env/jdbc/..."/>
<property name="lookupOnStartup" value="true"/>
<property name="proxyInterface" value="javax.sql.DataSource"/>
</bean>
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="hibernateProperties">
<props>
<prop key="hibernate.hbm2ddl.auto">update</prop>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop>
</props>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<bean id="persistenceExceptionTranslationPostProcessor" class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>
....
Thank you !
Just a speculation:
Your controller is defined in some kind of dispatcher-servlet.xml therefore seperates from the applicationContext in which < tx:annotation-driven/> is defined. The compoment you want to enhance with #Transactional need to be within the same context with < tx:annotation-driven> if I'm not mistaken. So the #Transactional does not work.
That was my silly mistake. The Spring uses CGLIB to proxy methods with #Transactional annotated and it seems like CBLIB can't enhance protected method.
protected String home() {
Changing this to
public String home() {
fixed the problem.

Spring + Hibernate + JPA + 2 databases. persist silently fails to commits with #Transactional annotation

My setup is Spring MVC 3.1, Hibernate 4.1 with two databases.
My service methods seem to work fine for reads (for both dbs). But the persist fails to insert the data in the db - and no exception in the logs at all. Looking at the sql output of hibernate it looks like it did retrieve the newly generated id but then never did the insert - if it did it doesn't show in the db nor was an insert logged in the log file. In the db I can see the sequence number is incremented but no data was inserted. I couldn't figure out what is wrong with my config. Hopefully someone has an idea.
Below are the relevant pieces from config file
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
</bean>
<bean id="mgrFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
p:dataSource-ref="dataSource">
<property name="packagesToScan" value="xxx.yyy" />
<property name="persistenceUnitName" value="puOne"/>
<property name="persistenceProviderClass" value="org.hibernate.ejb.HibernatePersistence"/>
</property>
</bean>
<tx:annotation-driven mode="aspectj" transaction-manager="txMgr"/>
<bean id="txMgr"
class="org.springframework.orm.jpa.JpaTransactionManager"
p:entityManagerFactory-ref="mgrFactory">
</bean>
This works fine as it is. What I want to do now is add the ability to use another database. So I added another data source, entity manager factory and transaction manager
<bean id="dataSourceTwo" class="org.apache.commons.dbcp.BasicDataSource">
</bean>
<bean id="mgrFactoryTwo"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
p:dataSource-ref="dataSourceTwo">
<property name="packagesToScan" value="xxx.zzz" />
<property name="persistenceUnitName" value="puTwo"/>
<property name="persistenceProviderClass" value="org.hibernate.ejb.HibernatePersistence"/>
</property>
</bean>
<tx:annotation-driven mode="aspectj" transaction-manager="txMgrTwo"/>
<bean id="txMgrTwo"
class="org.springframework.orm.jpa.JpaTransactionManager"
p:entityManagerFactory-ref="mgrFactoryTwo">
</bean>
I also made sure that the two respective base DAOs annotate their entity manager's with the unit name like
#PersistenceContext(unitName = "puOne")
protected EntityManager entityManager;
The last thing I did was to add the transaction manager names inside my #Transactional annotations inside my services like
#Override
#Transactional(value="txMgrTwo" propagation = Propagation.REQUIRED)
public boolean create(User user) {
userDao.persist(user);
}

How to exposeTransactionAware on a 2nd sessionFactory in hibernate4 + spring?

The test below was working in my application with Hibernate3. When I upgraded it to hibernate4, it started failing.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(inheritLocations=false,locations={
"/hibernate/spring-SF-tests.xml",
"/hibernate/spring-transaction.xml",
"/hibernate/testBeans.xml"
,"/hibernate/spring-audit.xml",
"/hibernate/iwrs-mail-beans-test.xml",
"/hibernate/fake-audit-meaning.xml"
})
#TransactionConfiguration(transactionManager="txManager", defaultRollback=true)
public class CodeAuditIntegrationTest extends CodeIntegrationTest {
#Autowired
private SessionFactory auditFactory;
#Before
public void cleanAudit(){
auditFactory.getCurrentSession().createQuery("delete from AuditLogRecord").executeUpdate();
}
#Test
public void clinicalTrialAssociationTest() {
super.clinicalTrialAssociationTest();
}
}
Which is failing with:
org.hibernate.HibernateException: No Session found for current thread
at
org.springframework.orm.hibernate4.SpringSessionContext.currentSession(SpringSessionContext.java:97)
at
org.hibernate.internal.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:881)
I have 2 different session factories in my app: sessionFactory and auditFactory. Both are configured in spring (see [1]).
The problem is that in an hibernate4 configuration the property exposeTransactionAwareSessionFactory was removed. I had that set to true for my auditFactory. I believe removing this property makes the injected auditFactory not being able to pick up the session in the transaction (as txManger is configured for sessionFactory), therefore yielding the error.
Questions:
how can I make the auditFactory have transactions managed by spring on this test?
Is that what was happening with the exposeTransactionAwareSessionFactory property in hibernate 3?
The only alternative I see is to wrap all the code that uses auditFactory in a Helper class annotated with #Transactional(otherTxManager). I did try that, but I got a couple of additional problems there:
I needed to use a separate DataSource (else I'd get [2])
Using 2 separate datasources I'll get a similar error on my Cucumber tests, now related with the transactionManager [3]
[1] The relavant XML configuration:
<bean id="txManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<tx:annotation-driven transaction-manager="txManager" />
<bean id="sessionFactory" scope="singleton"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="hibernateProperties">
<props>
<prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2dll}</prop>
<prop key="hibernate.hbm2ddl.import_files">${hibernate.sql_scripts}</prop>
</props>
</property>
<property name="packagesToScan">...</property>
<property name="annotatedPackages">...</property>
<property name="mappingLocations">...</property>
<property name="dataSource" ref="c3p0DataSource" />
<property name="entityInterceptor" ref="auditInterceptor" />
</bean>
<bean id="auditFactory" scope="singleton"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="exposeTransactionAwareSessionFactory"> -->needs to be removed in hibernate4!
<value>true</value>
</property>
<property name="mappingLocations">...</property>
<property name="packagesToScan">...</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2dll}</prop>
</props>
</property>
<property name="dataSource" ref="c3p0DataSource" />
</bean>
[2] Problem with using the same dataSource for the two factories:
org.springframework.transaction.IllegalTransactionStateException: Pre-bound JDBC Connection found! HibernateTransactionManager does not support running within DataSourceTransactionManager if told to manage the DataSource itself. It is recommended to use a single HibernateTransactionManager for all transactions on a single DataSource, no matter whether Hibernate or JDBC access.
at org.springframework.orm.hibernate4.HibernateTransactionManager.doBegin(HibernateTransactionManager.java:329)
[3] Problem in cucumber tests due to declaring 2 transaction managers:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No unique bean of type [org.springframework.transaction.PlatformTransactionManager] is defined: expected single bean but found 2: txAudit,txManager

Resources