How to use spring transaction manager in case of dynamic data source? - java

I have a scenario where datasource are dynamically created using Datasource Factory. So depending upon who accesses the system, DatasourceFacory(custom datasource factory) returns a corresponding datasource.
Now, with this strategy, how do I maintain Spring Transaction? Using a #Transactional annotation needs a fixed datasource with transaction manager configured.
I would like to continue to use #Transactional in a service method and not worry about having to maintain transaction myself.
I would think that I would have to extend some spring class and inject the datasource when the system starts.
I am using Spring and JdbcTemplate in my project. No Hibernate. Any help would be appreciated.

You have to use AbstractRoutingDataSource to link it with your transaction manager, this kind of datasource allows you use different datasources choosing them in runtime.
After that spring handles the transaction properly. You can check this kind of DataSource in the Spring Documentation.
The other option is has different transactionManagers.

For no-xml lovers
#Bean(name = "mysqldatasource1")
public DataSource dataSource() {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName(classname);
dataSource.setUrl(url);
dataSource.setUsername(usernmae);
dataSource.setPassword(password);
return dataSource;
}
#Bean(name = "mySqlJdbcTemplate1")
public JdbcTemplate jdbcTemplate() {
JdbcTemplate jdbcTemplate = new JdbcTemplate();
jdbcTemplate.setDataSource(dataSource());
return jdbcTemplate;
}
#Bean(name="mysqlTransaction1")
#Autowired
DataSourceTransactionManager tm1(#Qualifier ("mysqldatasource") DataSource datasource) {
DataSourceTransactionManager txm = new DataSourceTransactionManager(datasource);
return txm;
}
#Bean(name = "mysqldatasource2")
public DataSource dataSource() {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName(classname);
dataSource.setUrl(url);
dataSource.setUsername(usernmae);
dataSource.setPassword(password);
return dataSource;
}
#Bean(name = "mySqlJdbcTemplate2")
public JdbcTemplate jdbcTemplate() {
JdbcTemplate jdbcTemplate = new JdbcTemplate();
jdbcTemplate.setDataSource(dataSource());
return jdbcTemplate;
}
#Bean(name="mysqlTransaction2")
#Autowired
DataSourceTransactionManager tm1(#Qualifier ("mysqldatasource2") DataSource datasource) {
DataSourceTransactionManager txm = new DataSourceTransactionManager(datasource);
return txm;
}

You can create multiple Transaction Managers with different datasources and then access them in #Transactional annotation as follows:
<bean id="txManager1" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource1"/>
<qualifier value="txManager1"/>
</bean>
Access it as:
#Transactional("txManager1")
Similarly,
<bean id="txManager2" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource2"/>
<qualifier value="txManager2"/>
</bean>
Access it as:
#Transactional("txManager2")
As there can be only one annotation-driven TxManager, do not forget to remove this:
<tx:annotation-driven transaction-manager="transactionManager" />

Related

Create DataSource lazily and transaction manager

I have a DB access module that interacts with multiple types of data sources.
Some higher modules use the DB module without interacting with JDBC data source. Therefore, I would like the data source to be defined as lazy. However, I also define a transaction manager which I can't define as lazy since it is not a direct dependency in my code. Since the transaction manager depends on the data source, the latter is always created.
#Configuration
#EnableTransactionManagement
public class SpringConfig {
#Bean(destroyMethod = "close")
#Lazy
#Autowired
public DataSource dataSource() throws Exception {
//create data source
}
#Bean
#Lazy
#Autowired
public PlatformTransactionManager txManager() throws Exception {
DataSourceTransactionManager txManager = new DataSourceTransactionManager(dataSource());
return txManager;
}
}
#Lazy
#Repository("FundRepository")
public class MyRepository {
private JdbcTemplate jdbcTemplate;
#Autowired
public void setDataSource(DataSource dataSource) {
jdbcTemplate = new JdbcTemplate(dataSource);
}
}
Any idea how to make this work?

Spring + Jboss7 #Transactional Not working

