How to generate OptimistickLockException by parallel transactions? - java

I know multiple causes can trigger OptimistickLockException, but current test case demands a two transactions write into same data at same time to trigger the exception. More like to create read/write violation issue by parallel transactions. This is what I did:
In the testing class, Create a private field to hold the transactions and some transactional methods:
private List<Transaction> transactions = new ArrayList<Transaction>();
// This method only generate transactions and assign
// them to the member field
#Transactional
public void retrieveTransactions(){
transactions = generateTransactions(); // generateTransactions() is transactional
}
// Following two methods just write those
// transactions into same summery, only the first one sleeps
// during the process to make sure the second transaction
// can occur in a parallel way
#Transactional
public void processTransactionsOne(){
for(Transaction transaction:transactions){
logger.info("====Transaction One=====");
transaction.writeToSameSummary(); // writeToSameSummary() is transactional
try {Thread.sleep(10000); } // sleep for 10 seconds before commit
catch(InterruptedException ex) {}
}
}
#Transactional
public void processTransactionsTwo(){
for(Transaction transaction:transactions){
logger.info("====Transaction Two=====");
transaction.writeToSameSummary(); // writeToSameSummary() is transactional
}
}
in applicationContext.xml, I have made above three methods as scheduler, retrieveTransactions run every 60 seconds, processTransactionsOne and processTransactionsTwo run every 10 seconds
<task:scheduled ref="testBeanName" method="retrieveTransactions" cron="*/60 * * * * *"/>
<task:scheduled ref="testBeanName" method="processTransactionsOne" cron="*/10 * * * * *"/>
<task:scheduled ref="testBeanName" method="processTransactionsTwo" cron="*/10 * * * * *"/>
However, I can never reproduce OptimistickLockException and the log showing up
====Transaction One=====
====Transaction One=====
====Transaction One=====
......
====Transaction Two=====
====Transaction Two=====
====Transaction Two=====
So the transaction are running as synchronized and never in parallel. So how can I (1) making parallel transactions and (2)reproduce OptimistickLockException

To reproduce the optimistic locking exception, try to run code from a method that is not itself transactional. For example call a method that reads and modifies an entity, and returns a detached copy.
Then call the same method to modify the same entity, and return a new detached copy. The first copy now is stale compared to the database. Try to merge the first stale copy into a session, it will throw the optimistic locking exception.
This is some sample code for a versioned entity:
#Entity
public class MyEntity {
...
#Version
private Long version;
}
and this an example of a service that reads and returns an entity detached:
#Repository
public class SomeRepository {
#PersistenceContext
EntityManager em;
#Transactional
public MyEntity readAndModify(Long key) {
MyEntity myEntity = em.find( key, MyEntity.class);
myEntity.setSomeField(...); // this will trigger update via dirty checking
return myEntity;
}
}
This code in a test can then trigger the optimistic locking exception:
public someNonTransactionalTest() {
//read and modify entity, get detached copy
MyEntity detachedCopy1 = someRepository.readAndModify(1L);
//read and modify entity, get another detached copy - copy 1 is stale now
MyEntity detachedCopy2 = someRepository.readAndModify(1L);
// try to merge the stale copy to a session, the optimistic locking exception is thrown
detachedCopy1 = entityManager.merge(detachedCopy1);
... assertions ...
}

Related

How to pass a JPA entity to an Asynchronous EJB method

