NutzCN Logo
问答 nutz 绑定 spring 用声明事务不好使
发布于 599天前 作者 ebin1983 1698 次浏览 复制 上一个帖子 下一个帖子
标签: spring dao

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd"
Spring公共配置




classpath:config/dataSource.properties
classpath:config/config.properties


<bean id="springDaoRunner" class="com.sv.qc.dao.SpringDaoRunner" />

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
    <property name="driverClassName" value="${dao.driverClassName}" />
    <property name="username" value="${dao.username}" />
    <property name="password" value="${dao.password}" />
    <property name="url" value="${dao.url}" />
</bean>

<bean id="svDao" class="org.nutz.dao.impl.NutDao">
    <property name="dataSource">
       <ref local="dataSource" />
    </property>
    <property name="runner">
        <ref local="springDaoRunner" />
     </property>
</bean>

<bean id="springContextHolder" class="com.sv.qc.util.SpringContextHolder"
    lazy-init="false" />

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"></property>
</bean>

<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>

<!-- 避免事务失效和bean重复加载 -->
<context:component-scan base-package="com.sv.qc">
    <context:exclude-filter type="annotation"
       expression="org.springframework.stereotype.Controller" />
</context:component-scan>

package com.sv.qc.dao;
import java.sql.Connection;

import javax.sql.DataSource;

import org.nutz.dao.ConnCallback;
import org.nutz.dao.impl.DaoRunner;
import org.springframework.jdbc.datasource.DataSourceUtils;
public class SpringDaoRunner implements DaoRunner {
public void run(DataSource dataSource, ConnCallback callback) {
Connection con = DataSourceUtils.getConnection(dataSource);
try {
callback.invoke(con);
} catch (Exception e) {
if (e instanceof RuntimeException)
throw (RuntimeException) e;
else
throw new RuntimeException(e);
} finally {
DataSourceUtils.releaseConnection(con, dataSource);
}
}
}

31 回复

不好使 啊事务
@Transactional(rollbackFor = Exception.class)
public boolean delOrder(String order_no, String user_code) {
int ret = svDao.update(SysOrder.class, Chain.make("status", "11"), Cnd.where("order_no", "=", order_no));
this.addOrderTrack(user_code, order_no, "11");
if (ret > 0) {
return true;
} else {
return false;
}
}

这两个不是能这样混用吧

@Rekoe 那要怎么用呢 我想用spring的事务

在线等回复啊

@Transactional(rollbackFor = Exception.class)

把rollbackFor去掉试试

我这边使用和spring的配置的事务没问题的
能把delOrder 方法在类全部贴出吗

我怎么记得nutz的crud方法都不会抛出异常的呢

@hzl7652 你是怎么配置的

这代码只有一句update?那有没有事务都一样吧?

你说无效,体现在哪里了? 这种update一般不会报错吧?没报错何来回滚

来自炫酷的 NutzCN

我在SpringDaoRunner里面加了一句打印堆栈的,可以看到各种拦截器

java.lang.Throwable
	at org.nutz.j2cache.spring.SpringDaoRunner.run(SpringDaoRunner.java:17)
	at org.nutz.dao.impl.DaoSupport._exec(DaoSupport.java:219)
	at org.nutz.dao.impl.NutDao.func(NutDao.java:720)
	at org.nutz.dao.impl.NutDao.func(NutDao.java:701)
	at org.nutz.dao.impl.NutDao._count(NutDao.java:688)
	at org.nutz.dao.impl.NutDao.count(NutDao.java:662)
	at org.nutz.dao.impl.NutDao.count(NutDao.java:658)
	at org.nutz.j2cache.spring.NutzHappyService.doUpdate(NutzHappyService.java:32)
	at org.nutz.j2cache.spring.NutzHappyService$$FastClassBySpringCGLIB$$43115fb8.invoke(<generated>)
	at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:700)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
	at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:96)
	at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:260)
	at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:94)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:633)
	at org.nutz.j2cache.spring.NutzHappyService$$EnhancerBySpringCGLIB$$e0e3e7e8.doUpdate(<generated>)
	at org.nutz.j2cache.spring.SpringJ2CacheManagerTest.testSpringJ2CacheManager(SpringJ2CacheManagerTest.java:20)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
	at java.lang.reflect.Method.invoke(Unknown Source)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
	at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
	at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)

不过, 你还是得先回答上一个问题: 只有一句sql, 谈啥事务?

PS: 还有一种常见的原因, 就是那个数据库表的引擎是不支持事务的myisam