I am upgrading my JBoss server from 5 to 7 and am now incorporating Spring 4. I am having some trouble using Spring's #Transactional annotation. It does not appear to be working. I am also trying to use a java based configuration file instead of an xml file (I believe I can get away without using any xml, but correct me if I am wrong). The problem is that nothing is being saved in my db, leading me to believe that the #Transactional isn't working. Here is my config file:
#Configuration
#ComponentScan
#EnableTransactionManagement
public class SpringBeanConfiguration {
#Bean
public FirstTestBean firstTestBean() {
return new FirstTestBean();
}
#Bean
public TestService testService() {
return new TestServiceImpl();
}
#Bean
public SomethingDAO somethingDAO(){
return new SomethingDAOImpl();
}
#Bean
public GeneralDAO generalDAO(){
return new GeneralDAOImpl();
}
Here is a test class with the #Transactional method:
//#RequestScoped
#ManagedBean(name="firstTestBean")
#Component
public class FirstTestBean {
private EntityManager em;
private EntityManagerFactory emf;
#Transactional
public String transactionalTest() {
//ApplicationContext context = new AnnotationConfigApplicationContext(SpringBeanConfiguration.class);
Something something = new Something();
getEntityManager().persist(something);
return "dkljs";
}
public EntityManager getEntityManager() {
if (em == null) {
emf = Persistence.createEntityManagerFactory("xxx");
em = emf.createEntityManager();
}
return em;
}
I am also using Hibernate 4, which is compatible with Spring. I commented out the ApplicationContext because I have that running separately on the start up of JBoss. I was using that earlier to access a bean, but I have since simplified things in order to get the #Transactional working and thus do not need it here. The #ComponentScan does not need parameters because these classes are in the same package.
Any help would be greatly appreciated. Thanks!
Updates
I've made some of the changes suggested. Things appear to be moving in the right direction.
Here are my updated files:
#ManagedBean(name="firstTestBean")
public class FirstTestBean {
public String getTestString() {
ApplicationContext context = new AnnotationConfigApplicationContext(SpringBeanConfiguration.class);
TestService testService = context.getBean(TestService.class);
testService.transactionalTest();
return "adfs";
}
public String test() {
return "firstTestBean works";
}
}
Note - some of these classes will be run outside of the Jboss application server as standalone applications, so for that reason, I am staying away from FacesContext when instantiating the TestService in FirstTestBean, as Spring beans work in standalone, but FacesContext beans do not.
#Component
#Transactional
public class TestServiceImpl implements TestService {
public GeneralDAO generalDAO;
//#Autowired
private EntityManager em;
//#Autowired
private EntityManagerFactory emf;
public TestServiceImpl(){}
public String transactionTest() {
Something something = new Something();
getEntityManager().persist(something);
return "dkljs";
}
#autowired on the EntityManager and EntityManagerFactory did not work - I received an error saying No Qualified Bean of type EntityManager when it was annotated with the #autowired like suggested.
#Configuration
#ComponentScan
#EnableTransactionManagement
public class SpringBeanConfiguration implements TransactionManagementConfigurer {
#Override
public PlatformTransactionManager annotationDrivenTransactionManager() {
String hibernatePropsFilePath = "/[path]/hibernate.cfg.xml";
File hibernatePropsFile = new File(hibernatePropsFilePath);
org.hibernate.cfg.Configuration cfg = new org.hibernate.cfg.Configuration().configure(hibernatePropsFile);
SessionFactory sessionFactory = cfg.buildSessionFactory();
HibernateTransactionManager txManager = new HibernateTransactionManager(sessionFactory);
txManager.setNestedTransactionAllowed(true);
return txManager;
}
}
The error I am getting now is:Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.context.event.internalEventListenerProcessor': Initialization of bean failed
; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration':
Injection of autowired dependencies failed; nested exception is org.hibernate.service.UnknownUnwrapTypeException: Cannot unwrap to requested type [javax.sql.DataSource]
I take it this means that the #Transactional is at least being recognized, but I'm having some issues getting this to work. Any further advice would be greatly appreciated.
More Updates
Found this article: http://www.baeldung.com/the-persistence-layer-with-spring-and-jpa#javaconfig
and followed it. My new config file:
#Configuration
#ComponentScan
#EnableTransactionManagement
public class SpringBeanConfiguration { //implements TransactionManagementConfigurer {
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource());
em.setPackagesToScan(new String[] { "xxx.xxx.xxx" });
JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
// em.setJpaProperties(additionalProperties());
return em;
}
#Bean
public DataSource dataSource(){
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/jboss_test");
dataSource.setUsername( "root" );
dataSource.setPassword( "root" );
return dataSource;
}
#Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory emf){
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(emf);
return transactionManager;
}
#Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation(){
return new PersistenceExceptionTranslationPostProcessor();
}
Good news is that I am no longer having deployment issues. The logging also suggests that my changes are having some effect on the server. Unfortunately, nothing is being saved, so it is still not quite working.
The first thing that draws my attention is the use of #Transactional and #Component on a ManagedBean.
JSF and Spring are definately made to work together but i never saw them used this way on many projects i was working on.
Im not sure if that is the cause of your problems but please consider changing that.
I would do like this:
a) Define some service layer where you would wrap your calls with transactions and inject the JPA classes:
#Component
#Transactional
class Service{
#Autowired
private EntityManager em;
#Autowired
private EntityManagerFactory emf;
public String serviceMethod(..){ .. }
}
b) Inject that to Jsf's ManagedBean while removing the unnecessary annotations:
#ManagedBean(name="firstTestBean")
public class FirstTestBean {
#ManagedProperty("#{service}")
private Service service;
public String transactionalTest() {
return service.serviceMethod();
}
}
Ok, so I finally got it (Check out my OP for all updates that led me to this point).
Here's my final config file:
#Configuration
#ComponentScan
#EnableTransactionManagement
public class SpringBeanConfiguration {
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setPersistenceUnitName("myPersistenceContext");
em.setDataSource(dataSource());
em.setPackagesToScan(new String[] { "xxx.xxx.xxx" });
JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
// em.setJpaProperties(additionalProperties());
return em;
}
#Bean
public DataSource dataSource(){
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/jboss_test");
dataSource.setUsername( "root" );
dataSource.setPassword( "root" );
return dataSource;
}
#Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory emf){
JtaTransactionManager transactionManager = new JtaTransactionManager();
// transactionManager.setEntityManagerFactory(emf);
return transactionManager;
}
#Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation(){
return new PersistenceExceptionTranslationPostProcessor();
}
So first, I added in
em.setPersistenceUnitName("myPersistenceContext");
This gave me the error:
Spring IllegalStateException: A JTA EntityManager cannot use getTransaction()
From here, I did some research (Spring IllegalStateException: A JTA EntityManager cannot use getTransaction())
and changed
JpaTransactionManager transactionManager = new JpaTransactionManager();
to
JtaTransactionManager transactionManager = new JtaTransactionManager();
Also, my persistence.xml file is:
<persistence-unit name="myPersistenceContext">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<jta-data-source>java:jboss/datasources/myDS</jta-data-source>
<class>xxx.xxx.xxx.Something</class>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties>
<property name="hibernate.cache.use_second_level_cache" value="true" />
<property name="hibernate.id.new_generator_mappings" value="false"/>
<property name="hibernate.classloading.use_current_tccl_as_parent" value="false"/>
<property name="hibernate.transaction.jta.platform" value="org.hibernate.service.jta.platform.internal.JBossAppServerJtaPlatform" />
<property name="hibernate.show_sql" value="true" />
<property name="hibernate.archive.autodetection" value="class, hbm" />
</properties>
</persistence-unit>
Thanks to Maciej Kowalski for your help - you pushed me in the right direction.