While processing a request, I would like to 'kick off' separate task in a separate transaction based on the data received and stored in the database.
The main advantage is that I can return the result to my request before all additional processing is done, also if additional processing fails, this will not affect the original processing of the request (as they are in different transactions). This additional processing might require adding extra information to the data.
I have the following setup in mind. Where using #Asynchronous the additional task is created.
#Stateless
public class Bean1
{
#EJB
Bean2 bean2;
#PersistenceContext
private EntityManager entityManager;
public MyResult doSomething(MyInput input) {
MyEntity myEntity = new MyEntity();
// Fill data based on input
entityManager.persist(myEntity);
bean2.asyncActOnData(myEntity);
// Perhaps do some more work and return result
}
}
#Stateless
public class Bean2
{
#Asynchronous // This causes a new transaction to happen
public void asyncActOnData(MyInput input){
// Act upon data and store result in DB
}
}
This seems like a clean way, however this causes JPA Entity to become detached, possibly during processing in Bean2.
Currently I don't plan on changing the data after the persist call (and asnyc), but as the application grows I feel it would be safer to allow this to avoid mistakes.
What is the correct way to kick off the separate asynchronous task based on the persisted data?
I am using: Java EE 6 with Eclipselink 2.5.2 for JPA.
You can continue to make use of the detached instance provided that:
You're not planning to perform further persistence operations;
All associated entities (if any) were available when asyncActOnData was invoked.
However, if you need to perform further persistence operations you can do the following:
#Stateless
public class Bean1
{
#EJB
Bean2 bean2;
#PersistenceContext
private EntityManager entityManager;
public MyResult doSomething(MyInput input) {
MyEntity myEntity = new MyEntity();
// Fill data based on input
entityManager.persist(myEntity);
// Flush the persistence context and detach the entity
// An entity is not permitted to be in more than one
// persistence context at a time. This should prevent
// a race condition with the merge in bean2.
entityManager.flush();
entityManager.detach(myEntity);
Future<Result> futureResult = bean2.asyncActOnData(myEntity);
// Perhaps do some more work and return result
....
return futureResult.get();
}
}
#Stateless
public class Bean2 {
#PersistenceContext
private EntityManager entityManager;
#Asynchronous
public Future<Result> asyncActOnData(MyInput input) {
// this will ensure that the database still matches input
// and add input into the persistence context
input = entityManager.merge(input);
...
return new javax.ejb.AsyncResult<Result>(result);
}
}
You will find it useful to read §3.2 of the "Java™ Persistence API, Version 2.1" specification.

Spring data : #Transactional and propogation

