SSH框架整合问题之使用声明式事务管理,hibernate中update操作无效的问题
使用spring声明式事务管理
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager"
p:sessionFactory-ref="sessionFactory"/>
<!--配置事务增强处理bean,指定事务管理器 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!-- 配置详细的事务定义 -->
<tx:attributes>
<!-- 所有以get开头的方法都是只读的-->
<tx:method name="get*" read-only="true"/>
<!-- 其他方法默认的事务设置,指定超时时长为5秒 -->
<tx:method name="*" isolation="DEFAULT" propagation="REQUIRED" timeout="5"/>
</tx:attributes>
</tx:advice>
<aop:config>
<!-- 配置一个切入点 -->
<aop:pointcut id="myPointcut" expression="bean(readerService)||bean(bookService)"/>
<!-- 指定在mypointcut切入点应用txAdvice事务增强处理 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="myPointcut"/>
</aop:config>
由spring框架统一进行事务管理,在业务逻辑层(service层)目标方法之前开启事务,在方法结束后结束事务。
Service层
public class ReaderServiceImpl implements ReaderService {
private IReaderDAO readerDAO;
public IReaderDAO getReaderDAO() {
return readerDAO;
}
public void setReaderDAO(IReaderDAO readerDAO) {
this.readerDAO = readerDAO;
}
@Override
public int register(Reader baseBean){
String name=baseBean.getName();
String telNum=baseBean.getTelNum();
if(readerDAO.getByName(name)!=null||readerDAO.getByTelNum(telNum)!=null)
return -1; //-1.当前用户已注册
else{
readerDAO.save(baseBean);
return 1; //1.注册成功
}
}
@Override
public boolean changePassword(Reader baseBean,String newPassword) {
Reader reader=readerDAO.get(Reader.class, baseBean.getId());
if(reader.getPassword().equals(baseBean.getPassword())){
reader.setPassword(newPassword);
readerDAO.update(reader);
return true;
}else
return false;
}
DAO层
public class IDAOImpl implements IDAO {
private SessionFactory factory;
private ThreadLocal<Session> local=new ThreadLocal<Session>();
public void setFactory(SessionFactory factory){
this.factory=factory;
}
public SessionFactory getFactory(){
return this.factory;
}
public Session getSession(){
Session session=local.get();
if(session==null){
session=factory.openSession();
local.set(session);
}
return session;
}
@Override
public Serializable save(T entity) {
Session session=getSession();
Serializable id=session.save(entity);
return id;
}
@Override
public void update(T entity) {
Session session=getSession();
session.update(entity);
}
我这里利用了ThreadLocal线程本地变量,为每一个线程绑定一个Session,以避免Session开启过多,增加开销的情况。
但这里就出现了问题,当调用ReaderServiceImpl的register方法(即调用dao的save方法)是有用的,而调用changePassword方法(即dao的update方法)则无用。
解决方法:
将dao中getSession()改为
public Session getSession(){
return factory.getCurrentSession();
}
分析:
对于Session:
Session是应用程序和数据库层进行交互操作的一个单线程对象,Session是有一个必选的一级缓存的,在显式flush之前,所有的持久化数据都会存在缓存中。
当使用OpenSession打开的Session不会在事务提交时刷新缓存,
而getCurrentSession得到 Session的flushBeforeCompletionEnabled参数为true,会在提交时刷新缓存。
所以update操作会先将更新数据存在一级缓存中,不刷新缓存就是无效。
而save操作有效是因为save方法要求执行方法同时立即执行相应的sql语句,并返回唯一标识符,所以只要事务提交,不刷新缓存也是有效的