I'm using Spring 4.0.5, Mysql 5.6.19 and BoneCP 0.8.0
My issue is that, in one transaction defined in the application, MySql is commiting each insert or update, so it does not making a transaction.
First of all, I have read some questions like mine, but setting this in the Spring datasource does not work for me:
<property name="defaultAutoCommit" value="false" />
My classes are these:
DBWriter.java
private DataSourceTransactionManager txManager; // Injected in Spring Beans XML
private IMyDAO writerDAO;
public void saveBeans(DataContainer targetData) throws Throwable{
try {
JdbcTemplate templateTransaction = new JdbcTemplate(txManager.getDataSource());
MyTx newTx = new MyTx(targetData, templateTransaction, writerDAO);
TransactionTemplate txTemplate = new TransactionTemplate(txManager);
txTemplate.execute(newTx);
} catch (Throwable e) {
logger.error("Error saving into DB", e);
throw e;
}
}
MyTx.java
public class MyTx extends TransactionCallbackWithoutResult {
private IMyDAO writerDAO;
private DataContainer finalData;
private JdbcTemplate txTemplate;
public MyTx(DataContainer newData, JdbcTemplate newTxTemplate, IMyDAO writerDAO){
this.finalData = newData;
this.txTemplate = newTxTemplate;
this.writerDAO = writerDAO;
}
#Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
writerDAO.saveTargetBean(newData, txTemplate);
}
}
MyDAO.java
private void saveTargetBean(...) {
jdbcTemplate.update("INSERT...", ...); // First
jdbcTemplate.update("INSERT...", ...); // Second
jdbcTemplate.update("INSERT...", ...); // Third
}
My issue is, when debugging step by step or forcing failures, First, Second, Thirs is committing immediately to database, without transactional behaviour.
Is there anything wrong in my code or in my approach? Should I put the INSERTs sentences directly in doInTransactionWithoutResult method? Should I do this in another way, so the three inserts were done in a transactional way?
Any help would be very very appreciated.
Regards
As Martin suggest, declarative transaction with #Transactional are better for this, and works for me. So, thank you
These two links were very helpful for me in this topic:
Propagation types
#Transactional example
Related
I have a Spring Batch Task running on our cloud platform that will launch with the provided command line parameters, and then skip over the execution of the first Step with the following error:
[OUT] The job execution id 992 was run within the task execution 1325
[OUT] Step already complete or not restartable, so no action to execute:
StepExecution: id=1071, version=3, name=OFileStep, status=COMPLETED, exitStatus=COMPLETED, readCount=0, filterCount=0, writeCount=0 readSkipCount=0,
writeSkipCount=0, processSkipCount=0, commitCount=1, rollbackCount=0, exitDescription=
I have investigated the metadata tables in the MySQL instance that Spring Batch uses to find that the JOB_INSTANCE_ID is the same between multiple executions, when it should increment by 1 each time.
The #Bean that I have defined for the Job Configuration is:
#Bean
public Job job() {
return jobBuilderFactory.get(OTaskConstants.JOB_NAME)
.listener(listener())
.incrementer(new RunIdIncrementer())
.start(dataTransferTaskStep())
.next(controlMTaskStep())
.build();
}
Is anyone aware of what could be causing this behavior?
Below line clearly says it all.
Step already complete or not restartable, so no action to execute:
Meaning the step/job already complete and can not be restarted. This is the behavior of Spring Batch. In order to by pass this we need to pass an unique argument.
In your case i see you already have RunIdIncrementer. Now question is why it is not working.
Can you see BATCH_JOB_PARMS table to see what arguments are getting passed to the job? May be you are missing something.
You can also use SimpleIncrementor. See below code for explanation.
https://docs.spring.io/spring-batch/docs/current/reference/html/index-single.html#JobParametersIncrementer
Remove #Bean annotation on Job.
It causes the Job to be launched with no parameters every time you launch/start application as spring tries to load the bean and which in-turn launches the batch job.
Remove the annotation and use spring scheduler to schedule the jobs.
I had the same issue. Below code helped me resolve it. By adding params in job launcher a new job_instance_id is created for every run.
#SpringBootApplication
public class App implements CommandLineRunner {
#Autowired
JobLauncher jobLauncher;
#Autowired
Job job;
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
#Override
public void run(String... args) throws Exception {
JobParameters params = new JobParametersBuilder()
.addString("JobID", String.valueOf(System.currentTimeMillis()))
.toJobParameters();
jobLauncher.run(job, params);
}
}
Solution
Refer error message above “If you want to run this job again, change the parameters.” The formula is JobInstance = JobParameters + Job. If you do not have any parameters for JobParameters, just pass a current time as parameter to create a new JobInstance. For example,
CustomJobLauncher.java
//...
#Component
public class CustomJobLauncher {
#Autowired
JobLauncher jobLauncher;
#Autowired
Job job;
public void run() {
try {
JobParameters jobParameters =
new JobParametersBuilder()
.addLong("time",System.currentTimeMillis()).toJobParameters();
JobExecution execution = jobLauncher.run(job, jobParameters);
System.out.println("Exit Status : " + execution.getStatus());
} catch (Exception e) {
e.printStackTrace();
}
}
}
Source : https://mkyong.com/spring-batch/spring-batch-a-job-instance-already-exists-and-is-complete-for-parameters/
I want to insert/update records in two tables within one transaction in same method.
first insert into table1 and if that is successful then update the table2 and then if both are successful then only commit otherwise dont commit (in both table)
my code is like below:
#Transactional
public void persistTable1AndTable2() {
persistTable1(List<Table1Entity>);
updateTable2(List<Table2Entity>);
}
however when I test the code, and if after persistTable1 line I terminate the process, it still insert the record in first table.
so it doesnt consider this as block transaction.
I am using mysql.
Just to add:
above both table1 and table2 uses separate transactionmanager and entityManagerFactory
Can someone please help
[EDIT]
Code to configure JTA using atomikos API.
//Atomikos related JTA configuration
#Bean(name="userTransactionServiceImp")
public UserTransactionServiceImp userTransactionServiceImp()
{
Properties properties = new Properties();
properties.setProperty("com.atomikos.icatch.max_timeout", "600000");
UserTransactionServiceImp userTransactionServiceImp = new UserTransactionServiceImp(properties);
return userTransactionServiceImp;
}
#Bean
#DependsOn("userTransactionServiceImp")
public UserTransaction userTransaction()
{
UserTransactionImp userTransactionImp = new UserTransactionImp();
return userTransactionImp;
}
#Bean
#DependsOn("userTransactionServiceImp")
public TransactionManager userTransactionManager()
{
UserTransactionManager userTransactionManager = new UserTransactionManager();
userTransactionManager.setStartupTransactionService(false);
userTransactionManager.setForceShutdown(false);
return userTransactionManager;
}
#Bean
public PlatformTransactionManager transactionManager()
{
return new JtaTransactionManager(userTransaction(), userTransactionManager());
}
and configure jparepo I am having below config class:
#EnableJpaRepositories(basePackages = {"daemon.repository.entities"},
transactionManagerRef="transactionManager", entityManagerFactoryRef = "eventsEntityMF")
I found an example of something similar here
Have a quick check http://fabiomaffioletti.me/blog/2014/04/15/distributed-transactions-multiple-databases-spring-boot-spring-data-jpa-atomikos/
See if you are missing some configuration
I see the following exception message in my IDE when I try to get lazy initialized entity (I can't find where it is stored in the proxy entity so I can't provide the whole stack trace for this exception):
Method threw 'org.hibernate.LazyInitializationException' exception. Cannot evaluate com.epam.spring.core.domain.UserAccount_$$_jvste6b_4.toString()
Here is a stack trace I get right after I try to access a field of the lazy initialized entity I want to use:
org.hibernate.LazyInitializationException: could not initialize proxy - no Session
at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:165)
at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:286)
at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:185)
at com.epam.spring.core.domain.UserAccount_$$_jvstfc9_4.getMoney(UserAccount_$$_jvstfc9_4.java)
at com.epam.spring.core.web.rest.controller.BookingController.refill(BookingController.java:128)
I'm using Spring Data, configured JpaTransactionManager, database is MySql, ORM provider is Hibernate 4. Annotation #EnableTransactionManagement is on, #Transactional was put everywhere I could imagine but nothing works.
Here is a relation:
#Entity
public class User extends DomainObject implements Serializable {
..
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinColumn(name = "user_fk")
private UserAccount userAccount;
..
#Entity
public class UserAccount extends DomainObject {
..
#OneToOne(mappedBy = "userAccount")
private User user;
..
.. a piece of configuration:
#Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(env.getRequiredProperty(PROP_NAME_DATABASE_DRIVER));
dataSource.setUrl(env.getRequiredProperty(PROP_NAME_DATABASE_URL));
dataSource.setUsername(env.getRequiredProperty(PROP_NAME_DATABASE_USERNAME));
dataSource.setPassword(env.getRequiredProperty(PROP_NAME_DATABASE_PASSWORD));
return dataSource;
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setDataSource(dataSource());
entityManagerFactoryBean.setPersistenceProviderClass(HibernatePersistenceProvider.class);
entityManagerFactoryBean.setPackagesToScan(env.getRequiredProperty(PROP_ENTITYMANAGER_PACKAGES_TO_SCAN));
entityManagerFactoryBean.setJpaProperties(getHibernateProperties());
return entityManagerFactoryBean;
}
#Bean
public JpaTransactionManager transactionManager(#Autowired DataSource dataSource,
#Autowired EntityManagerFactory entityManagerFactory) {
JpaTransactionManager jpaTransactionManager = new JpaTransactionManager();
jpaTransactionManager.setEntityManagerFactory(entityManagerFactory);
jpaTransactionManager.setDataSource(dataSource);
return jpaTransactionManager;
}
.. and this is how I want to retrieve UserAccount:
#RequestMapping(...)
#Transactional()
public void refill(#RequestParam Long userId, #RequestParam Long amount) {
User user = userService.getById(userId);
UserAccount userAccount = user.getUserAccount();
userAccount.setMoney(userAccount.getMoney() + amount);
}
Hibernate version is 4.3.8.Final, Spring Data 1.3.4.RELEASE and MySql connector 5.1.29.
Please, ask me if something else is needed. Thank you in advance!
Firstly, you should understand that the root of the problem is not a transaction. We have a transaction and a persistent context (session). With #Transactional annotation Spring creates a transaction and opens persistent context. After method is invoked a persistent context becomes closed.
When you call a user.getUserAccount() you have a proxy class that wraps UserAccount (if you don't load UserAccount with User). So when a persistent context is closed, you have a LazyInitializationException during call of any method of UserAccount, for example user.getUserAccount().toString().
#Transactional working only on the userService level, in your case. To get #Transactional work, it is not enough to put the #Transactional annotation on a method. You need to get an object of a class with the method from a Spring Context. So to update money you can use another service method, for example updateMoney(userId, amount).
If you want to use #Transactional on the controller method you need to get a controller from the Spring Context. And Spring should understand, that it should wrap every #Transactional method with a special method to open and close a persistent context. Other way is to use Session Per Request Anti pattern. You will need to add a special HTTP filter.
https://vladmihalcea.com/the-open-session-in-view-anti-pattern/
As #v.ladynev briefly explained, your issue was that you wanted to initialize a lazy relation outside of the persistence context.
I wrote an article about this, you might find it helpful: https://arnoldgalovics.com/lazyinitializationexception-demystified/
For quick solutions despite of performance issues use #transactional in your service
Sample:
#Transactional
public TPage<ProjectDto> getAllPageable(Pageable pageable) {
Page<Project> data = projectRepository.findAll(pageable);
TPage<ProjectDto> response = new TPage<>();
response.setStat(data, Arrays.asList(modelMapper.map(data.getContent(), ProjectDto[].class)));
return response;
}
it will get user details for project manager in the second query.
For more advanced solution, you should read the blog post in the #galovics answer.
I used below to fix
sessionFactory.getObject().getCurrentSession()
Create query and get required object
I was also facing the same error while running my springBoot App.
What is the real issue here?
Please check have you autowired the repository at controller level
If first step is correct then please check where ever you have autowired your JPA repository , it should be a part of #Transactional code.
If not please add #Transactional annotation.It will solve your issue.
I was getting this error:
Method threw 'org.hibernate.LazyInitializationException' exception.
This is because currently there is no session present. Hibernate opens a session and closes it, but for "lazy = true" or "fetch = FetchType.LAZY" such fields are populated by proxies. When you try to find the value for such a field, it will attempt to go to the database using the active session to retrieve the data. If no such session can be found, you get this exception.
You can fix it using "lazy=false" or check whether you have used #Transcational properly (try to use this in your service layer than your data access layer), you can also use
#Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
OR
#Transactional
I have an Entity Manager in my EJB
#PersistenceContext(unitName = "cnsbEntities")
private EntityManager em;
I populate an object and then I commit it in my DB, but if I have an exception, for duplicate ID, I can't catch it and I don't know why.
try{
em.merge(boelLog);
} catch (Exception e){
System.out.println("Generic Exception");
}
JPA uses transactions to send entity modifications to the database. You can specify those transactions manually through Bean Managed Transactions (BMT) or let the application server do it for you (Container Managed Transactions; the default).
So, you need to catch the exception at the end of the transaction, and not after calling merge() or persist() methods of EntityManager class. In your case, probably the transaction will end when you return from the last EJB object.
Example for Container Managed Transactions (the default):
#Stateless
public class OneEjbClass {
#Inject
private MyPersistenceEJB persistenceEJB;
public void someMethod() {
try {
persistenceEJB.persistAnEntity();
} catch(PersistenceException e) {
// here you can catch persistence exceptions!
}
}
}
...
#Stateless
public class MyPersistenceEJB {
// this annotation forces application server to create a new
// transaction when calling this method, and to commit all
// modifications at the end of it!
#TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public void persistAnEntity() {
// merge stuff with EntityManager
}
}
It's possible to specify when a method call (or any method call of an object of an EJB) must, can or must not create a new transaction. This is done through the #TransactionAttribute annotation. By default, every method of an EJB is configured as REQUIRED (same as specifying #TransactionAttribute(TransactionAttributeType.REQUIRED)), that tells the application to reuse (continue) the transaction that is active when that method was called, and create a new transaction if needed.
More about transactions here: http://docs.oracle.com/javaee/7/tutorial/doc/transactions.htm#BNCIH
More about JPA and JTA here: http://en.wikibooks.org/wiki/Java_Persistence/Transactions
I'm writing unit tests in JUnit, but have not been able to successfully cover a branch of a particular method that catches a SQLException and returns a null object.
This is the class I'm testing:
#Component
public class UnitOfMeasureRowMapper implements RowMapper<UnitOfMeasure> {
public UnitOfMeasure mapRow(final ResultSet resultSet, final int rowNumber) throws SQLException {
UnitOfMeasure unitOfMeasure = new UnitOfMeasure();
try {
unitOfMeasure.setUnitOfMeasureId(resultSet.getInt("UNITOFMEASUREID"));
unitOfMeasure.setOwnerUserId(resultSet.getInt("USERID"));
unitOfMeasure.setName(resultSet.getString("NAME"));
unitOfMeasure.setDescription(resultSet.getString("DESCRIPTION"));
} catch (SQLException e) {
unitOfMeasure = null;
}
return unitOfMeasure;
}
}
This is the JUnit test that I have written to cover the second branch of the above method (with appropriate context from the test class):
private static UnitOfMeasure testUnitOfMeasure;
private static UnitOfMeasureRowMapper mockRowMapper;
public void setUp() throws Exception {
mockRowMapper = mock(UnitOfMeasureRowMapper.class);
mockResultSet = mock(ResultSet.class);
}
#Test(expected=SQLException.class)
public void testUnitOfMeasureRowMapperFailsSQLException() throws SQLException {
when(mockRowMapper.mapRow(mockResultSet, 1)).thenReturn(null);
testUnitOfMeasure = mockRowMapper.mapRow(mockResultSet, 1);
}
I think the problem is with the last line; somehow I need to force a SQLException. The problem is, I don't know how and haven't been able to find an answer. Can anyone help?
If I understand the question well, the class under test is UnitOfMeasureRowMapper. If this is true, then you don't want to mock it in your test, otherwise you are testing a mock!
What is under test in your JUnit, is the behavior of UnitOfMeasureRowMapper#mapRow when ResultSet you give it throws a SQLException during the execution of the method. Then you want this method to return null.
I would write it like this:
private ResultSet mockResultSet;
private RowMapper<UnitOfMeasure> rowMapper = new UnitOfMeasureRowMapper();
public void setUp() throws Exception {
mockResultSet = mock(ResultSet.class);
}
#Test
public void mapRow_SHOULD_return_null_WHEN_resultSet_throws_a_SQLException() {
when(mockResultSet.getInt(anyString()).thenThrow(new SQLException());
assertThat(mockRowMapper.mapRow(mockResultSet, 1), nullValue());
}
As suggested Samuel in his answer, you may set one of the method of the result set you use to throw a SQLException, and then check in your JUnit that the mapRow method returns null as expected. Here you are not testing the behavior of the result set, so its fine to mock it to achieve a behavior it would normally have under some circunstancies that would be painful to obtain otherwise. Mocking the result set behavior lets you focus on testing the RowMapper behavior.
You are testing the UnitOfMeasureRowMapper that implements RowMapper. So have a rowMapper property in your JUnit, and I prefer to see it through its interface. I like to brutally call the constructor of UnitOfMeasureRowMapper because I want to keep my JUnit as simple as they can be.
Set one of the methods (maybe getInt?) of your mock ResultSet to throw the exception. You didn't specify what mocking framework you're using so I can't tell you the exact syntax.