#Transactional doesn't open Hibernate Transaction

I declared Spring Beans in my beans.xml:
<context:annotation-config />
<context:component-scan base-package="com.pack"/>
<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="transactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
<property name="dataSource" ref="dataSource"></property>
</bean>
dataSource and sessionFactory beans:
#Bean(name = "dataSource")
public DriverManagerDataSource dataSource() {
DriverManagerDataSource ds = new DriverManagerDataSource();
ds.setUsername(userName);
ds.setPassword(password);
ds.setDriverClassName(driverName);
ds.setUrl(url);
return ds;
}
#Bean(name = "sessionFactory")
public LocalSessionFactoryBean localSessionFactoryBean() {
LocalSessionFactoryBean factory = new LocalSessionFactoryBean();
factory.setDataSource(dataSourceConfiguration.dataSource());
Properties props = new Properties();
props.put("hibernate.dialect", "org.hibernate.dialect.MySQLDialect");
props.put("hibernate.hbm2ddl.auto", "update");
props.put("hibernate.current_session_context_class", "thread");
factory.setHibernateProperties(props);
factory.setMappingResources("com/pack/Item.hbm.xml");
return factory;
}
If I use sessionFactory and dataSource beans separately they work well. A also have DAO class:
#Repository(value = "itemDaoHibernateImpl")
public class ItemDaoHibernateImpl implements ItemDao {
#Resource(name = "sessionFactory")
private SessionFactory factory;
public void setFactory(SessionFactory factory) {
this.factory = factory;
}
public Session session() {
return factory.getCurrentSession();
}
#Override
public void create(Item item) {
session().save(item);
}
I don't open the sessions because I want to force Spring to do this. I have Service class with method:
#Override
#Transactional
public void create(Item item) {
dao.create(item);
}
When I call it, I have the exception:
org.hibernate.HibernateException: save is not valid without active transaction
I've done like this tutorial tells. Where is my mistake?
Try to remove props.put("hibernate.current_session_context_class", "thread") from your sessionFactory configuration. When you are using Spring managed transactions, you don't need it. Let me know if that works.
When I have come across this before it is due to whether Spring is using CGLib or Javassist to augment your class to provide transactionality. if I remember correctly if you only have Javassist in then the class that Spring needs to create the proxy on in order to implement the Transactional annotation must implement an interface.

Spring 4, JBoss 7, #Configuration Bean equivalent XML - Spring Transactions

It seem like EntityManager is NOT flushing out the changes to the database. What makes this problem harder is that there is no exception thrown. I am using declarative transactions to define my transaction boundaries with the following flow:
SignUpController --> #Transactional: Tx.Start --> AccountServiceImpl.createAccount --> AccountDAOImpl.createAccount --> Tx.Commit --> SignUpController
From my investigation it seems like:
1.Spring is properly defining the Transaction Boundaries using #Transactional. Hence Transaction are being created
2.Also please Note the I am using the SharedEntityManagerBean, which means I CANNOT explicitly define my transaction boundaries e.g:
em.getTransaction().begin();
em.persist(entity);
em.getTransaction().commit();
The above code results in the following exception: Not allowed to create transaction on shared EntityManager - use Spring transactions or EJB CMT instead
Below is the relevant code and configuration. Any assistance will be greatly appreciated. Thanks.
MainConfig.java:
#Configuration
#ComponentScan(basePackages="com",excludeFilters= {#Filter(Configuration.class)})
#EnableTransactionManagement
public class MainConfig {
#Bean
public DataSource dataSource() {
DataSource ds = (DataSource)getFromInitialContext("java:jboss/datasources/PrimaryDB");
return ds;
}
#Bean
public PlatformTransactionManager transactionManager() {
return new DataSourceTransactionManager(dataSource());
}
#Bean
public EntityManagerFactory entityManagerFactory() {
EntityManagerFactory emf = (EntityManagerFactory) getFromInitialContext("java:jboss/entityManagerFactory");
return emf;
}
private Object getFromInitialContext(String jndiValue) {
Context ctx = null;
Object object = null;
try {
ctx = new InitialContext();
object = ctx.lookup(jndiValue);
} catch (NamingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return object;
}
}
persistance.xml:
<persistence-unit name="PrimaryDB" transaction-type="JTA">
<jta-data-source>java:jboss/datasources/PrimaryDB</jta-data-source>
<class>com.domain.entities.Account</class>
<properties>
<!-- Bind entity manager factory to JNDI at java:jboss/myEntityManagerFactory -->
<property name="jboss.entity.manager.factory.jndi.name"
value="java:jboss/entityManagerFactory" />
<!-- Properties for Hibernate -->
<property name="hibernate.hbm2ddl.auto" value="create-drop" />
<property name="hibernate.show_sql" value="true" />
<property name="hibernate.use_sql_comments" value="true" />
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect" />
</properties>
</persistence-unit>
AccountServiceImpl:
#Service
public class AccountServiceImpl implements AccountService {
#Inject
AccountDAO accountDAOImpl;
#Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
public Account createAccount(SignupForm form, BindingResult formBinding) {
TransactionDebugUtil.transactionRequired("AccountServiceImpl.createAccount");
Account account =
new Account(form.getUsername(), form.getPassword(),form.getFirstName(), form.getLastName());
try {
accountDAOImpl.createAccount(account);
} catch (Exception ex) {
formBinding.rejectValue("username", "user.duplicateUsername",
"already in use: " + ex.getMessage());
return null;
}
return account;
}
}
AccountDAOImpl.java:
#Repository
public class AccountDAOImpl extends GenericJpaDAO<Account> implements AccountDAO {
#Override
#PersistenceContext
public void setEntityManager(EntityManager entityManager) {
this.entityManager = entityManager;
entityManager.setFlushMode(FlushModeType.COMMIT);
}
#Override
public void createAccount(Account account) throws UsernameAlreadyInUseException {
entityManager.persist(account);
}
Hi the confusion was caused due to using annotations for configuring the transaction manager rather then XML.
Typical XML Configuration for SpringTransaction:
1.
<tx:annotation-driven />
^It scans all beans in the application context and creates AOP interceptor for those which are annotated.This is done via the SpringTransactionAnnotationParser, which is used by TransactionInterceptor.
#EnableTransactionManagement
^This is the equivalent configuration bean annotation to allow transactional support
2
. JTA datasource is defined in the persistence.xml. JBoss 7 automatically creates a JTA Transaction Manager and binds it to the following JNDI Location: java:/TransactionManager by default. This Transaction Manager is automatically discovered in spring with the following xml configuration:
<tx:jta-transaction-manager />
The equivalent annotation configuration for this is:
#Bean
public PlatformTransactionManager transactionManager() {
JtaTransactionManager txManager = new JtaTransactionManager();
return txManager;
}
This resolved my issue. Thanks #geoand for your help.
this kind of behaviour usually happens when we dont have #transactional
on the persistent method.
so add #Transactional on top of method like below
#Override
#Transactional
public void createAccount(Account account) throws UsernameAlreadyInUseException {
entityManager.persist(account);
}
You are not handling transaction management correctly. Replace your transaction manager bean with:
#Bean
public PlatformTransactionManager transactionManager() {
final JpaTransactionManager txManager = new JpaTransactionManager();
txManager.setEntityManagerFactory(entityManagerFactory());
return txManager;
}

Transaction management with Spring Batch

I am discovering actually Spring and I am able to setup some jobs. Now, I would like to save my imported datas in a database using Hibernate/JPA and I keep getting this error :
14:46:43.500 [main] ERROR o.s.b.core.step.AbstractStep - Encountered an error executing the step javax.persistence.TransactionRequiredException: no transaction is in progress
I see that the problem is with the transaction. Here is my spring java config for the entityManager and the transactionManager :
#Configuration
public class PersistenceSpringConfig implements EnvironmentAware
{
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() throws Exception
{
// Initializes the entity manager
LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
factoryBean.setPersistenceUnitName(PERSISTENCE_UNIT_NAME);
factoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
factoryBean.setDataSource(dataSource());
// Scans the database model
factoryBean.setPackagesToScan(EntiteJuridiqueJPA.class.getPackage().getName());
// Defines the Hibernate properties
Properties jpaProperties = new Properties();
jpaProperties.setProperty("hibernate.show_sql", "false");
jpaProperties.setProperty("hibernate.format_sql", "false");
String connectionURL = "jdbc:h2:file:" + getDatabaseLocation();
jpaProperties.setProperty("hibernate.connection.url", connectionURL);
jpaProperties.setProperty("hibernate.connection.username", "sa");
jpaProperties.setProperty("hibernate.connection.driver_class", "org.h2.Driver");
jpaProperties.setProperty("hibernate.dialect", H2Dialect.class.getName());
jpaProperties.setProperty("hibernate.hbm2ddl.auto", "create");
jpaProperties.setProperty("hibernate.hbm2ddl.import_files_sql_extractor", "org.hibernate.tool.hbm2ddl.MultipleLinesSqlCommandExtractor");
jpaProperties.setProperty("hibernate.hbm2ddl.import_files",
"org/springframework/batch/core/schema-drop-h2.sql,org/springframework/batch/core/schema-h2.sql");
factoryBean.setJpaProperties(jpaProperties);
return factoryBean;
}
#Bean
public PlatformTransactionManager transactionManager2() throws Exception
{
EntityManagerFactory object = entityManagerFactory().getObject();
JpaTransactionManager jpaTransactionManager = new JpaTransactionManager(object);
return jpaTransactionManager;
}
I am using the JpaItemWriter to store the datas in the database :
#Bean
public ItemWriter<EntiteJuridiqueJPA> writer()
{
JpaItemWriter<EntiteJuridiqueJPA> writer = new JpaItemWriter<EntiteJuridiqueJPA>();
writer.setEntityManagerFactory(entityManagerFactory.getObject());
return writer;
}
This is the code that causes the exception : javax.persistence.TransactionRequiredException: no transaction is in progress
Any idea to how to solve this problem?
[Edit] I am putting also the Job definition and the step definition. All my Spring configuration is written in Java.
#Configuration
#EnableBatchProcessing
#Import(PersistenceSpringConfig.class)
public class BatchSpringConfig
{
#Autowired
private JobBuilderFactory jobBuilders;
#Autowired
private StepBuilderFactory stepBuilders;
#Autowired
private DataSource dataSource;
#Autowired
private LocalContainerEntityManagerFactoryBean entityManagerFactory;
#Bean
public Step step()
{
return stepBuilders.get("step").<EntiteJuridique, EntiteJuridiqueJPA> chunk(5).reader(cvsReader(null))
.processor(processor()).writer(writer()).listener(processListener()).build();
}
#Bean
#StepScope
public FlatFileItemReader<EntiteJuridique> cvsReader(#Value("#{jobParameters[input]}") String input)
{
FlatFileItemReader<EntiteJuridique> flatFileReader = new FlatFileItemReader<EntiteJuridique>();
flatFileReader.setLineMapper(lineMapper());
flatFileReader.setResource(new ClassPathResource(input));
return flatFileReader;
}
#Bean
public LineMapper<EntiteJuridique> lineMapper()
{
DefaultLineMapper<EntiteJuridique> lineMapper = new DefaultLineMapper<EntiteJuridique>();
DelimitedLineTokenizer lineTokenizer = new DelimitedLineTokenizer();
lineTokenizer.setDelimiter(";");
lineTokenizer.setNames(new String[] { "MEGA_ENTITE", "PORTEFEUILLE", "MEGA_ENTITE", "Libellé" });
BeanWrapperFieldSetMapper<EntiteJuridique> fieldSetMapper = new BeanWrapperFieldSetMapper<EntiteJuridique>();
fieldSetMapper.setTargetType(EntiteJuridique.class);
lineMapper.setLineTokenizer(lineTokenizer);
lineMapper.setFieldSetMapper(fieldSetMapper);
return lineMapper;
}
#Bean
public Job dataInitializer()
{
return jobBuilders.get("dataInitializer").listener(protocolListener()).start(step()).build();
}
#Bean
public ItemProcessor<EntiteJuridique, EntiteJuridiqueJPA> processor()
{
return new EntiteJuridiqueProcessor();
}
#Bean
public ItemWriter<EntiteJuridiqueJPA> writer()
{
JpaItemWriter<EntiteJuridiqueJPA> writer = new JpaItemWriter<EntiteJuridiqueJPA>();
writer.setEntityManagerFactory(entityManagerFactory.getObject());
return writer;
// return new EntiteJuridiqueWriter();
}
#Bean
public ProtocolListener protocolListener()
{
return new ProtocolListener();
}
#Bean
public CSVProcessListener processListener()
{
return new CSVProcessListener();
}
#Bean
public PlatformTransactionManager transactionManager2() throws Exception
{
EntityManagerFactory object = entityManagerFactory.getObject();
JpaTransactionManager jpaTransactionManager = new JpaTransactionManager(object);
return jpaTransactionManager;
}
[EDIT] I am still stuck with this problem. I have followed the suggestions of #Sean Patrick Floyd and #bellabax by setting a transaction manager for the stepBuilders, but I still get the same exception. I have tested my entityManager independtly of spring-batch and I am able to store any data in the database.
But, when using the same entity manager with spring batch, I have this exception.
Anyone can give more insights how transactions are managed within spring batch? Thx for your help?
The problem is that you are creating a second transaction manager (transactionManager2), but Spring Batch is using another transaction manager for starting transactions. If you use #EnableBatchProcessing, Spring Batch automatically registers a transaction manager to use for its transactions, and your JpaTransactionManager never gets used.
If you want to change the transaction manager that Spring Batch uses for transactions, you have to implement the interface BatchConfigurer. Take a look at this example: https://github.com/codecentric/spring-batch-javaconfig/blob/master/src/main/java/de/codecentric/batch/configuration/WebsphereInfrastructureConfiguration.java.
Here I am switching the transaction manager to a WebspherUowTransactionManager, and in the same way you can switch the transaction manager to some other transaction manager.
Here's the link to the blog post explaining it: http://blog.codecentric.de/en/2013/06/spring-batch-2-2-javaconfig-part-3-profiles-and-environments/
You need to explicitly reference your Transaction Manager in your step definition:
<job id="sampleJob" job-repository="jobRepository">
<step id="step1">
<tasklet transaction-manager="transactionManager">
<chunk reader="itemReader" writer="itemWriter" commit-interval="10"/>
</tasklet>
</step>
</job>
See: 5.1.1. Configuring a Step
Ah, seeing that you use JavaConfig, you need to assign the transaction manager to the TaskletStepBuilder using builder.transactionManager(transactionManager) (inherited from StepBuilderHelper)

Categories