Spring batch doesnt start transaction when using custom writer - spring

I am working on a spring batch which reads from a csv file and writes into database. i am using FlatFileItemReader for reading the file and implemented ItemWriter which uses Jpa to insert data into database. But batch fails with no transaction in progress.
Here is my job configuration
<bean id="datasource" class="oracle.jdbc.pool.OracleDataSource">
<property name="user" value="xxx" />
<property name="password" value="xx" />
<property name="URL" value="xxx" />
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<bean id="entityManagerFactory" name="model"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="datasource"/>
<property name="jpaVendorAdapter">
<bean
class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="generateDdl" value="false"/>
<property name="showSql" value="true"/>
<property name="database">
<util:constant
static-field="org.springframework.orm.jpa.vendor.Database.ORACLE"/>
</property>
<property name="databasePlatform" value="org.hibernate.dialect.Oracle12cDialect"/>
</bean>
</property>
</bean>
<batch:job id="testJob">
<batch:step id="step">
<batch:tasklet>
<batch:chunk reader="cvsFileItemReader" writer="databaseWriter"
commit-interval="10">
</batch:chunk>
</batch:tasklet>
</batch:step>
</batch:job>
And below is my writer
public class DatabaseWriter implements ItemWriter<Report> {
private EntityManagerFactory entityManagerFactory;
#Autowired
public DatabaseWriter(EntityManagerFactory entityManagerFactory) {
this.entityManagerFactory = entityManagerFactory;
}
#Override
public void write(List<? extends Report> list) throws Exception {
EntityManager entityManager = entityManagerFactory.createEntityManager();
for (Report report : list) {
entityManager.persist(report );
}
entityManager.flush();
}
}
It only works if start transaction explicitly .that is like below
public class DatabaseWriter implements ItemWriter<Report> {
private EntityManagerFactory entityManagerFactory;
#Autowired
public DatabaseWriter(EntityManagerFactory entityManagerFactory) {
this.entityManagerFactory = entityManagerFactory;
}
#Override
public void write(List<? extends Report> list) throws Exception {
EntityManager entityManager = entityManagerFactory.createEntityManager();
entityManager.getTransaction().begin();
for (Report report : list) {
entityManager.persist(report );
}
entityManager.getTransaction().commit();
}
}
Is it really needed that transaction should be maintained explicitly and spring batch doesn't control it out of box?
EDIT
I fixed it by changing writer like
public class DatabaseWriter implements ItemWriter<Report> {
#PersistenceContext
private EntityManager entityManager;
#Override
public void write(List<? extends Report> list) {
for (Report report : list) {
entityManager.persist(report );
}
}
Changing EntityMangerFactory to EntityManager with PersistenceContext fixed the problem. But i am struggling to understand reason for this behaviour

Create DAO and move your JPA code there and autowire the DAO in your writer.
Also make sure you autowire the entity manager like below.
#PersistenceContext
private EntityManager em;

But i am struggling to understand reason for this behaviour
The first approach does not work because you create a transaction manually while your code is actually running within the scope of a transaction driven by Spring Batch. So there will be two transactions with different contexts.
You should keep in mind that a tasklet is executed in a transaction driven by Spring Batch (including the item writer for a chunk-oriented tasklet). So any code running in that scope should conform to the transaction definition of the tasklet. In your case, the EntityManager injected in the writer is driven by the JpaTransactionManager you defined, which is also used by Spring Batch for the tasklet's transaction.
As a side note, you can use the JpaItemWriter provided by Spring Batch instead of writing a custom writer. The code is almost identical.

Related

Connection pool replacement for already implemented Spring Jdbctemplate project

I am a doing a mid size project with spring jdbc and MsSQL server , project is almost 50% done , now when every request doing lots of inserts and updates specially with those tables which contains lots of columns and large datasets is performing very slow , and sometimes showing connection closed.
Now i am thinking to integrate C3p0 or similar connection pooling but i cant change any DAO code which i already done ..
I implemented a DAOHelper class with JDBCTemplate variable and injecting the JDBCTemplate dependency in applicationContext.xml with autowiring of DAOClass in controller class , and i extended this DAOHelper to all DAO classes and using this jdbcTemplate to do JDBC operations.
<bean id="ds" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.microsoft.sqlserver.jdbc.SQLServerDriver"/>
<property name="url" value="jdbc:sqlserver://192.168.1.101:1433;databaseName=OrderManager"/>
<property name="username" value="sa"/>
<property name="password" value="520759"/>
</bean>
<bean id="JdbcDataSource" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="ds"/>
</bean>
<bean id="OrderDAO" class="com.ordermanager.order.dao.OrderDAO" >
<property name="jdbcTemplate" ref="JdbcDataSource"/>
<property name="transactionManager" ref="transactionManager"/>
</bean>
#Controller
public class OrderController {
#Autowired
OrderDAO orderDAO;
#RequestMapping(value = "/addNewItem", method = RequestMethod.GET)
public ModelAndView addItem(#RequestParam("ParamData") JSONObject paramJson) {
ApplicationContext ctx = new FileSystemXmlApplicationContext(ConstantContainer.Application_Context_File_Path);
OrderDAO orderDAO = (OrderDAO) ctx.getBean("OrderDAO");
return new ModelAndView("MakeResponse", "responseValue", orderDAO.addItem(paramJson));
}
public class DAOHelper {
private JdbcTemplate jdbcTemplate;
private PlatformTransactionManager transactionManager;
public PlatformTransactionManager getTransactionManager() {
return transactionManager;
}
public void setTransactionManager(PlatformTransactionManager txManager) {
this.transactionManager = txManager;
}
public JdbcTemplate getJdbcTemplate() /*I am using this Method for all JDBC Task*/ {
return jdbcTemplate;
}
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
Now with minimal code changes how can i integrate C3p0 or any good connection pooling library with my already written code.
Just change the ds bean in your config xml with following and consider adding other c3p0 properties according to your own. make sure to have c3p0 jar in your classpath.
<bean id="ds" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close">
<property name="driverClass" value="com.microsoft.sqlserver.jdbc.SQLServerDriver" />
<property name="jdbcUrl" value="jdbc:sqlserver://192.168.1.101:1433;databaseName=OrderManager" />
<property name="user" value="sa" />
<property name="password" value="520789" />
</bean>

Spring Data lazyload does not LazyInitializationException

#Component
#Transactional
public class TestClass extends AbstractClass
{
#Autowire
ClassARepo classARepo;
#Override
public void test() {
ClassA classA = classARepo.findOne(1);
List<ClassB> list = classA.getClassBs();
list.size();
}
}
ClassB is mapped as onetomany and lazily loaded.
In the above code
classARepo.findOne(1);
Executes correctly. but
List<ClassB> list = classA.getClassBs();
list.size();
Fails with LazyInitializationException.
public interface ClassARepo extends CrudRepository<ClassA, Integer> {
}
Instance for TestA is created like the one below
#PersistJobDataAfterExecution
#DisallowConcurrentExecution
#Transactional
#Component
public class TestClassJOB extends AbstractJob
{
#Autowired
TestClass indexer;
}
Context:
<!-- JPA mapping configuration -->
<bean id="persistenceXmlLocation" class="java.lang.String">
<constructor-arg value="classpath:/persistence.xml"></constructor-arg>
</bean>
<!-- entity manager -->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
p:dataSource-ref="dataSource" p:persistenceUnitName="jpaData"
p:persistenceXmlLocation-ref="persistenceXmlLocation">
<property name="packagesToScan" value="com..persist.entity" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
</property>
</bean>
<!-- transaction manager -->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"
p:entityManagerFactory-ref="entityManagerFactory" lazy-init="true" p:dataSource-ref="dataSource" />
<!-- JPA repositories -->
<jpa:repositories base-package="com..persist.repo"
entity-manager-factory-ref="entityManagerFactory" transaction-manager-ref="transactionManager" />
I tried many resources and could not solve the issue. The following error message is displayed "could not initialize proxy - no Session".
What could be the cause of the issue?
When the session is available while classARepo.findOne(1) is called, why is not available during lazy fetch(list.size())?
The issue was the instance for TestClassJOB was created by Quartz. So the transnational proxy was not applied to the class which was the reason for the issue.
I fixed the issue by declaring a transaction template
#Autowired
TransactionTemplate transactionTemplate;
and then wrapping the code within
transactionTemplate.execute(new TransactionCallbackWithoutResult()
{
#Override
protected void doInTransactionWithoutResult(TransactionStatus status)
{
<code here>
}
}

JPA entityMangerFactory not found through Persistence.createEntityManagerFactory("XYZ")

Any help will be greatly appreciated.
We are working on a web application. Which uses a JAR file (a java maven project) and has been added as a maven dependency in the web application.
Combination of this JAR file and web application itself creating problem.
Both web application and JAR are using Hibernate JPA to interact with database. But both are using 2 different ways for creating/initializing entityManagerFactory.
Web Application uses Spring xml based configuration to initialize entityManagerFactory.
CODE:
persistence.xml code:
<persistence-unit name="org.jbpm.persistence.jpa.local"
transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<mapping-file>META-INF/JBPMorm-JPA2.xml</mapping-file>
<class>org.drools.persistence.info.SessionInfo</class>
<class>org.jbpm.persistence.processinstance.ProcessInstanceInfo</class>
<class>org.drools.persistence.info.WorkItemInfo</class>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.SQLServerDialect" />
<property name="hibernate.max_fetch_depth" value="3" />
<property name="hibernate.show_sql" value="true" />
</properties>
</persistence-unit>
Spring configuration:
<context:component-scan base-package="com.company.rd.core" />
<context:component-scan base-package="com.company.rd.services" />
<jee:jndi-lookup id="testDataSource" jndi-name="java:comp/env/jdbc/SybaseDB" />
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="testDataSource"/>
<property name="defaultTimeout" value="120"></property>
</bean>
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="org.jbpm.persistence.jpa.local" />
<property name="dataSource" ref="testDataSource" />
<property name="jpaDialect" ref="jpaDialect" />
<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="jpaDialect" class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />
<bean id="jpaTransactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
<property name="jpaDialect" ref = "jpaDialect"></property>
<property name="defaultTimeout" value="120"></property>
</bean>
<jee:jndi-lookup id="logDataSource" jndi-name="java:comp/env/jdbc/DRMLOG" />
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
</beans>
And Here is the code to initializing entitymanagerFactory in JAR file.
persistence.xml
<persistence-unit name="codeAuthorization" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<non-jta-data-source>java:/comp/env/jdbc/SybaseDB</non-jta-data-source>
<class>com.company.auth.entity.AuthorizationCode</class>
<class>com.company.auth.entity.UserInvalidAttempt</class>
<class>com.company.auth.entity.AuthorizationProperty</class>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.SQLServerDialect"/>
<property name="hibernate.show_sql" value="true" />
</properties>
</persistence-unit>
And a java file which is injected into Base DAO through spring.
#Service
public class AuthorizationEntityMangerService {
#PersistenceUnit(name = "codeAuthorization")
private EntityManagerFactory entityManagerFactory;
public AuthorizationEntityMangerService() {
entityManagerFactory = Persistence
.createEntityManagerFactory("org.jbpm.persistence.jpa.local");
}
public EntityManagerFactory getEntityManagerFactory() {
return entityManagerFactory;
}
public EntityManager getEntityManager() {
return this.entityManagerFactory.createEntityManager();
}
public void closeEntityManager(EntityManager entityManager) {
if (entityManager != null && entityManager.isOpen()) {
entityManager.close();
}
}
public EntityTransaction getTransaction(EntityManager entityManager) {
return entityManager.getTransaction();
}
public void rollBackTransaction(EntityTransaction transaction) {
if (transaction != null && transaction.isActive()) {
transaction.rollback();
}
}
public void commitTransaction(EntityTransaction transaction) {
if (transaction != null && transaction.isActive()) {
transaction.commit();
}
}
}
Calling code from Base DAO.
public Object getSingleResult(final String queryString, final String key,
final NamedQueryParameter namedQueryParameter) {
EntityTransaction transaction = null;
EntityManager entityManager = null;
try {
entityManager = this.entityMangerService.getEntityManager();
transaction = entityMangerService.getTransaction(entityManager);
transaction.begin();
final Query query = entityManager.createQuery(queryString);
setQueryParameter(query, namedQueryParameter);
final Object result = query.getSingleResult();
entityMangerService.commitTransaction(transaction);
return result;
} catch (final NoResultException e) {
entityMangerService.rollBackTransaction(transaction);
logger.error("Error" : " + e.getMessage());
return null;
} finally {
entityMangerService.closeEntityManager(entityManager);
}
}
Now Here is the problem when ever line entityManager.createQuery(queryString); execute it throws the exception.
2015-06-05 17:39:46,363 WARN DefaultExceptionHandler:94 - Unhandled exception caught by the Stripes default
exception handler.
java.lang.IllegalArgumentException: org.hibernate.hql.internal.ast.QuerySyntaxException: AuthorizationProperty is
not mapped [SELECT pe.value FROM AuthorizationProperty pe WHERE pe.name=:propertyName AND pe.deleted=0]
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1364)
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1300)
at org.hibernate.ejb.AbstractEntityManagerImpl.createQuery(AbstractEntityManagerImpl.java:294)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.springframework.orm.jpa.ExtendedEntityManagerCreator$ExtendedEntityManagerInvocationHandler.invoke
(ExtendedEntityManagerCreator.java:334)
at com.sun.proxy.$Proxy53.createQuery(Unknown Source)
at com.company.authentication.dao.AuthorizationBaseDAO.getSingleResult(AuthorizationBaseDAO.java:40)
at com.company.authentication.dao.PropertyDAOImlp.getPropertyValue(PropertyDAOImlp.java:22)
at com.company.authentication.services.AuthorizationPropertyService.getPropertyValueByName
(AuthorizationPropertyService.java:19)
at com.company.rd.servlet.JspAuthorizationRestFilter.hasAuthorizationCode
(JspAuthorizationRestFilter.java:105)
at com.company.rd.servlet.AbstractAuthorizationRestFilter.isRequestAuthenticated
(AbstractAuthorizationRestFilter.java:120)
at com.company.rd.servlet.JspAuthorizationRestFilter.doFilter(JspAuthorizationRestFilter.java:84)
I have debugged the code and found entityManagerFactory for persistenceUnit "codeAuthorization" is not initialized. Only "org.jbpm.persistence.jpa.local" is available (verified through eclipse debugger) inside this method.
Note: This JAR is working fine in some other application where web application and JAR using same way to initialize entityMangerFactory [through Persistence.createEntityManagerFactory("")].
Please let me know How can I get "codeAuthorization" entiryManagerFactory
You are using Spring then use Spring, currently you are doing a lot of work to work around Spring and dependency injection and managed transaction. Don't use Persistence.createEntityManagerFactory(""). Just inject the EntityManager where you need it using an EntityManager field annotated with #PersistenceContext and specify the name of the one you want.
Also don't manage the transactions, entity manager yourself, spring does that for you. For this use the right PlatformTransactionManager the JpaTransactionManager and not the DatasourceTransactionManager as that won't work in a JPA environment. (At least not to manage your JPA transactions).
Doing this will really simplify your code and your life.
So basically ditch the service that is doing those nasty things and simple do things like this in your dao.
#Repository
public class YourDao {
#PersistenceContext(name="codeAuthorization")
private EntityManager em;
#Transactional
public Object getSingleResult(final String queryString, final String key,
final NamedQueryParameter namedQueryParameter) {
final Query query = em.createQuery(queryString);
setQueryParameter(query, namedQueryParameter);
return query.getSingleResult();
}
}
In your configuration replace the DatasourceTransactionManager with the JpaTransactionManager and add <tx:annotation-driven />. Then clean your code.
Note: The JpaTransactionManager is perfectly capable of managing plain JDBC transactions if you still need those, ideally you would have a single transaction manager.

Null pointer in Initialising NamedParameterJdbcTemplate bean

If i do the code in controller with
final DataSource dataSource = (DataSource) getApplicationContext().getBean("dataSource", DataSource.class);
final JdbcTemplate jdbcTemplate = new JdbcTemplate(jdbcDataSource);
final NamedParameterJdbcTemplate namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(jdbcTemplate);
This will work fine
If i have to attain the same in Spring application context
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate" autowire="constructor" />
<bean class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate"
id="namedParameterJdbcTemplate">
<constructor-arg ref="jdbcTemplate" />
</bean>
In Hibernate Repository Class
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
public void setNamedParameterJdbcTemplate(NamedParameterJdbcTemplate namedParameterJdbcTemplate) {
this.namedParameterJdbcTemplate = namedParameterJdbcTemplate;
}
This throws error
Is there any way to make the object namedParameterJdbcTemplate working in Repository class
If you've declared your jdbcTemplate in your XML, that is all you should need.
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource" />
</bean>
You do need to set the datasource on the JdbcTemplate though.
Once you autowire the JdbcTemplate into your DAO, you should be able to use that to create a NamedParameterJdbcTemplate.
#Repository
public class DAOImpl implements DAO {
#Autowired
private JdbcTemplate jdbcTemplate;
public void foo() {
NamedParameterJdbcTemplate namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(jdbcTemplate);
....
}
}
You need to actually create an instance of it before you use it.
If you want to use XML to create your instance you can do that too, but you'll need to autowire in the instance that gets created by the XML.
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="namedParamterJdbcTemplate" class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">
<constructor-arg ref="jdbcTemplate"/>
</bean>
Then you should only have to autowire in the NamedParameterJdbcTemplate into your DAO.

Spring JPA - Injecting transaction manager vs injecting entity manager

If I wanted manage transactions programmatically, what is the difference between starting the transaction by injecting a PlatformTransactionManager vs directly injecting EntityMangerFactory/EntityManager and getting transaction from Entitymanager
public class MyDAO {
#PersistenceContext(unitName="test") EntityManager em;
JpaTransactionManager txnManager = null;
public void setTxnManager(JpaTransactionManager mgr) {
txnManager = mgr;
}
public void process(Request request) throws Exception {
TransactionStatus status =
txnManager.getTransaction(new DefaultTransactionDefinition());
try {
em.persist(request);
txnManager.commit(status);
} catch (Exception up) {
txnManager.rollback(status);
throw up;
}
}
As apposed to injecting EntityManager directly
public class MyDAO {
#PersistenceContext(unitName="test")
EntityManager em;
public void process(Request request) throws Exception {
EntityTransaction txn = em.getTransaction();
try {
em.persist(request);
txn.commit();
} catch (Exception up) {
txn.rollback();
throw up;
}
}
where as spring config snippet looks like this
<beans>
<bean id="MyDAO" class="com.xyz.app.dao.MyDAO">
<context:annotation-config />
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerE ntityManagerFactoryBean">
<property name="persistenceUnitName" value="persistence" />
<property name="dataSource" ref="dataSourceProvider" />
<property name="jpaVendorAdapter" ref="jpaVendorAdapter" />
</bean>
<bean id="transactionManagerJpa" class="org.springframework.orm.jpa.JpaTransactionM anager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
</beans>
Transaction managers should not be injected into DAOs, because a DAO has no way to tell whether they're one participant in a larger transaction or not.
I think the transaction manager belongs with the service layer, not the persistence layer. The services know about use cases and units of work. They orchestrate other services, DAOs and model objects to fulfill their use cases.

Resources