【发布时间】:2016-11-16 00:15:09
【问题描述】:
假设我们已经在 Spring(4.2.7 版)中正确配置了由 Hibernate(4.3.11 版)支持的 JPA。启用了休眠一级缓存。我们使用声明式事务。
我们有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
}
}
还有从OuterBean调用的InnerBean:
@Service
public class InnerBean {
@Resource
private UserDao userDao;
@Transactional
public void withTransaction() {
User user = userDao.load(1l);
user.setName("newUserName");
}
}
OuterBean 中的方法 user.getName() 两次返回相同的值是否正确(第二次是在数据库中更新名称之后)?
换句话说,@Transactional(propagation = Propagation.NEVER) 为方法 withoutTransaction() 创建 Hibernate 会话导致第二次调用 user.getName() 从 Hibernate 一级缓存而不是数据库读取是否正确?
编辑
为了解释更多问题,我附上了创建休眠会话的跟踪
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
现在让我们比较一下我删除@Transactional(propagation = Propagation.NEVER)时的跟踪
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
请注意,当我省略 @Transactional(propagation = Propagation.NEVER) 时,会为来自 userDao 的每次方法调用创建单独的会话。
所以我的问题也可以表述为
不应该是
@Transactional(propagation = Propagation.NEVER)在 Spring 中作为监护人实现,防止我们意外使用 事务,没有任何副作用(会话创建)?
【问题讨论】:
-
在 withTransaction 中你应该调用 userDao.merge(user) 来查看 user.getName() 的变化
-
我不明白打电话给
userDao.merge(user)会有什么帮助。调用withTransaction()newUserName 后正确插入数据库。问题是withoutTransaction()第二次调用user.getName()使用休眠一级缓存(因为@Transactional(propagation = Propagation.NEVER)创建休眠会话)而不是获取 newUserName 直接表单数据库。 -
hibernate 总是使用一级缓存,除非你清除它。
-
userDao.load是事务方法吗? -
您好,您可以发布 Hibernate 配置吗?