今天在测试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是不被事务管理的,后其他都没有关系