@wendal int ret = svDao.update(SysOrder.class, Chain.make("status", "11"), Cnd.where("order_no", "=", order_no));
this.addOrderTrack(user_code, order_no, "11");

2个啊 this.addOrderTrack(user_code, order_no, "11"); 这个又添加一个记录

@ebin1983 你说的事务没生效是第二个dao操作抛异常,但第一个dao操作没回滚?

来自炫酷的 NutzCN

贴代码请用代码块

来自炫酷的 NutzCN

@wendal

@Transactional(propagation = Propagation.REQUIRED)
	public boolean delOrder(String order_no, String user_code) throws RuntimeException {
		try {

			int ret = svDao.update(SysOrder.class, Chain.make("status", "11"), Cnd.where("order_no", "=", order_no));
			addOrderTrack(user_code, order_no, "11");
			if (ret > 0) {
				throw new RuntimeException("");
			} else {

			}

		} catch (Exception e) {
			throw new RuntimeException(e);
		}
		return false;
	}
	private void addOrderTrack(String user_code, String order_no, String status) {
		SysOrder order = svDao.fetch(SysOrder.class, Cnd.where("order_no", "=", order_no));
		SysOrderTrack track = new SysOrderTrack();
		track.setOrder_no(order_no);
		track.setStatus(status);
		track.setDf_code(order.getDf_code());
		track.setUser_code(user_code);
		track.setCreate_date(new Date());
		track.setCreate_time(new Date());
		this.svDao.fastInsert(track);
	}

我是故意抛的异常 但是没有回滚 第一个update 还是改了数据了

@wendal 我检查过了 特意改的 都是inodb

把业务逻辑去掉,单独两个dao操作然后抛出异常

来自炫酷的 NutzCN

@wendal

@Transactional(propagation = Propagation.REQUIRED)
	public boolean delOrder(String order_no, String user_code) throws RuntimeException {
		try {

			int ret = svDao.update(SysOrder.class, Chain.make("status", "11"), Cnd.where("order_no", "=", order_no));
			SysOrder order = svDao.fetch(SysOrder.class, Cnd.where("order_no", "=", order_no));
			SysOrderTrack track = new SysOrderTrack();
			track.setOrder_no(order_no);
			track.setStatus("11");
			track.setDf_code(order.getDf_code());
			track.setUser_code(user_code);
			track.setCreate_date(new Date());
			track.setCreate_time(new Date());
			svDao.fastInsert(track);
			if (ret > 0) {
				throw new RuntimeException("");
			} else {

			}
			
			//addOrderTrack(user_code, order_no, "11");
			

		} catch (Exception e) {
			throw new RuntimeException(e);
		}
		return false;
	}

还是没有回滚

待我看看spring的trace日志

我的测试代码输出的日志

