Let's assume that we have correctly configured JPA backed by Hibernate (version 4.3.11) in Spring (version 4.2.7). Hibernate first level cache is enabled. We use declarative transactions.
We have OuterBean
:
@Service
public class OuterBean {
@Resource
private UserDao userDao;
@Resource
private InnerBean innerBean;
@Transactional(propagation = Propagation.NEVER)
public void withoutTransaction() {
User user = userDao.load(1l);
System.out.println(user.getName()); // returns "userName"
// this call is supposed to change the initial user name
innerBean.withTransaction();
user = userDao.load(1l);
System.out.println(user.getName()); // still returns "userName" instead of "newUserName"
}
}
And InnerBean
that is called from OuterBean
:
@Service
public class InnerBean {
@Resource
private UserDao userDao;
@Transactional
public void withTransaction() {
User user = userDao.load(1l);
user.setName("newUserName");
}
}
Is it correct behaviour that method user.getName()
in OuterBean
returns the same value twice (second time is after update name in database)?
In other words is it correct behaviour that @Transactional(propagation = Propagation.NEVER)
annotation creates Hibernate session for method withoutTransaction()
that causes that second call user.getName()
reads from Hibernate first level cache instead of database?
EDIT
To explain the problem more I'm attaching the traces from creation of Hibernate sessions:
TRACE org.hibernate.internal.SessionFactoryImpl$SessionBuilderImpl - Opening Hibernate Session. tenant=null, owner=org.hibernate.jpa.internal.EntityManagerImpl@c17285e
TRACE org.hibernate.internal.SessionImpl - Opened session at timestamp: 14689173439
TRACE org.hibernate.internal.SessionImpl - Setting flush mode to: AUTO
TRACE org.hibernate.internal.SessionImpl - Setting cache mode to: NORMAL
TRACE org.hibernate.internal.SessionImpl - Setting cache mode to: NORMAL
TRACE org.hibernate.internal.SessionImpl - Setting cache mode to: NORMAL
userName
TRACE org.hibernate.internal.SessionFactoryImpl$SessionBuilderImpl - Opening Hibernate Session. tenant=null, owner=org.hibernate.jpa.internal.EntityManagerImpl@715c48ca
TRACE org.hibernate.internal.SessionImpl - Opened session at timestamp: 14689173439
TRACE org.hibernate.internal.SessionImpl - Setting flush mode to: AUTO
TRACE org.hibernate.internal.SessionImpl - Setting cache mode to: NORMAL
TRACE org.hibernate.internal.SessionImpl - Setting cache mode to: NORMAL
TRACE org.hibernate.internal.SessionImpl - Setting cache mode to: NORMAL
TRACE org.hibernate.internal.SessionImpl - Automatically flushing session
TRACE org.hibernate.internal.SessionImpl - before transaction completion
TRACE org.hibernate.internal.SessionImpl - after transaction completion
TRACE org.hibernate.internal.SessionImpl - Closing session
TRACE org.hibernate.internal.SessionImpl - Setting cache mode to: NORMAL
TRACE org.hibernate.internal.SessionImpl - Setting cache mode to: NORMAL
userName
TRACE org.hibernate.internal.SessionImpl - Closing session
Now let's compare with the traces when I remove @Transactional(propagation = Propagation.NEVER)
annotation:
TRACE org.hibernate.internal.SessionFactoryImpl$SessionBuilderImpl - Opening Hibernate Session. tenant=null, owner=org.hibernate.jpa.internal.EntityManagerImpl@4ebd2c5f
TRACE org.hibernate.internal.SessionImpl - Opened session at timestamp: 14689203905
TRACE org.hibernate.internal.SessionImpl - Setting flush mode to: AUTO
TRACE org.hibernate.internal.SessionImpl - Setting cache mode to: NORMAL
TRACE org.hibernate.internal.SessionImpl - Setting cache mode to: NORMAL
TRACE org.hibernate.internal.SessionImpl - Setting cache mode to: NORMAL
TRACE org.hibernate.internal.SessionImpl - Closing session
userName
TRACE org.hibernate.internal.SessionFactoryImpl$SessionBuilderImpl - Opening Hibernate Session. tenant=null, owner=org.hibernate.jpa.internal.EntityManagerImpl@5af84083
TRACE org.hibernate.internal.SessionImpl - Opened session at timestamp: 14689203905
TRACE org.hibernate.internal.SessionImpl - Setting flush mode to: AUTO
TRACE org.hibernate.internal.SessionImpl - Setting cache mode to: NORMAL
TRACE org.hibernate.internal.SessionImpl - Setting cache mode to: NORMAL
TRACE org.hibernate.internal.SessionImpl - Setting cache mode to: NORMAL
TRACE org.hibernate.internal.SessionImpl - Automatically flushing session
TRACE org.hibernate.internal.SessionImpl - before transaction completion
TRACE org.hibernate.internal.SessionImpl - after transaction completion
TRACE org.hibernate.internal.SessionImpl - Closing session
TRACE org.hibernate.internal.SessionFactoryImpl$SessionBuilderImpl - Opening Hibernate Session. tenant=null, owner=org.hibernate.jpa.internal.EntityManagerImpl@35f4f41f
TRACE org.hibernate.internal.SessionImpl - Opened session at timestamp: 14689203906
TRACE org.hibernate.internal.SessionImpl - Setting flush mode to: AUTO
TRACE org.hibernate.internal.SessionImpl - Setting cache mode to: NORMAL
TRACE org.hibernate.internal.SessionImpl - Setting cache mode to: NORMAL
TRACE org.hibernate.internal.SessionImpl - Setting cache mode to: NORMAL
TRACE org.hibernate.internal.SessionImpl - Closing session
newUserName
Please notice when I omit @Transactional(propagation = Propagation.NEVER)
annotation a separate session is created for every invocation of method from userDao
.
So my question can be formulated also as
Shouldnโt be @Transactional(propagation = Propagation.NEVER)
implemented in Spring as a guardian that prevents us from accidental use of
transaction, without any side effect (session creation)?