Предположим, что мы правильно настроили jpa, поддерживаемый hibernate (4.3.11) в spring (4.2.7). Включен кеш первого уровня Hibernate. Мы используем декларативные транзакции. У нас есть 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());//return userName
innerBean.withTransaction();
user = userDao.load(1l);
System.out.println(user.getName());//return userName instead of newUserName
}
}
И InnerBean, вызываемый из OuterBean:
@Service
public class InnerBean {
@Resource
private UserDao userDao;
@Transactional
public void withTransaction(){
User user = userDao.load(1l);
user.setName("newUserName");
}
}
Правильно ли поведение метода user.getName()
в OuterBean возвращает одно и то же значение дважды (второй раз после имени обновления в базе данных)?
Другими словами, это правильное поведение, при котором @Transactional(propagation = Propagation.NEVER)
создает сеанс hibernate для метода withoutTransaction()
, что приводит к тому, что второй вызов user.getName()
считывает из спящего кэша первого уровня вместо базы данных?
ИЗМЕНИТЬ
Чтобы объяснить проблему больше, я трассирую аттестат из создания сеансов спящего режима
TRACE org.hibernate.internal.SessionFactoryImpl$SessionBuilderImpl - Opening Hibernate Session. tenant=null, [email protected]
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, [email protected]
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
Теперь сравним трассировку при удалении @Transactional(propagation = Propagation.NEVER)
TRACE org.hibernate.internal.SessionFactoryImpl$SessionBuilderImpl - Opening Hibernate Session. tenant=null, [email protected]
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, [email protected]
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, [email protected]
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
Обратите внимание, что когда я опускаю @Transactional(propagation = Propagation.NEVER)
, отдельный сеанс является crete для каждого вызова метода из userDao.
Поэтому мой вопрос можно сформулировать также как
Должен ли @Transactional(propagation = Propagation.NEVER)
реализован в spring в качестве опекуна, который мешает нам случайно использовать транзакцию без какого-либо побочного эффекта (создание сеанса)?