msfx1214 2008-12-23 16:07
浏览 462
已采纳

Spring事务的问题,bulkUpdate,或query.executeUpdate

今天在测试spring任务调度时,突然发现我配的声明事务不起作用了,找了好久才发现不是我的事务的问题,是我在Dao中用了一个方法有问题

方法如下:
public void updateByIds(final Set updateIds)throws DaoException{
try {
String queryString="update from Yaoyueyingyue y set y.state='2' where y.yaoyueid in (?)";
getHibernateTemplate().bulkUpdate(queryString, updateIds.toArray());
} catch (Exception e) {
e.printStackTrace();
throw new DaoException(this.getClassName()+e.getMessage());
}

updateIds是一个包含要更新的编号集合,我发现用这个方法在我Manager中调用Dao事务就不起作用了,后来又改成这样
public void updateByIds(final Set updateIds)throws DaoException{
try {
getHibernateTemplate().execute(new HibernateCallback(){
final String hql="update Yaoyueyingyue y set y.state=2 where yaoyueid in (:yaoyueid)";
public Object doInHibernate(Session session) throws HibernateException, SQLException {
Query query=session.createQuery(hql);
query.setParameterList("yaoyueid", updateIds);
query.executeUpdate();
return null;
}

    });

} catch (Exception e) {
e.printStackTrace();
throw new DaoException(this.getClassName()+e.getMessage());
}

}

事务还是不行,这两个方法好像不受spring AOP事务管理,只要执行到这个Dao的方法就自动提交了,出来异常也不能回滚,真是郁闷,小弟对这块不是很明白,为什么事务就不行了呢,希望那位牛人,帮我解释下,谢谢了,

我的spring声明事务大概如下:



true

<bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp">  
    <property name="transactionTimeout" value="300"/>    
</bean>  

<bean id="springTransactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">  
    <property name="transactionManager"><ref bean="atomikosTransactionManager"  /></property>  
    <property name="userTransaction"><ref bean="atomikosUserTransaction"  /></property>  
</bean>  

aop:config
<!--
This definition creates auto-proxy infrastructure based on the given pointcut,
expressed in AspectJ pointcut language. Here: applying the advice named
"txAdvice" to all methods on classes named PetStoreImpl.
-->
advice-ref="txAdvice" />
advice-ref="txAdvice" />
/aop:config

<!-- @Transactional 时要使用下面一行 -->

<!-- -->
<!-- Transaction advice definition, based on method name patterns.
Defaults to PROPAGATION_REQUIRED for all methods whose name starts with
"insert" or "update", and to PROPAGATION_REQUIRED with read-only hint
for all other methods.-->
<!-- 引用springTransactionManager -->

tx:attributes


....

/tx:attributes
/tx:advice

atomikosTransactionManager这个东西不用管,是一个开源的支持JTA分布式的JAR,希望有人能够为我解答........
[b]问题补充:[/b]
谢谢你的解答,但是我还是不明白你的意思,你是指我的Dao中用了内部类吗,你所指的 “调用updateByIds方法 的代码 跳出你的当前类 然后在别的类调用当前了类的接口中的方法updateByIds ”是指什么意思,能不能说明白点,谢谢了!!!
[b]问题补充:[/b]
谢谢你的答复,你的意思我也理解,但好像不是这个问题,因为我并没有像你说的那样在类的内部调用,我的所有方法都是在业务逻辑层调用的Manager层,Manager层我是在Spring中配置了的声明事务的,我给你据个例子:
这两个方法都是Dao中的
方法一:
[code="java"]
public void updateByIds(final Set updateIds)throws DaoException{
try {
/*String queryString="update from Yaoyuepub y set y.state='2' where y.yaoyueid in (?)";
getHibernateTemplate().bulkUpdate(queryString, updateIds.toArray());*/

        getHibernateTemplate().execute(new HibernateCallback(){
            final String hql="update Yaoyuepub y set y.state=2 where yaoyueid in (:yaoyueid)";
            public Object doInHibernate(Session session) throws HibernateException, SQLException {
                Query query=session.createQuery(hql);
                query.setParameterList("yaoyueid", updateIds);
                query.executeUpdate();
                return null;
            }

        }
        );
    } catch (Exception e) {
        e.printStackTrace();
        throw new DaoException(this.getClassName()+e.getMessage());
    }       
}

[/code]

这个方法是把所有的要改的ID都一次性更新调,我是不想执行多条sql,在Manager中调用这个方法事务就起不了作用,还有一个方法,就是普通的更新对象
[code="java"]
public void update(T t) throws DaoException {
try {
getHibernateTemplate().update(t);
} catch (Exception e) {
throw new DaoException(getClassName() + " update exception...",e);
}

}

[/code]

要是把刚才Manager中调用改成循环执行下面的方法一个一个对象,就是有事务的,所以调用都一样,更类的内部调用应给没有关系的,我认为不管是query.executeUpdate还是spring自己提供的bulkUpdate这两个方法都是要写sql的,目的是满足批量更新和更大的灵活性,但是事务就不行了,我认为肯定可以让声明式事务支持这两个方法,就是不知道怎么配置一下,你可以自己在代码中分别做个例子试试,看看是不是用批量更新事务就控制不了了,

这就是我的理解,还请多多指教,谢谢诶!!!
[b]问题补充:[/b]

我测试过了,以为可以了,但是还是不行,下面是我调用的一小部分代码
[code="java"]
public void runThread() {
Set updateIds = new HashSet();
for (Yaoyueyingyue yaoyueyingyue : yaoyueyingyues) {

updateIds.add(yaoyueyingyue.getYaoyueid());

}

if (updateIds.size() > 0) {
yaoyuepubDao.updateByIds(updateIds);
if(true)
throw new RuntimeException("AAAAAAAAAAAAAAAAAAAAAAAAA");
yaoyueyingyueDao.updateByIds(updateIds);
}

[/code]
上面是我Manager中Spring任务调度自动执行的方法的一小部分,我中间估计抛出了异常,但是yaoyuepubDao数据库中都更新了,事务不起作用,我估计其实就是和bulkUpdate方法一样,只要这个方法能用事务控制了的话,应该没问题了
[b]问题补充:[/b]
下面是我在网上拷贝的--------------------------------

  Spring的HibernateTemplate提供了Hibernate的完美封装,即通过匿名类实现回调,来保证Session的自动资源管理和事务的管理。其中核心方法是:

  java代码:

HibernateTemplate.execute(new HibernateCallback() {
 public Object doInHibernate(Session session) throws HibernateException {
  ....
 }
}

  回调方法提供了session作为参数,有了session,就可以自由的使用Hibernate API编程了。使用了spring的之后,代码修改如下:

  web层代码:

  java代码:

DetachedCriteria detachedCriteria = DetachedCriteria.forClass(Department.class);
detachedCriteria.createAlias("employees", "e").add(Restrictions.eq("name", "department")).add(Restrictions.gt(("e.age"), new Integer(20)));
departmentManager.findByCriteria(detachedCriteria);

  构造detachedCriteria,作为参数传递给departmentManager

  业务层代码使用spring,DepartmentManager的findByCriteria如下:

  java代码:

public List findByCriteria(final DetachedCriteria detachedCriteria) {
 return (List) getHibernateTemplate().execute(new HibernateCallback() {
  public Object doInHibernate(Session session) throws HibernateException {
   Criteria criteria = detachedCriteria.getExecutableCriteria(session);
   return criteria.list();
  }
 });
}

  实际上也就是:

  java代码:

Criteria criteria = detachedCriteria.getExecutableCriteria(session);
return criteria.list();

  而已

  但是该程序代码执行,会抛出强制类型转换异常!

  我跟踪了一下spring和Hibernate源代码,原因如下:

  spring的HibernateTemplate的execute方法提供的回调接口具有Session作为参数,但是实际上,默认情况下,HibernateTemplate传递给回调接口的session并不是org.hibernate.impl.SessionImpl类,而是SessionImpl类的一个Proxy类。之所以替换成为一个Proxy类,HibernateTemplate的注释说明,Proxy提供了一些额外的功能,包括自动设置Cachable,Transaction的超时时间,Session资源的更积极的关闭等等。

  java代码:

private boolean exposeNativeSession = false;
...

  execute方法内部:

Session sessionToExpose = (exposeNativeSession ? session : createSessionProxy(session));

  但是遗憾的是,Hibernate的DetachedCriteria的setExecutableCriteria方法却要求将session参数强制转为SessionImpl,但是spring传过来的却是一个Proxy类,因此就报错了。

  java代码:

public Criteria getExecutableCriteria(Session session) {
 impl.setSession( (SessionImpl) session ); // 要求SessionImpl,Spring传递的是Proxy
 return impl;
}

  解决方法,禁止Spring的HibernateTemplate传递Proxy类,强制要求它传递真实的SessionImpl类,即给exexute方法增加一个参数,提供参数为true,如下:

  java代码:

public List findByCriteria(final DetachedCriteria detachedCriteria) {
 return (List) getHibernateTemplate().execute(new HibernateCallback() {
  public Object doInHibernate(Session session) throws HibernateException {
   Criteria criteria = detachedCriteria.getExecutableCriteria(session);
   return criteria.list();
  }
 }, true);
}

[b]问题补充:[/b]
bulkUpdate这个方法按你给的源码,那我是用错了,但是你所说的我的模拟异常不再AOP的事务之内,我就不同意你的观点了,[code="java"]

if (updateIds.size() > 0) {

yaoyuepubDao.updateByIds(updateIds); // 事务开启 执行updateByIds 事务提交

if(true)

throw new RuntimeException("AAAAAAAAAAAAAAAAAAAAAAAAA");

yaoyueyingyueDao.updateByIds(updateIds); // 事务开启 执行updateByIds 事务提交

}

[/code]

我这段代码是两个Dao的操作,而这两个Dao的操作是被封装在一个Manger中的方法中的,Manager的每个方法都是有事务的,在操作玩第一个Dao后抛出一个RunTime异常,这时候第一个Dao操作已经执行了,这时候事务应该回滚的,不应该去更新的第一个Dao的操作,Manager中本来就业务层,中间有好多的Dao操作,事务应该控制这些Dao要不都提交,要不都回滚,你说呢,而你说的在11-12行之间加异常,那在一个Dao中,再说的的Dao是没有配事务的,又何谈回滚呢,要是把我上面两个Dao操作改成普通的对象更新,是可以回滚的,这个我肯定
比如这样
[code="java"]
yaoyuepubDao.update(yaoyuepub);
if(true)

throw new RuntimeException("AAAAAAAAAAAAAAAAAAAAAAAAA");

yaoyueyingyueDao.update(yaoyueyingyue);

[/code]

这个时候如果抛出异常,yaoyuepub是不会更新到数据库的,会回滚的,所以我总结就是executeUpdate(sql)这个方法我们直接sql,和操作对象是不一样的的,具体我也没有研究
[b]问题补充:[/b]
[code="java"]
public void updateByIds(final Set updateIds)throws DaoException{

try {

    getHibernateTemplate().execute(new HibernateCallback(){   
        final String hql="update Yaoyuepub y set y.state=2 where yaoyueid in (:yaoyueid)";   
        public Object doInHibernate(Session session) throws HibernateException, SQLException {   
            Query query=session.createQuery(hql);   
            query.setParameterList("yaoyueid", updateIds);   
            query.executeUpdate();   
            return null;   
        }   

    }   
    , true);   
} catch (Exception e) {   
    e.printStackTrace();   
    throw new DaoException(this.getClassName()+e.getMessage());   
}   

}

[/code]

把上面的updateIds方法改成:
[code="java"]
public void updateByIds(final Set updateIds)throws DaoException{
try {
DetachedCriteria dc=DetachedCriteria.forClass(Yaoyuepub.class);
dc.add(Restrictions.in("yaoyueid", updateIds));
List yaoyuepubs=select(dc);
for(Yaoyuepub y:yaoyuepubs){
update(y);
}

                } catch (Exception e) {
        e.printStackTrace();
        throw new DaoException(this.getClassName()+e.getMessage());
    }       
}

[/code]

所有的调用都不变,spring事务就起作用了,说明自己createQuery然后executeUpdate是不被事务管理的,后其他都没有关系

  • 写回答

7条回答 默认 最新

  • dch1287 2008-12-27 22:43
    关注

    呵呵 我以为你的事务陪在了Dao这一层了
    如果是你说的Manager那么这样抛出异常模拟事务回滚是对的

    至于你说自己createQuery 执行hql不受事务控制 我可以明确的告诉你 没有这种说法
    我之前三四个Spring+Hibernate的项目经验告诉我事务这样控制是没有问题的

    找找其他原因吧 不可能是你总结的那个原因

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(6条)

报告相同问题?

悬赏问题

  • ¥20 蓝牙耳机怎么查看日志
  • ¥15 Fluent齿轮搅油
  • ¥15 八爪鱼爬数据为什么自己停了
  • ¥15 交替优化波束形成和ris反射角使保密速率最大化
  • ¥15 树莓派与pix飞控通信
  • ¥15 自动转发微信群信息到另外一个微信群
  • ¥15 outlook无法配置成功
  • ¥30 这是哪个作者做的宝宝起名网站
  • ¥60 版本过低apk如何修改可以兼容新的安卓系统
  • ¥25 由IPR导致的DRIVER_POWER_STATE_FAILURE蓝屏