I am working with Spring 3.0.7 / Hibernate 3.5.0.
I have some code that works with hibernate entity. It works ok. The problem occurs, when I try to make integration test for this code. Below is the scheme of how code is laid out with regards to transactions, just to give an idea what the issus is about.
class EntityDAOImpl implements EntityDAO<T>
{
public T save(T entity)
{
getHibernateTemplate().saveOrUpdate(entity);
}
}
class EntityManagerImpl : implements EntityManager
{
//EntityDAO dao;
#Transactional
public Entity createEntity()
{
Entity entity = dao.createNew();
//set up entity
dao.save(entity);
}
public Entity getFirstEntity()
{
return dao.getFirst();
}
}
The code is run across 2 threads.
Thread1:
//EntityManager entityManager
entityManager.createEntity();
//enity was saved and commited into DB
Thread thread2 = new Thread();
thread2.start();
//...
thread2.join();
...
Thread2:
//since entity was commited, second thread has no problem reading it here and work with it
Entity entity = entityDao.findFirst();
Now I have also an integration test to this code. And here where lies the problem. Transactions for this test are rollbacked, since I don't want to see any changes in the database after it is done.
#TransactionConfiguration(transactionManager="transactionManagerHibernate", defaultRollback=true)
public class SomeTest
{
#Transactional
public void test() throws Exception
{
//the same piece of code as described above is run here.
//but since entityManager.createEntity(); doesn't close transaction,
//Thread2 will never find this entity when calling entityDao.findFirst()!!!!!
}
}
I understand that sharing a hibernate session between threads is not a good idea.
What approach would you recommend to handle this situation?
My first thought was try to simplify and avoid threading - but threading helps me spair memory, otherwise I would have to hold huge chunks of data in memory.
Related
I have a #Transactional method that changes the state of two entities of different, not related, repositories.
something like this:
#Transactional
public void foo() {
A a = repoA.findById(1);
a.setState(s1);
B b = repoB.findById(1);
b.setState(s2);
// (and I also do repoA.save(a); and repoB.save(b); although it is redundant)
}
I also have a transactional method bar that calls foo and publishes an event that is being caught by a TransactionalEventListener like this:
#Transactional
public void bar() {
foo();
applicationEventPublisher.publishEvent(new AppEvent(123));
}
and
#Component
public class MyApplicationEventListener {
#TransactionalEventListener
public void handleAfterCommit(AppEvent appEvent){
//do something;
}
}
Now the issue is that in 80% of the time when handleAfterCommit method is invoked, only (A a ) is being committed but (B b) is losing its changes.
I need help to understand what is going on here, I tried to debug and explore the
TransactionAspectSupport.currentTransactionStatus() but didn't find any insights.
Thanks,
Eilon
I found the issue, we are using a custom AttributeConverter and we didnt implement Equals for the relevant javav object, this caused every dirty check on select to fail and do a full update (overriding values that meanwhile have been changed)
Thanks
I have some code which (in production):
In one thread, primes a cache with data from the db
In another thread, grabs the data from the cache, and starts iterating it's properties.
This threw a LazyInitializationException.
While I know how to fix the problem, I want to get a test around this. However I can't figure out how to recreate the exception in the correct part of the test.
I have to prime the DB with some test data, therefore my test is annotated with #Transactional. Failing to do so causes the set-up to fail with... you guessed it... LazyInitializationException.
Here's my current test:
#Transactional
public class UpdateCachedMarketPricingActionTest extends AbstractIntegrationTest {
#Autowired
private UpdateCachedMarketPricingAction action;
#Autowired
private PopulateMarketCachesTask populateMarketCachesTask;
#Test #SneakyThrows
public void updatesCachedValues()
{
// Populate the cache from a different thread, as this is how it happens in real life
Thread updater = new Thread(new Runnable() {
#Override
public void run() {
populateMarketCachesTask.populateCaches();
}
});
updater.start();
updater.join();
updateMessage = {...} //ommitted
action.processInstrumentUpdate(updateMessage);
}
So, I'm priming my cache in a separate thread, to try to get it outside of the current #Transaction scope. Additionally, I'm also calling entityManager.detatch(entity) inside the cache primer, to try to ensure that the entities that exist within the cache can't lazy-load their collections.
However, the test passes... no exception is thrown.
How can I forcibly get an entity to a state that when I next try to iterate it's collections, it will throw the LazyInitializationException?
You need to ensure that the transactions for each operation are committed, independent of each other. Annotating your test method or test class with #Tranactional leaves the current test transaction open and then rolls it back after execution of the entire test.
So one option is to do something like the following:
#Autowired
private PlatformTransactionManager transactionManager;
#Test
public void example() {
new TransactionTemplate(transactionManager).execute(new TransactionCallbackWithoutResult() {
#Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
// add your code here...
}
});
}
You could invoke your first operation in its own callback, and then invoke the second operation in a different callback. Then, when you access Hibernate or JPA entities after the callbacks, the entities will no longer be attached to the current unit of work (e.g., Hibernate Session). Consequently, accessing a lazy collection or field at that point would result in a LazyInitializationException.
Regards,
Sam
p.s. please note that this technique will naturally leave changes committed to your database. So if you need to clean up that modified state, consider doing so manually in an #AfterTransaction method.
Because of all the problems we can meet when trying to use Hibernate in a multithreaded application (1st clue, 2nd clue, 3rd clue, etc.), I was thinking of another solution: implementing the logical part within a classic Controller, and simply call it from my thread using URL.openConnection().
In other words, instead of doing something like this:
MyThread.java
public class MyThread implements Runnable {
#Override
public void run() {
// do some great stuff with Hibernate
}
}
Anywhere.java
new Thread(new MyThread()).start();
I would like to try something like that:
MyController.java
#Controller
public class MyController {
#RequestMapping(value = "myUrl", method = RequestMethod.GET)
public void myMethod() {
// do some great stuff with Hibernate
}
}
MyThread.java
public class MyThread implements Runnable {
#Override
public void run() {
// simple call the above mapped url
}
}
Anywhere.java
new Thread(new MyThread()).start();
What do you think about it? Good or bad? I haven't tried yet, but I think such a solution will prevent the common errors we can meet using Hibernate in multithreading, because the server will execute the logical part as if someone were requesting the fake page.
PS: I know there are some solutions to use Hibernate in multithreaded applications, but each time I try one, another appears, and that until the I'm-fed-up-with-it point of no return.
PS2: I'm aware that such a solution need to be secured (e.g. UID as a token).
I don't really see what problem you're trying to solve here. Hibernate is almost always used in a multi-threaded environment. In webapps, for example, concurrent requests are handled by multiple concurrent threads, and each thread uses its own Hibernate session. And that doesn't cause any problem.
You will have problem if you share the same session among threads, or if you share a given entity among threads.
If you start your own thread, and this thread uses its own session and entities, I don't see why you would have any problem.
JBoss 4.x
EJB 3.0
I've seen code like the following (greatly abbreviated):
#Stateless
#TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public class EJB1 implements IEJB1
{
#EJB
private IEJB1 self;
#EJB
private IEJB2 ejb2;
#TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public boolean someMethod1()
{
return someMethod2();
}
#TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public boolean someMethod2()
{
return self.someMethod3();
}
#TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public boolean someMethod3()
{
return ejb2.someMethod1();
}
}
And say EJB2 is almost an exact copy of EJB1 (same three methods), and EJB2.someMethod3() calls into EJB3.someMethod1(), which then finally in EJB3.someMethod3() writes to the DB.
This is a contrived example, but have seen similar code to the above in our codebase. The code actually works just fine.
However, it feels like terrible practice and I'm concerned about the #TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) on every method that doesn't even actually perform any DB writes. Does this actually create a new transaction every single time for every method call with the result of:
new transaction
-new transaction
--new transaction
---new transaction
...(many more)
-------new transaciton (DB write)
And then unwraps at that point? Would this ever be a cause for performance concern? Additional thoughts?
Does this actually create a new transaction every single time for
every method call
No, it doesn't. The new transaction will be created only when calling method by EJB reference from another bean. Invoking method2 from method1 within the same bean won't spawn the new transaction.
See also here and here. The latter is exceptionally good article, explaining transaction management in EJB.
Edit:
Thanks #korifey for pointing out, that method2 actually calls method3 on bean reference, thus resulting in a new transaction.
It really creates new JTA transaction in every EJB and this must do a serious performance effect to read-only methods (which makes only SELECTS, not updates). Use #SUPPORTS for read-only methods
If I have a util class with static methods that will call Hibernate functions to accomplish basic data access. I am wondering if making the method synchronized is the right approach to ensure thread-safety.
I want this to prevent access of info to the same DB instance. However, I'm now sure if the following code are preventing getObjectById being called for all Classes when it is called by a particular class.
public class Utils {
public static synchronized Object getObjectById (Class objclass, Long id) {
// call hibernate class
Session session = new Configuration().configure().buildSessionFactory().openSession();
Object obj = session.load(objclass, id);
session.close();
return obj;
}
// other static methods
}
To address the question more generally...
Keep in mind that using synchronized on methods is really just shorthand (assume class is SomeClass):
synchronized static void foo() {
...
}
is the same as
static void foo() {
synchronized(SomeClass.class) {
...
}
}
and
synchronized void foo() {
...
}
is the same as
void foo() {
synchronized(this) {
...
}
}
You can use any object as the lock. If you want to lock subsets of static methods, you can
class SomeClass {
private static final Object LOCK_1 = new Object() {};
private static final Object LOCK_2 = new Object() {};
static void foo() {
synchronized(LOCK_1) {...}
}
static void fee() {
synchronized(LOCK_1) {...}
}
static void fie() {
synchronized(LOCK_2) {...}
}
static void fo() {
synchronized(LOCK_2) {...}
}
}
(for non-static methods, you would want to make the locks be non-static fields)
By using synchronized on a static method lock you will synchronize the class methods and attributes ( as opposed to instance methods and attributes )
So your assumption is correct.
I am wondering if making the method synchronized is the right approach to ensure thread-safety.
Not really. You should let your RDBMS do that work instead. They are good at this kind of stuff.
The only thing you will get by synchronizing the access to the database is to make your application terribly slow. Further more, in the code you posted you're building a Session Factory each time, that way, your application will spend more time accessing the DB than performing the actual job.
Imagine the following scenario:
Client A and B attempt to insert different information into record X of table T.
With your approach the only thing you're getting is to make sure one is called after the other, when this would happen anyway in the DB, because the RDBMS will prevent them from inserting half information from A and half from B at the same time. The result will be the same but only 5 times ( or more ) slower.
Probably it could be better to take a look at the "Transactions and Concurrency" chapter in the Hibernate documentation. Most of the times the problems you're trying to solve, have been solved already and a much better way.
Static methods use the class as the object for locking, which is Utils.class for your example. So yes, it is OK.
static synchronized means holding lock on the the class's Class object
where as
synchronized means holding lock on the class' instance. That means, if you are accessing a non-static synchronized method in a thread (of execution) you still can access a static synchronized method using another thread.
So, accessing two same kind of methods(either two static or two non-static methods) at any point of time by more than a thread is not possible.
Why do you want to enforce that only a single thread can access the DB at any one time?
It is the job of the database driver to implement any necessary locking, assuming a Connection is only used by one thread at a time!
Most likely, your database is perfectly capable of handling multiple, parallel access
If it is something to do with the data in your database, why not utilize database isolation locking to achieve?
To answer your question, yes it does: your synchronized method cannot be executed by more than one thread at a time.
How the synchronized Java keyword works
When you add the synchronized keyword to a static method, the method can only be called by a single thread at a time.
In your case, every method call will:
create a new SessionFactory
create a new Session
fetch the entity
return the entity back to the caller
However, these were your requirements:
I want this to prevent access to info to the same DB instance.
preventing getObjectById being called for all classes when it is called by a particular class
So, even if the getObjectById method is thread-safe, the implementation is wrong.
SessionFactory best practices
The SessionFactory is thread-safe, and it's a very expensive object to create as it needs to parse the entity classes and build the internal entity metamodel representation.
So, you shouldn't create the SessionFactory on every getObjectById method call.
Instead, you should create a singleton instance for it.
private static final SessionFactory sessionFactory = new Configuration()
.configure()
.buildSessionFactory();
The Session should always be closed
You didn't close the Session in a finally block, and this can leak database resources if an exception is thrown when loading the entity.
According to the Session.load method JavaDoc might throw a HibernateException if the entity cannot be found in the database.
You should not use this method to determine if an instance exists (use get() instead). Use this only to retrieve an instance that you assume exists, where non-existence would be an actual error.
That's why you need to use a finally block to close the Session, like this:
public static synchronized Object getObjectById (Class objclass, Long id) {
Session session = null;
try {
session = sessionFactory.openSession();
return session.load(objclass, id);
} finally {
if(session != null) {
session.close();
}
}
}
Preventing multi-thread access
In your case, you wanted to make sure only one thread gets access to that particular entity.
But the synchronized keyword only prevents two threads from calling the getObjectById concurrently. If the two threads call this method one after the other, you will still have two threads using this entity.
So, if you want to lock a given database object so no other thread can modify it, then you need to use database locks.
The synchronized keyword only works in a single JVM. If you have multiple web nodes, this will not prevent multi-thread access across multiple JVMs.
What you need to do is use LockModeType.PESSIMISTIC_READ or LockModeType.PESSIMISTIC_WRITE while applying the changes to the DB, like this:
Session session = null;
EntityTransaction tx = null;
try {
session = sessionFactory.openSession();
tx = session.getTransaction();
tx.begin();
Post post = session.find(
Post.class,
id,
LockModeType.LockModeType.PESSIMISTIC_READ
);
post.setTitle("High-Performance Java Perisstence");
tx.commit();
} catch(Exception e) {
LOGGER.error("Post entity could not be changed", e);
if(tx != null) {
tx.rollback();
}
} finally {
if(session != null) {
session.close();
}
}
So, this is what I did:
I created a new EntityTransaction and started a new database transaction
I loaded the Post entity while holding a lock on the associated database record
I changed the Post entity and committed the transaction
In the case of an Exception being thrown, I rolled back the transaction