2015-12-07 10:11:12,483 org.springframework.context.support.AbstractApplicationContext.prepareRefresh(AbstractApplicationContext.java:512) INFO  - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@75412c2f: startup date [Mon Dec 07 10:11:12 CST 2015]; root of context hierarchy
2015-12-07 10:11:12,534 org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:316) INFO  - Loading XML bean definitions from class path resource [beans.xml]
2015-12-07 10:11:12,729 org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:603) INFO  - Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@2d8f65a4: defining beans [happy,springDaoRunner,ds,dao,transactionManager,org.springframework.aop.config.internalAutoProxyCreator,org.springframework.transaction.annotation.AnnotationTransactionAttributeSource#0,org.springframework.transaction.interceptor.TransactionInterceptor#0,org.springframework.transaction.config.internalTransactionAdvisor]; root of factory hierarchy
ALL Nutz Log via Log4jLogAdapter
2015-12-07 10:11:12,753 org.nutz.log.Logs.<clinit>(Logs.java:20) INFO  - Nutz is licensed under the Apache License, Version 2.0 .
Report bugs : https://github.com/nutzam/nutz/issues
2015-12-07 10:11:12,759 org.nutz.dao.impl.SimpleDataSource.<init>(SimpleDataSource.java:39) WARN  - SimpleDataSource is use for Test/Attempt, NOT Using in Production environment!
2015-12-07 10:11:12,759 org.nutz.dao.impl.SimpleDataSource.<init>(SimpleDataSource.java:40) WARN  - SimpleDataSource is NOT a Connection Pool, So it is slow but safe for debug/study
2015-12-07 10:11:12,891 org.nutz.filepool.NutFilePool.<init>(NutFilePool.java:23) INFO  - Init file-pool by: C:\Users\wendal/.nutz/tmp/dao/ [200000]
2015-12-07 10:11:12,902 org.nutz.filepool.NutFilePool.<init>(NutFilePool.java:66) INFO  - file-pool.cursor: 727
2015-12-07 10:11:12,912 org.nutz.dao.jdbc.Jdbcs.getExpert(Jdbcs.java:98) INFO  - Get Connection from DataSource for JdbcExpert
2015-12-07 10:11:13,151 org.springframework.transaction.interceptor.AbstractFallbackTransactionAttributeSource.getTransactionAttribute(AbstractFallbackTransactionAttributeSource.java:107) DEBUG - Adding transactional method 'NutzHappyService.doUserClear' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; '',-java.lang.Exception
2015-12-07 10:11:13,271 org.nutz.dao.impl.sql.run.NutDaoExecutor._runSelect(NutDaoExecutor.java:193) DEBUG - SELECT COUNT(*) FROM t_user 
2015-12-07 10:11:13,284 org.springframework.transaction.support.TransactionSynchronizationManager.bindResource(TransactionSynchronizationManager.java:193) TRACE - Bound value [org.springframework.jdbc.datasource.ConnectionHolder@49c7b90e] for key [org.nutz.dao.impl.SimpleDataSource@73700b80] to thread [main]
2015-12-07 10:11:13,284 org.springframework.transaction.support.TransactionSynchronizationManager.initSynchronization(TransactionSynchronizationManager.java:272) TRACE - Initializing transaction synchronization
2015-12-07 10:11:13,285 org.springframework.transaction.interceptor.TransactionAspectSupport.prepareTransactionInfo(TransactionAspectSupport.java:444) TRACE - Getting transaction for [org.nutz.j2cache.spring.NutzHappyService.doUserClear]
2015-12-07 10:11:13,300 org.springframework.transaction.support.TransactionSynchronizationManager.getResource(TransactionSynchronizationManager.java:140) TRACE - Retrieved value [org.springframework.jdbc.datasource.ConnectionHolder@49c7b90e] for key [org.nutz.dao.impl.SimpleDataSource@73700b80] bound to thread [main]
2015-12-07 10:11:13,300 org.nutz.dao.impl.sql.run.NutDaoExecutor._runStatement(NutDaoExecutor.java:313) DEBUG - DELETE FROM t_user 
2015-12-07 10:11:13,302 org.springframework.transaction.support.TransactionSynchronizationManager.getResource(TransactionSynchronizationManager.java:140) TRACE - Retrieved value [org.springframework.jdbc.datasource.ConnectionHolder@49c7b90e] for key [org.nutz.dao.impl.SimpleDataSource@73700b80] bound to thread [main]
2015-12-07 10:11:13,302 org.springframework.transaction.interceptor.TransactionAspectSupport.completeTransactionAfterThrowing(TransactionAspectSupport.java:488) TRACE - Completing transaction for [org.nutz.j2cache.spring.NutzHappyService.doUserClear] after exception: java.lang.RuntimeException
2015-12-07 10:11:13,302 org.springframework.transaction.interceptor.RuleBasedTransactionAttribute.rollbackOn(RuleBasedTransactionAttribute.java:131) TRACE - Applying rules to determine whether transaction should rollback on java.lang.RuntimeException
2015-12-07 10:11:13,303 org.springframework.transaction.interceptor.RuleBasedTransactionAttribute.rollbackOn(RuleBasedTransactionAttribute.java:148) TRACE - Winning rollback rule is: RollbackRuleAttribute with pattern [java.lang.Exception]
2015-12-07 10:11:13,308 org.springframework.transaction.support.TransactionSynchronizationManager.clearSynchronization(TransactionSynchronizationManager.java:331) TRACE - Clearing transaction synchronization
2015-12-07 10:11:13,308 org.springframework.transaction.support.TransactionSynchronizationManager.doUnbindResource(TransactionSynchronizationManager.java:243) TRACE - Removed value [org.springframework.jdbc.datasource.ConnectionHolder@49c7b90e] for key [org.nutz.dao.impl.SimpleDataSource@73700b80] from thread [main]
2015-12-07 10:11:13,313 org.nutz.dao.impl.sql.run.NutDaoExecutor._runSelect(NutDaoExecutor.java:193) DEBUG - SELECT COUNT(*) FROM t_user 
2015-12-07 10:11:13,313 org.springframework.context.support.AbstractApplicationContext.doClose(AbstractApplicationContext.java:1044) INFO  - Closing org.springframework.context.support.ClassPathXmlApplicationContext@75412c2f: startup date [Mon Dec 07 10:11:12 CST 2015]; root of context hierarchy
2015-12-07 10:11:13,314 org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroySingletons(DefaultSingletonBeanRegistry.java:444) INFO  - Destroying singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@2d8f65a4: defining beans [happy,springDaoRunner,ds,dao,transactionManager,org.springframework.aop.config.internalAutoProxyCreator,org.springframework.transaction.annotation.AnnotationTransactionAttributeSource#0,org.springframework.transaction.interceptor.TransactionInterceptor#0,org.springframework.transaction.config.internalTransactionAdvisor]; root of factory hierarchy

测试代码如下

beans.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
	xmlns:cache="http://www.springframework.org/schema/cache"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/cache 
        http://www.springframework.org/schema/cache/spring-cache.xsd
        http://www.springframework.org/schema/aop 
        http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
        http://www.springframework.org/schema/tx 
        http://www.springframework.org/schema/tx/spring-tx-3.2.xsd">
        
	<bean id="happy" class="org.nutz.j2cache.spring.NutzHappyService">
		<property name="dao" ref="dao"></property>
	</bean>

	<bean id="springDaoRunner" class="org.nutz.j2cache.spring.SpringDaoRunner" />
	<bean id="ds" class="org.nutz.dao.impl.SimpleDataSource">
		<property name="username" value="root" />
		<property name="password" value="root" />
		<property name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/nutzbook2" />
	</bean>
	<bean id="dao" class="org.nutz.dao.impl.NutDao">
		<constructor-arg>
			<ref bean="ds" />
		</constructor-arg>
		<property name="runner" ref="springDaoRunner"></property>
	</bean>
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="ds"></property>
	</bean>
	<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true" />
</beans>

testcase方法

package org.nutz.j2cache.spring;

import org.junit.Assert;
import org.junit.Test;
import org.nutz.dao.Dao;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringJ2CacheManagerTest extends Assert {

	@Test
	public void testSpringJ2CacheManager() {
		ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
		NutzHappyService happy = ctx.getBean("happy", NutzHappyService.class);
		
		Dao dao = ctx.getBean("dao", Dao.class);
		int count = dao.count("t_user");
		assertTrue(count > 0);
		try {
			happy.doUserClear();
		} catch (Exception e) {
			// 里面主动抛出异常
		}
		assertEquals(count, dao.count("t_user"));
		ctx.close();
	}

}

NutzHappyService 的源码

package org.nutz.j2cache.spring;

import org.nutz.dao.Dao;
import org.springframework.transaction.annotation.Transactional;

public class NutzHappyService {
	
	protected Dao dao;
	
	public void setDao(Dao dao) {
		this.dao = dao;
	}

	@Transactional(rollbackFor=Exception.class)
	public void doUserClear() {
		dao.clear("t_user");
		throw new RuntimeException();
	}
}

测试的点有:
1. NutzHappyService 有一个spring事务注解的方法,里面清除了t_user表,然后抛出了异常
2. 日志中显示, spring的事务进行了rollback, 可以看到rollback等字眼
3. 执行doUserClear前后的t_user表记录数相同,所以事务真的是回滚了

@wendal springDaoRunner 类和我的一样么

贴给你

package org.nutz.j2cache.spring;

import java.sql.Connection;

import javax.sql.DataSource;

import org.nutz.dao.ConnCallback;
import org.nutz.dao.impl.DaoRunner;
import org.springframework.jdbc.datasource.DataSourceUtils;

public class SpringDaoRunner implements DaoRunner {

	public void run(DataSource dataSource, ConnCallback callback) {
		Connection con = DataSourceUtils.getConnection(dataSource);
		try {
			callback.invoke(con);
		}
		catch (Exception e) {
			if (e instanceof RuntimeException)
				throw (RuntimeException) e;
			else
				throw new RuntimeException(e);
		}
		finally {
			DataSourceUtils.releaseConnection(con, dataSource);
		}
	}
}

我把代码放在这里,你clone下来试试吧(请无视项目名...)

http://git.oschina.net/wendal/j2cache-spring

@wendal 事务好使了,不是nutz的问题,是spring配置的问题
由子容器扫描装配了@Service 注解的实例。
spring的context是父子容器,由ServletContextListener 加载spring配置文件产生的是父容器,springMVC加载配置文件产生的是子容器,子容器对Controller进行扫描装配时装配了@Service注解的实例 (@Controller 实例依赖@Service实例),而该实例理应由父容器进行初始化以保证事务的增强处理,所以此时得到的将是原样的Service(没有经过事务加强处理,故而没有事务处理能力。

<!-- 不扫描带有@Controller注解的类 ,让 springMVC 子容器加载。
<context:component-scan base-package="com.lexing.platform">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>

添加回复
请先登陆
回到顶部