I have the following code:
public void method1(String id){
Object object = repository.findOne(id);
object.setState("running");
repository.save(object);
try{
object2.method2(object); //This method takes 2 hours to complete
object.setState("complete");
}catch(Exception e){
object.setState("failed");
}
repository.save(object);
}
So, I change the state to "running" before calling a method that takes hours to execute. My object is a JPA Entity(with lazily loded collections) and method2() tries to load all the linked entities.
Now, in method2, I am getting
could not initialize proxy - no Session
error because it is outside of transaction (expected behavior). To prevent this, there are two solutions:
Annotate method1 with #Transactional. This would solve it, but then, the state won't be reflected to other transactions until the method execution finishes.
Change the fetch mode in Entity config and make it Eager. This would also solve it but I don't want eager fetching every time.
Is there any other way by which I can make it work?
How about this:
Option 1
1) Create a service method for status changing like following:
#Transactional( propagation = Propagation.REQUIRES_NEW)
public void changeStatusInNewTransaction(String id, String status){
Object object = repository.findOne(id);
object.setState(status);
repository.save(object);
}
2) Change the original method as follows:
#Autowired
Service service;
#Transactional
public void method1(String id){
service.changeStatusInNewTransaction(id, "running");
Object object = repository.findOne(id);
try{
object2.method2(object); //This method takes 2 hours to complete
object.setState("complete");
}catch(Exception e){
object.setState("failed");
}
repository.save(object);
}
Thanks to this set-up, everything can be run under one #Transactional method, but when the state is to be changed to 'running' then :
The current transaction would be suspended
New one would be created
State would be changed and transaction commited
Parent transaction would continue and you can process with your big operation not having a problem that other users will wont see the status change for 2 hours..
Option 2
1) Create a service method for status changing like following:
#Transactional
public void changeStatusInNewTransaction(String id, String status){
Object object = repository.findOne(id);
object.setState(status);
repository.save(object);
}
2) Create transactional method just for long processing
#Transactional
public void performLongProcessing(String id){
Object object = repository.findOne(id);
object2.method2(object); //This method takes 2 hours to complete
object.setState("complete");
repository.save(objects;
}
3) Mark the main method to run without transaction:
#Transactional(propagation = Propagation.NOT_SUPPORTED)
public void method1(String id){
service.changeStatusInNewTransaction(id, "running");
try{
service.performLongProcessing(id);
}catch(Exception e){
service.changeStatusInNewTransaction(id, "failed");
}
}
Having a transaction around a method that executes for several hours, seems like a design mistake, so method1() should not have #Transactional! When you start a transaction, you need a connection and this connection will be allocated from you connection pool for the entire duration, which greatly limits scalability (and pisses of your DBA).
could not initialize proxy - no Session
You get this error because (without #Transactional on method1) your entity is detached after repository.save() has been called, and you can't load the lazy collections. A quick solution for this is to inject an EntityManager into object2 and call EntityManager.refresh() inside method2() this does not require a transaction, as you are only reading data.
There is no reason to use any sort of Transaction propagation to solve this issue.

How to get the transaction Status from HibernateTransactionManager

I am trying to upgrade my spring transaction manager from JtaTransactionManager to HibernateTransactionManager. In JTA TransactionManager we have one method which gives the status of current transaction. Based on the status we are doing some operations. The implementation is as follows :
private void checkTransactionStatus(TransactionStatus status){
if(status instanceof DefaultTransactionStatus) {
DefaultTransactionStatus transactionStatus = (DefaultTransactionStatus) status;
if(transactionStatus.getTransaction() instanceof JtaTransactionObject){
JtaTransactionObject txObject = (JtaTransactionObject) transactionStatus.getTransaction();
int jtaStatus;
try {
jtaStatus = txObject.getUserTransaction().getStatus();
if(jtaStatus==Status.STATUS_MARKED_ROLLBACK){
// logic heare
}
} catch (SystemException e) {}
}
}
}
I want to replace this method with HibernateTransactionManager specific code. I analyzed and found that, HibernateTransactionManager is using HibernateTransactionObject as transaction object. But, unfortunately it's a private inner class that I can't use to get the status. Then I tried to use the parent class JdbcTransactionObjectSupport. But, I don't know how to get the status from this parent class object.
private void checkTransactionStatus(TransactionStatus status){
if(status instanceof DefaultTransactionStatus) {
DefaultTransactionStatus transactionStatus = (DefaultTransactionStatus) status;
if(transactionStatus.getTransaction() instanceof JdbcTransactionObjectSupport){
JdbcTransactionObjectSupport txObject = (JdbcTransactionObjectSupport) transactionStatus.getTransaction();
//how to get the current status ?
}
}
}
Spring has a mechanism for receiving callbacks. You can implement the TransactionSynchronization interface (or easier extend the TransactionSynchronizationAdapter). You probably want to implement the afterCompletion(int) method and put your logic in there.
public class MyTxCallback extends TransactionSynchronizationAdapter {
public void afterCompletion(int status) {
if (status==STATUS_ROLLED_BACK) {
//logic here.
}
}
}
You can then bind that to the transaction by calling the TransactionSynchronizationManager when a transaction is started. Now when the transaction is done the method will be called and you can do your logic (regardless of the underlying transactional resource used).
If you use HibernateTransactionManager you can get the current transaction state from the Hibernate Session:
LocalStatus status = session.getTransaction().getLocalStatus();
and the LocalStatus has the following states:
public enum LocalStatus {
/**
* The local transaction has not yet been begun
*/
NOT_ACTIVE,
/**
* The local transaction has been begun, but not yet completed.
*/
ACTIVE,
/**
* The local transaction has been competed successfully.
*/
COMMITTED,
/**
* The local transaction has been rolled back.
*/
ROLLED_BACK,
/**
* The local transaction attempted to commit, but failed.
*/
FAILED_COMMIT
}

Spring #transaction not working as expected in junit in non debug mode

All MyService methods are transactional. The junit test below, gets count of items, saves a new item, and gets count of items to make sure that counts has been incremented by 1.
public class MyTest extends ServiceTest{
1. int countBefore = myService.getCount(); //return n
2. myService.add(item); //item is really added to DB
3. int countAfter = myService.getCount(); //return n (sometimes n+1)
}
#Transactional(propagation=Propagation.REQUIRES_NEW, isolation=Isolation.READ_COMMITTED)
getCount(){…}
#Transactional(propagation=Propagation.REQUIRES_NEW, isolation=Isolation.SERIALIZABLE)
add(){…}
#Ignore
#ContextConfiguration(locations = { "file:src/main/resources/xxx-context.xml",
"file:src/main/resources/xxx-data.xml",
"file:src/main/resources/xxx-services.xml" })
#TransactionConfiguration(transactionManager = "txManager", defaultRollback = false)
#TestExecutionListeners( { DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class,
TransactionalTestExecutionListener.class,
TestListener.class})
public class ServiceTest extends AbstractUT{
#Ignore
#RunWith(SpringJUnit4ClassRunner.class)
#TestExecutionListeners( {TestListener.class})
public class AbstractUT{
When debugging (3.) returns n+1 which is what I want. But when running the test without debug I get n.
Even sometimes when running the test I get n+1 and next time I get n and when comparing the std output between the two execution, it looks exactly the same. I have enabled log4j.logger.org.springframework.transaction=TRACE and I can see:
Initializing transaction synchronization
Getting transaction for MyService.getCount
...
Completing transaction for MyService.getCount
Clearing transaction synchronization
...
Initializing transaction synchronization
Getting transaction for MyService.add
...
Completing transaction for MyService.add
Clearing transaction synchronization
...
Initializing transaction synchronization
Getting transaction for MyService.getCount
...
Completing transaction for MyService.getCount
Clearing transaction synchronization
So transactions are being executed one after the other, but how is possible that (3.) don't see the saved item?
Transaction managment is setup in my test class as per: https://stackoverflow.com/a/28657650/353985
How can I find what is going wrong?
Thanks!
Had similar issue, but in my case it did not rollback. It seems that you forgot to add #Transactional. From documentation (link)
Transaction management
In the TestContext framework, transactions are managed by the
TransactionalTestExecutionListener which is configured by default,
even if you do not explicitly declare #TestExecutionListeners on your
test class. To enable support for transactions, however, you must
configure a PlatformTransactionManager bean in the ApplicationContext
that is loaded via #ContextConfiguration semantics (further details
are provided below). In addition, you must declare Spring’s
#Transactional annotation either at the class or method level for your
tests.
Here is example form the link above.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
#TransactionConfiguration(transactionManager="txMgr", defaultRollback=false)
#Transactional
public class FictitiousTransactionalTest {
#BeforeTransaction
public void verifyInitialDatabaseState() {
// logic to verify the initial state before a transaction is started
}
#Before
public void setUpTestDataWithinTransaction() {
// set up test data within the transaction
}
#Test
// overrides the class-level defaultRollback setting
#Rollback(true)
public void modifyDatabaseWithinTransaction() {
// logic which uses the test data and modifies database state
}
#After
public void tearDownWithinTransaction() {
// execute "tear down" logic within the transaction
}
#AfterTransaction
public void verifyFinalDatabaseState() {
// logic to verify the final state after transaction has rolled back
}
}
A solution I found till now to pass test is to put assert in afterTransaction method
public class MyTest extends ServiceTest{
#Test
public void test(){
1. int countBefore = myService.getCount(); //return n
2. myService.add(item); //item is really added to DB
}
#AfterTransaction
public void verifyFinalDatabaseState() {
3. int countAfter = myService.getCount(); //return n (sometimes n+1)
//Now always return n+1
}
I would have asked this in a comment but since my reputation does not allow it, I would just try to provide an answer.
It is possible that your are using and ORM that caches the results of count query. Depending how your add/getCount methods are implemented and the configurations of the ORM and datasource, on your second invocation of getCount, you might get a cached value obtained during first invocation of getCount.
This does not explain however why in debug mode you always get the correct result.
Because the current running transaction is set at the test method level, you have two options:
You either remove the #Transactional from the test method and rely on your service method #Transactional boundaries. This way when you call:
int countBefore = myService.getCount();
myService.add(item);
int countAfter = myService.getCount();
Each service call will run in an isolated transaction, just like it happens in the run-time production call.
You flush the Hibernate Session, just after adding the item:
int countBefore = myService.getCount();
myService.add(item);
transactionTemplate.execute(new TransactionCallback<Void>() {
#Override
public Company doInTransaction(TransactionStatus transactionStatus) {
entityManager.flush();
return null;
}
});
int countAfter = myService.getCount();
A HQL/JPQL count query should trigger a flush in AUTO flush mode, but an SQL native query doesn't flush the Session.

How to test org.hibernate.Session?

Hi I have tested this code with junit 4:
public class UserDaoTest {
/**
* Runs before all test methods
*/
#BeforeClass
public static void createDB() {
sessionFactory = HibernateUtil.getSessionFactory();
userDao = new UserDaoImpl();
languageDao = new LanguageDaoImpl();
requestDao = new RequestDaoImpl();
feedbackDao = new FeedbackDaoImpl();
hostelDao = new HostelDaoImpl();
imageDao = new ImageDaoImpl();
}
/**
* Runs after all test methods
*/
#AfterClass
public static void closeSessionFactory() {
HibernateUtil.shutdown();
}
/**
* Runs before each test method
*
* #return
*/
#Before
public void beginTransaction() {
session = sessionFactory.getCurrentSession();
transaction = session.beginTransaction();
}
/**
* Runs after each test method
*/
#After
public void rollbackTransaction() {
if (transaction != null && transaction.isActive()) {
System.out.println("Rolling back trasnaction after #Test method");
// rollback transaction so that tests don't modify database
transaction.rollback();
}
}
#Test
public void testSaveUser() {
User user1 = new User("Ostap", "Stashyshyn", Gender.MALE);
// save user. It delegates to `session.save(user1)` method.
userDao.create(user1);
Integer userId = user1.getUserId();
Assert.assertNotNull(userId);
// causes persistent entities to be saved into persistent context
session.flush();
// read languages from db
List<User> users = userDao.readAll();
Assert.assertThat(users.size(), CoreMatchers.is(1));
// next method annotated with #After is running.
// It rollbacks transaction causing hibernate not to store data into database. Thus database state is the same as when entering this test method
}
It tests successfully, but when I set breakpoint after session.flush() and go to db from command line I see that there is no row for user.
Calling userDao.readAll(); causes hibernate to issue select statement and return users?
I see insert and select statements on console but nothing in db. But when I call transaction.commit() instead of session.flush() then appropriate data is in db.
Is above code correct way to test saving user? Or I should call transaction.commit() instead of session.flush()?
EDIT: I shall adhere to this way of testing org.hibernate.Session?
Thank you!
The session used in this test is a different session to the db session you got from command. Until transaction.commit(), you will not see any data changes by your test.
Your test is valid! You should not use transaction.commit() instead of session.flush() as it will persist your test data and make your next run harder.
You only do transaction.commit() instead of session.flush() if you want to debug your test and verify the test data manually/
session.flush() doesn't mean that persistent object will be written to database. flush will only responsible for synchronizing the underlying persistent store with persistant state held in memory. in simple word it will update or insert into your tables in the running transaction, but it may not commit those changes all this is depends on FlushMode setting.
As the code given by you is not complete so its difficult to tell wther its correct or not. Because what you have written in userDao.create() is hiiden? Where you beginning Transaction is also not clear.

Categories

Resources