上篇文章寫到了編程式事務的使用TransactionTemplate類的實現,TransactionTemplate類里的execute方法需要TransactionCallback接口實現類做參數,其接口的定義中只包含一個doInTransaction方法用于執行事務操作,上面的代碼中使用匿名類的方式定義TransactionCallback接口實現,并且在doInTransaction()方法中定義事務處理代碼。
第五章 聲明式事務管理
我們使用最多的還是Spring聲明式事務管理,其實所有Spring事務管理都是基于AOP來實現,而其中的聲明式事務才是真正體現AOP全部優點的最佳應用,記得我以前參加面試一說到AOP我就提及聲明式事務管理,哎,那個時候對AOP的理解也就僅限于此,太菜了。Spring聲明式事務不涉及組件的依賴關系,使用它時候不需要編寫任何代碼,很大程度的節省了工作量,提高了工作效率。下面我在我的框架里面加入聲明式事務,首先修改applicationContext.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:context
="http://www.springframework.org/schema/context"
xmlns:aop
="http://www.springframework.org/schema/aop"
xmlns:tx
="http://www.springframework.org/schema/tx"
xsi:schemaLocation
="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd"
>
<!--
掃描該路徑下的spring組件
-->
<
context:component-scan
base-package
="cn.com.sharpxiajun"
/>
<!--
讀取資源文件
-->
<
bean
id
="propertyConfigurer"
class
="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
>
<
property
name
="locations"
>
<
list
>
<
value
>
classpath:conf/constants.properties
</
value
>
</
list
>
</
property
>
</
bean
>
<!--
配置數據源
-->
<!--
<bean id="myDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${db.driverClass}"/>
<property name="jdbcUrl" value="${db.jdbcUrl}"/>
<property name="user" value="${db.user}"/>
<property name="password" value="${db.password}"/>
</bean>
-->
<
bean
id
="myDataSource"
class
="org.apache.commons.dbcp.BasicDataSource"
destroy-method
="close"
>
<
property
name
="driverClassName"
value
="${db.driverClass}"
/>
<
property
name
="url"
value
="${db.jdbcUrl}"
/>
<
property
name
="username"
value
="${db.user}"
/>
<
property
name
="password"
value
="${db.password}"
/>
</
bean
>
<
bean
id
="sqlMapClient"
class
="org.springframework.orm.ibatis.SqlMapClientFactoryBean"
>
<
property
name
="configLocation"
>
<
value
>
classpath:conf/SqlMapConfig.xml
</
value
>
</
property
>
<
property
name
="dataSource"
ref
="myDataSource"
/>
</
bean
>
<
bean
id
="sqlMapClientTemplate"
class
="org.springframework.orm.ibatis.SqlMapClientTemplate"
>
<
property
name
="sqlMapClient"
>
<
ref
local
="sqlMapClient"
/>
</
property
>
</
bean
>
<
bean
id
="transactionManager"
class
="org.springframework.jdbc.datasource.DataSourceTransactionManager"
>
<
property
name
="dataSource"
>
<
ref
local
="myDataSource"
/>
</
property
>
</
bean
>
<!--
聲明式事務
-->
<
tx:advice
id
="txAdvice"
transaction-manager
="transactionManager"
>
<
tx:attributes
>
<
tx:method
name
="get*"
read-only
="true"
propagation
="REQUIRED"
/>
<
tx:method
name
="query*"
read-only
="true"
propagation
="REQUIRED"
/>
<
tx:method
name
="find*"
read-only
="true"
propagation
="REQUIRED"
/>
<
tx:method
name
="list*"
read-only
="true"
propagation
="REQUIRED"
/>
<
tx:method
name
="search*"
read-only
="true"
propagation
="REQUIRED"
/>
<
tx:method
name
="add*"
propagation
="REQUIRED"
isolation
="READ_COMMITTED"
/>
<
tx:method
name
="insert*"
propagation
="REQUIRED"
isolation
="READ_COMMITTED"
/>
<
tx:method
name
="del*"
propagation
="REQUIRED"
isolation
="READ_COMMITTED"
/>
<
tx:method
name
="save*"
propagation
="REQUIRED"
isolation
="READ_COMMITTED"
/>
<
tx:method
name
="update*"
propagation
="REQUIRED"
isolation
="READ_COMMITTED"
/>
<
tx:method
name
="modify*"
propagation
="REQUIRED"
isolation
="READ_COMMITTED"
/>
</
tx:attributes
>
</
tx:advice
>
<
bean
id
="transactionTemplate"
class
="org.springframework.transaction.support.TransactionTemplate"
>
<
property
name
="transactionManager"
>
<
ref
bean
="transactionManager"
/>
</
property
>
<
property
name
="propagationBehaviorName"
>
<
value
>
PROPAGATION_REQUIRED
</
value
>
</
property
>
</
bean
>
<!--
將我自己定義的攔截器生成bean
-->
<
bean
id
="methodServiceAdvisor"
class
="cn.com.sharpxiajun.common.aop.MethodServiceAdvisor"
/>
<
aop:config
>
<!--
配置規則,滿足以下規則的將攔截,第一個*表示所有返回類型,第二個表示service包下的所有class,第三個表示所有方法
-->
<
aop:pointcut
id
="baseServiceMethods"
expression
="execution(* cn.com.sharpxiajun.service.*.*(..))"
/>
<!--
聲明式事務
-->
<
aop:advisor
advice-ref
="txAdvice"
pointcut-ref
="baseServiceMethods"
/>
<!--
符合上面規則的攔截器都會調用到methodServiceAdvisor
-->
<
aop:advisor
advice-ref
="methodServiceAdvisor"
pointcut-ref
="baseServiceMethods"
/>
</
aop:config
>
</
beans
>
這里面添加了<tx:advice id="txAdvice" transaction-manager="transactionManager">,根據方法前綴不同綁定不同的事務策略,然后在<aop:config>里面添加<aop:advisor advice-ref="txAdvice" pointcut-ref="baseServiceMethods" />,那么service包下面類里的方法都會綁定相應的事務管理。在javaEE工程里面到底是DAO層綁定事務還是在Service層綁定事務?這個問題以前有同事問過我,我當時回答是都應該綁定,現在我會說最好只在Service層綁定事務。DAO層在我的理解里應該是數據庫操作的映射,那是針對數據的原子操作,而Service層則是把這些各種數據庫操作封裝成一個業務操作單元,所以Service層的含義更符合事務本質,所以事務最好綁定在Service層。DAO層再綁定事務沒必要,而且多余,甚至還會操作不必要的錯誤。
接下來我修改USERS.xml映射文件,代碼如下:
<?
xml version="1.0" encoding="UTF-8"
?>
<!
DOCTYPE sqlMap PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN" "http://ibatis.apache.org/dtd/sql-map-2.dtd"
>
<
sqlMap
namespace
="USERS"
>
<
select
id
="queryUserList"
parameterClass
="java.util.Map"
resultClass
="java.util.HashMap"
>
select t.username,t.password,t.enabled from users t
</
select
>
<!--
新增用戶信息
-->
<
insert
id
="addUsers"
parameterClass
="java.util.Map"
>
insert into users
<
dynamic
prepend
="("
>
<
isNotNull
prepend
=","
property
="username"
>
username
</
isNotNull
>
<
isNotNull
prepend
=","
property
="password"
>
password
</
isNotNull
>
<
isNotNull
prepend
=","
property
="enabled"
>
enabled
</
isNotNull
>
)
</
dynamic
>
values
<
dynamic
prepend
="("
>
<
isNotNull
prepend
=","
property
="username"
>
#username:VARCHAR#
</
isNotNull
>
<
isNotNull
prepend
=","
property
="password"
>
#password:VARCHAR#
</
isNotNull
>
<
isNotNull
prepend
=","
property
="enabled"
>
#enabled:NUMBER#
</
isNotNull
>
)
</
dynamic
>
</
insert
>
</
sqlMap
>
修改cn.com.sharpxiajun.dao包下的接口UsersDao,代碼如下:
package
cn.com.sharpxiajun.dao;
import
java.util.List;
import
java.util.Map;
public
interface
UsersDao {
public
static
final
String QUERY_USERS_SQL = "USERS.queryUserList";
public
static
final
String ADD_USERS_SQL = "USERS.addUsers";
public
List<Map<String, Object>> queryUserList(Map<String, Object> map)
throws
Exception;
public
Object addUsers(Map<String, Object> map)
throws
Exception;
}
修改cn.com.sharpxiajun.dao.impl包下面UsersDaoImpl類,代碼如下:
package
cn.com.sharpxiajun.dao.impl;
import
java.util.List;
import
java.util.Map;
import
org.springframework.beans.factory.annotation.Autowired;
import
org.springframework.beans.factory.annotation.Qualifier;
import
org.springframework.context.annotation.Scope;
import
org.springframework.orm.ibatis.SqlMapClientTemplate;
import
org.springframework.stereotype.Repository;
import
cn.com.sharpxiajun.dao.UsersDao;
@SuppressWarnings("unchecked")
@Scope("prototype")
@Repository("usersDao")
public
class
UsersDaoImpl
implements
UsersDao {
@Autowired
@Qualifier("sqlMapClientTemplate")
private
SqlMapClientTemplate sqlMapClientTemplate =
null
;
public
List<Map<String, Object>> queryUserList(Map<String, Object> map)
throws
Exception {
return
sqlMapClientTemplate.queryForList(QUERY_USERS_SQL, map);
}
@Override
public
Object addUsers(Map<String, Object> map)
throws
Exception {
return
sqlMapClientTemplate.insert(ADD_USERS_SQL, map);
}
}
修改cn.com.sharpxiajun.service包下接口UsersService,代碼如下:
package
cn.com.sharpxiajun.service;
import
java.util.List;
import
java.util.Map;
public
interface
UsersService {
public
List<Map<String, Object>> queryUsersList(Map<String, Object> map)
throws
Exception;
public
Object addUsers(Map<String, Object> map)
throws
Exception;
}
修改cn.com.sharpxiajun.service.impl包下類UsersServiceImpl,代碼如下:
package
cn.com.sharpxiajun.service.impl;
import
java.util.List;
import
java.util.Map;
import
org.springframework.beans.factory.annotation.Autowired;
import
org.springframework.beans.factory.annotation.Qualifier;
import
org.springframework.context.annotation.Scope;
import
org.springframework.stereotype.Service;
import
cn.com.sharpxiajun.dao.UsersDao;
import
cn.com.sharpxiajun.service.UsersService;
@SuppressWarnings("unchecked")
@Scope("prototype")
@Service("userService")
public
class
UsersServiceImpl
implements
UsersService {
@Autowired
@Qualifier("usersDao")
private
UsersDao usersDao =
null
;
@Override
public
List<Map<String, Object>> queryUsersList(Map<String, Object> map)
throws
Exception {
return
usersDao.queryUserList(map);
}
@Override
public
Object addUsers(Map<String, Object> map)
throws
Exception {
return
usersDao.addUsers(map);
}
}
修改cn.com.sharpxiajun.junittest.service包下測試類UsersServiceImplTest,代碼如下:
package
cn.com.sharpxiajun.junittest.service;
import
java.util.HashMap;
import
java.util.List;
import
java.util.Map;
import
org.apache.commons.logging.Log;
import
org.apache.commons.logging.LogFactory;
import
org.junit.After;
import
org.junit.Before;
import
org.junit.Test;
import
org.junit.runner.RunWith;
import
org.springframework.beans.factory.annotation.Autowired;
import
org.springframework.test.context.ContextConfiguration;
import
org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests;
import
org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import
org.springframework.test.context.transaction.TransactionConfiguration;
import
cn.com.sharpxiajun.service.UsersService;
import
cn.com.sharpxiajun.service.impl.UsersServiceImpl;
@RunWith(SpringJUnit4ClassRunner.
class
)
@ContextConfiguration(locations={"classpath:conf/applicationContext.xml"})
@TransactionConfiguration(defaultRollback =
false
)
public
class
UsersServiceImplTest
extends
AbstractTransactionalJUnit4SpringContextTests {
private
final
static
Log log = LogFactory.getLog(UsersServiceImpl.
class
);
@Autowired
private
UsersService usersService =
null
;
public
UsersServiceImplTest()
{
System.out.println("初始化測試類....");
}
@Before
public
void
setUp()
throws
Exception
{
System.out.println("測試開始....");
}
@After
public
void
tearDown()
throws
Exception
{
System.out.println("測試結束!!");
}
@Test
public
void
testQueryUserList()
{
Map<String, Object> map =
new
HashMap<String, Object>();
map.put("username", "sharpxiajun");
try
{
List<Map<String, Object>> list = usersService.queryUsersList(map);
log.info(list);
}
catch
(Exception e) {
e.printStackTrace();
}
}
@Test
public
void
testAddUsers()
{
Map<String, Object> map =
new
HashMap<String, Object>();
map.put("username", "xiajun");
map.put("password", "xiajun");
map.put("enabled", 1);
try
{
Object obj = usersService.addUsers(map);
log.info("testAddUsers:" + obj);
}
catch
(Exception e) {
e.printStackTrace();
}
}
}
運行測試類,結果如下:
數據新增了,但是沒有體現事務的作用,我前面寫事務示例代碼也沒有考慮到如何表現事務的作用,為了體現事務作用,我在USERS.xml配置文件里添加新方法,代碼如下:
<!--
修改用戶信息
-->
<
update
id
="updateUsers"
parameterClass
="java.util.Map"
>
update users set errinfo = 'error'
</
update
>
修改cn.com.sharpxiajun.dao包下的UsersDao接口,代碼如下:
package
cn.com.sharpxiajun.dao;
import
java.util.List;
import
java.util.Map;
public
interface
UsersDao {
public
static
final
String QUERY_USERS_SQL = "USERS.queryUserList";
public
static
final
String ADD_USERS_SQL = "USERS.addUsers";
public
static
final
String UPDATE_USERS_SQL = "USERS.updateUsers";
public
List<Map<String, Object>> queryUserList(Map<String, Object> map)
throws
Exception;
public
Object addUsers(Map<String, Object> map)
throws
Exception;
public
Object updateUsers(Map<String, Object> map)
throws
Exception;
}
修改cn.com.sharpxiajun.dao.impl包下的UsersDaoImpl類,代碼如下:
package
cn.com.sharpxiajun.dao.impl;
import
java.util.List;
import
java.util.Map;
import
org.springframework.beans.factory.annotation.Autowired;
import
org.springframework.beans.factory.annotation.Qualifier;
import
org.springframework.context.annotation.Scope;
import
org.springframework.orm.ibatis.SqlMapClientTemplate;
import
org.springframework.stereotype.Repository;
import
cn.com.sharpxiajun.dao.UsersDao;
@SuppressWarnings("unchecked")
@Scope("prototype")
@Repository("usersDao")
public
class
UsersDaoImpl
implements
UsersDao {
@Autowired
@Qualifier("sqlMapClientTemplate")
private
SqlMapClientTemplate sqlMapClientTemplate =
null
;
public
List<Map<String, Object>> queryUserList(Map<String, Object> map)
throws
Exception {
return
sqlMapClientTemplate.queryForList(QUERY_USERS_SQL, map);
}
@Override
public
Object addUsers(Map<String, Object> map)
throws
Exception {
return
sqlMapClientTemplate.insert(ADD_USERS_SQL, map);
}
@Override
public
Object updateUsers(Map<String, Object> map)
throws
Exception {
return
sqlMapClientTemplate.update(UPDATE_USERS_SQL, map);
}
}
在cn.com.sharpxiajun.service包的UsersService接口里添加方法:
public
void
updateUsers(Map<String, Object> map)
throws
Exception;
在cn.com.sharpxiajun.service.impl下的UsersServiceImpl類里實現該方法,代碼如下:
@Override
public
void
updateUsers(Map<String, Object> map)
throws
Exception {
usersDao.addUsers(map);
usersDao.updateUsers(map);
}
從上面代碼可以看出USERS.xml的update方法是不能被執行,程序一定會報出異常,如果方法綁定了事務,那么usersDao.addUsers(map);操作會被回滾掉,也就是新增操作不會成功,下面我在cn.com.sharpxiajun.junittest.service包下的UsersServiceImplTest類里添加新的測試方法,代碼如下:
@Test
public
void
testUpdateUsers()
{
Map<String, Object> map =
new
HashMap<String, Object>();
map.put("username", "sharp");
map.put("password", "sharp");
map.put("enabled", 1);
try
{
usersService.updateUsers(map);
}
catch
(Exception e) {
e.printStackTrace();
}
}
(注意:最好把測試新增方法的代碼注釋掉)
運行測試類,控制臺會打印出下面異常:
2011-10-25 23:10:59 SQLErrorCodesFactory - SQLErrorCodes loaded: [DB2, Derby, H2, HSQL, Informix, MS-SQL, MySQL, Oracle, PostgreSQL, Sybase]
org.springframework.jdbc.BadSqlGrammarException: SqlMapClient operation; bad SQL grammar []; nested exception is com.ibatis.common.jdbc.exception.NestedSQLException:
--- The error occurred in cn/com/sharpxiajun/dao/sqlmap/USERS.xml.
--- The error occurred
while
applying a parameter map.
--- Check the USERS.updateUsers-InlineParameterMap.
--- Check the statement (update failed).
--- Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column 'errinfo' in 'field list'
at org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator.doTranslate(SQLErrorCodeSQLExceptionTranslator.java:233)
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:72)
at org.springframework.orm.ibatis.SqlMapClientTemplate.execute(SqlMapClientTemplate.java:203)
at org.springframework.orm.ibatis.SqlMapClientTemplate.update(SqlMapClientTemplate.java:378)
at cn.com.sharpxiajun.dao.impl.UsersDaoImpl.updateUsers(UsersDaoImpl.java:36)
at cn.com.sharpxiajun.service.impl.UsersServiceImpl.updateUsers(UsersServiceImpl.java:45)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:309)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:183)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
at cn.com.sharpxiajun.common.aop.MethodServiceAdvisor.invoke(MethodServiceAdvisor.java:31)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:110)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:89)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
at $Proxy18.updateUsers(Unknown Source)
at cn.com.sharpxiajun.junittest.service.UsersServiceImplTest.testUpdateUsers(UsersServiceImplTest.java:103)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:74)
at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:31)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:82)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:240)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:180)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:49)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
Caused by: com.ibatis.common.jdbc.exception.NestedSQLException:
--- The error occurred in cn/com/sharpxiajun/dao/sqlmap/USERS.xml.
--- The error occurred
while
applying a parameter map.
--- Check the USERS.updateUsers-InlineParameterMap.
--- Check the statement (update failed).
--- Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column 'errinfo' in 'field list'
at com.ibatis.sqlmap.engine.mapping.statement.MappedStatement.executeUpdate(MappedStatement.java:107)
at com.ibatis.sqlmap.engine.impl.SqlMapExecutorDelegate.update(SqlMapExecutorDelegate.java:457)
at com.ibatis.sqlmap.engine.impl.SqlMapSessionImpl.update(SqlMapSessionImpl.java:90)
at org.springframework.orm.ibatis.SqlMapClientTemplate$9.doInSqlMapClient(SqlMapClientTemplate.java:380)
at org.springframework.orm.ibatis.SqlMapClientTemplate$9.doInSqlMapClient(SqlMapClientTemplate.java:1)
at org.springframework.orm.ibatis.SqlMapClientTemplate.execute(SqlMapClientTemplate.java:200)
... 49 more
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column 'errinfo' in 'field list'
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
at com.mysql.jdbc.Util.handleNewInstance(Util.java:406)
at com.mysql.jdbc.Util.getInstance(Util.java:381)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1031)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:957)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3376)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3308)
at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:1837)
at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:1961)
at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2543)
at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:1737)
at com.mysql.jdbc.PreparedStatement.execute(PreparedStatement.java:998)
at org.apache.commons.dbcp.DelegatingPreparedStatement.execute(DelegatingPreparedStatement.java:169)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at com.ibatis.common.jdbc.logging.PreparedStatementLogProxy.invoke(PreparedStatementLogProxy.java:62)
at $Proxy21.execute(Unknown Source)
at com.ibatis.sqlmap.engine.execution.SqlExecutor.executeUpdate(SqlExecutor.java:80)
at com.ibatis.sqlmap.engine.mapping.statement.MappedStatement.sqlExecuteUpdate(MappedStatement.java:216)
at com.ibatis.sqlmap.engine.mapping.statement.MappedStatement.executeUpdate(MappedStatement.java:94)
... 54 more
測試結束!!
2011-10-25 23:10:59 TestContextManager - Caught exception
while
allowing TestExecutionListener [org.springframework.test.context.transaction.TransactionalTestExecutionListener@6798eb] to process 'after' execution
for
test: method [
public
void
cn.com.sharpxiajun.junittest.service.UsersServiceImplTest.testUpdateUsers()], instance [cn.com.sharpxiajun.junittest.service.UsersServiceImplTest@168989e], exception [
null
]
org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:717)
at org.springframework.test.context.transaction.TransactionalTestExecutionListener$TransactionContext.endTransaction(TransactionalTestExecutionListener.java:516)
at org.springframework.test.context.transaction.TransactionalTestExecutionListener.endTransaction(TransactionalTestExecutionListener.java:291)
at org.springframework.test.context.transaction.TransactionalTestExecutionListener.afterTestMethod(TransactionalTestExecutionListener.java:184)
at org.springframework.test.context.TestContextManager.afterTestMethod(TestContextManager.java:406)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:90)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:240)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:180)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:49)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
結果如下:
沒有新的記錄被添加,事務被成功綁定了~~~。
那么如果我們寫在Service里面的方法沒有用applicationContext.xml里面設置的前綴,效果會如何了?
我在cn.com.sharpxiajun.service包的UsersService接口下添加新方法,代碼如下:
public
void
noTransactionMethod(Map<String, Object> map)
throws
Exception;
在cn.com.sharpxiajun.service.impl包里UsersServiceImpl類實現該方法,代碼如下:
@Override
public
void
noTransactionMethod(Map<String, Object> map)
throws
Exception {
usersDao.addUsers(map);
usersDao.updateUsers(map);
}
最后在cn.com.sharpxiajun.junittest.service包下,修改UsersServiceImplTest測試類,代碼如下:
//
@Test
//
public void testAddUsers()
//
{
//
Map<String, Object> map = new HashMap<String, Object>();
//
map.put("username", "xiajun");
//
map.put("password", "xiajun");
//
map.put("enabled", 1);
//
//
try {
//
Object obj = usersService.addUsers(map);
//
log.info("testAddUsers:" + obj);
//
} catch (Exception e) {
//
e.printStackTrace();
//
}
//
}
@Test
public
void
testNoTransactionMethod()
{
Map<String, Object> map =
new
HashMap<String, Object>();
map.put("username", "sharp");
map.put("password", "sharp");
map.put("enabled", 1);
try
{
usersService.noTransactionMethod(map);
}
catch
(Exception e) {
e.printStackTrace();
}
}
//
@Test
//
public void testUpdateUsers()
//
{
//
Map<String, Object> map = new HashMap<String, Object>();
//
map.put("username", "sharp");
//
map.put("password", "sharp");
//
map.put("enabled", 1);
//
//
try {
//
usersService.updateUsers(map);
//
} catch (Exception e) {
//
e.printStackTrace();
//
}
//
}
運行測試類,控制臺打印出的異常是:
2011
-
10
-
25
23
:
28
:
00
SQLErrorCodesFactory - SQLErrorCodes loaded: [DB2, Derby, H2, HSQL, Informix, MS-SQL, MySQL, Oracle, PostgreSQL, Sybase]
org.springframework.jdbc.BadSqlGrammarException: SqlMapClient operation; bad SQL grammar []; nested exception
is
com.ibatis.common.jdbc.exception.NestedSQLException:
--- The error occurred
in
cn/com/sharpxiajun/dao/sqlmap/USERS.xml.
--- The error occurred
while
applying a parameter map.
--- Check the USERS.updateUsers-InlineParameterMap.
--- Check the statement (update failed).
--- Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column
'
errinfo
'
in
'
field list
'
at org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator.doTranslate(SQLErrorCodeSQLExceptionTranslator.java:
233
)
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:
72
)
at org.springframework.orm.ibatis.SqlMapClientTemplate.execute(SqlMapClientTemplate.java:
203
)
at org.springframework.orm.ibatis.SqlMapClientTemplate.update(SqlMapClientTemplate.java:
378
)
at cn.com.sharpxiajun.dao.impl.UsersDaoImpl.updateUsers(UsersDaoImpl.java:
36
)
at cn.com.sharpxiajun.service.impl.UsersServiceImpl.noTransactionMethod(UsersServiceImpl.java:
38
)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:
39
)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:
25
)
at java.lang.reflect.Method.invoke(Method.java:
597
)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:
309
)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:
183
)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:
150
)
at cn.com.sharpxiajun.common.aop.MethodServiceAdvisor.invoke(MethodServiceAdvisor.java:
31
)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:
172
)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:
110
)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:
172
)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:
89
)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:
172
)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:
202
)
at $Proxy18.noTransactionMethod(Unknown Source)
at cn.com.sharpxiajun.junittest.service.UsersServiceImplTest.testNoTransactionMethod(UsersServiceImplTest.java:
88
)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:
39
)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:
25
)
at java.lang.reflect.Method.invoke(Method.java:
597
)
at org.junit.runners.model.FrameworkMethod$
1
.runReflectiveCall(FrameworkMethod.java:
44
)
at org.junit.
internal
.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:
15
)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:
41
)
at org.junit.
internal
.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:
20
)
at org.junit.
internal
.runners.statements.RunBefores.evaluate(RunBefores.java:
28
)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:
74
)
at org.junit.
internal
.runners.statements.RunAfters.evaluate(RunAfters.java:
31
)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:
82
)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:
72
)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:
240
)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:
50
)
at org.junit.runners.ParentRunner$
3
.run(ParentRunner.java:
193
)
at org.junit.runners.ParentRunner$
1
.schedule(ParentRunner.java:
52
)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:
191
)
at org.junit.runners.ParentRunner.access$
000
(ParentRunner.java:
42
)
at org.junit.runners.ParentRunner$
2
.evaluate(ParentRunner.java:
184
)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:
61
)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:
70
)
at org.junit.runners.ParentRunner.run(ParentRunner.java:
236
)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:
180
)
at org.eclipse.jdt.
internal
.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:
49
)
at org.eclipse.jdt.
internal
.junit.runner.TestExecution.run(TestExecution.java:
38
)
at org.eclipse.jdt.
internal
.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:
467
)
at org.eclipse.jdt.
internal
.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:
683
)
at org.eclipse.jdt.
internal
.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:
390
)
at org.eclipse.jdt.
internal
.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:
197
)
Caused by: com.ibatis.common.jdbc.exception.NestedSQLException:
--- The error occurred
in
cn/com/sharpxiajun/dao/sqlmap/USERS.xml.
--- The error occurred
while
applying a parameter map.
--- Check the USERS.updateUsers-InlineParameterMap.
--- Check the statement (update failed).
--- Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column
'
errinfo
'
in
'
field list
'
at com.ibatis.sqlmap.engine.mapping.statement.MappedStatement.executeUpdate(MappedStatement.java:
107
)
at com.ibatis.sqlmap.engine.impl.SqlMapExecutorDelegate.update(SqlMapExecutorDelegate.java:
457
)
at com.ibatis.sqlmap.engine.impl.SqlMapSessionImpl.update(SqlMapSessionImpl.java:
90
)
at org.springframework.orm.ibatis.SqlMapClientTemplate$
9
.doInSqlMapClient(SqlMapClientTemplate.java:
380
)
at org.springframework.orm.ibatis.SqlMapClientTemplate$
9
.doInSqlMapClient(SqlMapClientTemplate.java:
1
)
at org.springframework.orm.ibatis.SqlMapClientTemplate.execute(SqlMapClientTemplate.java:
200
)
...
49
more
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column
'
errinfo
'
in
'
field list
'
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:
39
)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:
27
)
at java.lang.reflect.Constructor.newInstance(Constructor.java:
513
)
at com.mysql.jdbc.Util.handleNewInstance(Util.java:
406
)
at com.mysql.jdbc.Util.getInstance(Util.java:
381
)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:
1031
)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:
957
)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:
3376
)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:
3308
)
at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:
1837
)
at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:
1961
)
at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:
2543
)
at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:
1737
)
at com.mysql.jdbc.PreparedStatement.execute(PreparedStatement.java:
998
)
at org.apache.commons.dbcp.DelegatingPreparedStatement.execute(DelegatingPreparedStatement.java:
169
)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:
39
)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:
25
)
at java.lang.reflect.Method.invoke(Method.java:
597
)
at com.ibatis.common.jdbc.logging.PreparedStatementLogProxy.invoke(PreparedStatementLogProxy.java:
62
)
at $Proxy21.execute(Unknown Source)
at com.ibatis.sqlmap.engine.execution.SqlExecutor.executeUpdate(SqlExecutor.java:
80
)
at com.ibatis.sqlmap.engine.mapping.statement.MappedStatement.sqlExecuteUpdate(MappedStatement.java:
216
)
at com.ibatis.sqlmap.engine.mapping.statement.MappedStatement.executeUpdate(MappedStatement.java:
94
)
...
54
more
我們發現這里面少了事務回滾的異常,數據庫查詢結果如下:
有記錄被新增了~~~
第六章 通過注解綁定事務管理
聲明式事務咋看一下還是十分簡單的,但是Spring引入注解后還有更加簡單的配置,請看修改后新的applicationContext.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:context
="http://www.springframework.org/schema/context"
xmlns:aop
="http://www.springframework.org/schema/aop"
xmlns:tx
="http://www.springframework.org/schema/tx"
xsi:schemaLocation
="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd"
>
<!--
掃描該路徑下的spring組件
-->
<
context:component-scan
base-package
="cn.com.sharpxiajun"
/>
<!--
讀取資源文件
-->
<
bean
id
="propertyConfigurer"
class
="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
>
<
property
name
="locations"
>
<
list
>
<
value
>
classpath:conf/constants.properties
</
value
>
</
list
>
</
property
>
</
bean
>
<!--
配置數據源
-->
<!--
<bean id="myDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${db.driverClass}"/>
<property name="jdbcUrl" value="${db.jdbcUrl}"/>
<property name="user" value="${db.user}"/>
<property name="password" value="${db.password}"/>
</bean>
-->
<
bean
id
="myDataSource"
class
="org.apache.commons.dbcp.BasicDataSource"
destroy-method
="close"
>
<
property
name
="driverClassName"
value
="${db.driverClass}"
/>
<
property
name
="url"
value
="${db.jdbcUrl}"
/>
<
property
name
="username"
value
="${db.user}"
/>
<
property
name
="password"
value
="${db.password}"
/>
</
bean
>
<
bean
id
="sqlMapClient"
class
="org.springframework.orm.ibatis.SqlMapClientFactoryBean"
>
<
property
name
="configLocation"
>
<
value
>
classpath:conf/SqlMapConfig.xml
</
value
>
</
property
>
<
property
name
="dataSource"
ref
="myDataSource"
/>
</
bean
>
<
bean
id
="sqlMapClientTemplate"
class
="org.springframework.orm.ibatis.SqlMapClientTemplate"
>
<
property
name
="sqlMapClient"
>
<
ref
local
="sqlMapClient"
/>
</
property
>
</
bean
>
<
bean
id
="transactionManager"
class
="org.springframework.jdbc.datasource.DataSourceTransactionManager"
>
<
property
name
="dataSource"
>
<
ref
local
="myDataSource"
/>
</
property
>
</
bean
>
<!--
聲明式事務
-->
<!--
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="get*" read-only="true" propagation="REQUIRED"/>
<tx:method name="query*" read-only="true" propagation="REQUIRED"/>
<tx:method name="find*" read-only="true" propagation="REQUIRED"/>
<tx:method name="list*" read-only="true" propagation="REQUIRED"/>
<tx:method name="search*" read-only="true" propagation="REQUIRED"/>
<tx:method name="add*" propagation="REQUIRED" isolation="READ_COMMITTED"/>
<tx:method name="insert*" propagation="REQUIRED" isolation="READ_COMMITTED"/>
<tx:method name="del*" propagation="REQUIRED" isolation="READ_COMMITTED"/>
<tx:method name="save*" propagation="REQUIRED" isolation="READ_COMMITTED"/>
<tx:method name="update*" propagation="REQUIRED" isolation="READ_COMMITTED"/>
<tx:method name="modify*" propagation="REQUIRED" isolation="READ_COMMITTED"/>
</tx:attributes>
</tx:advice>
-->
<
bean
id
="transactionTemplate"
class
="org.springframework.transaction.support.TransactionTemplate"
>
<
property
name
="transactionManager"
>
<
ref
bean
="transactionManager"
/>
</
property
>
<
property
name
="propagationBehaviorName"
>
<
value
>
PROPAGATION_REQUIRED
</
value
>
</
property
>
</
bean
>
<!--
將我自己定義的攔截器生成bean
-->
<
bean
id
="methodServiceAdvisor"
class
="cn.com.sharpxiajun.common.aop.MethodServiceAdvisor"
/>
<
aop:config
>
<!--
配置規則,滿足以下規則的將攔截,第一個*表示所有返回類型,第二個表示service包下的所有class,第三個表示所有方法
-->
<
aop:pointcut
id
="baseServiceMethods"
expression
="execution(* cn.com.sharpxiajun.service.*.*(..))"
/>
<!--
聲明式事務
-->
<!--
<aop:advisor advice-ref="txAdvice" pointcut-ref="baseServiceMethods" />
-->
<!--
符合上面規則的攔截器都會調用到methodServiceAdvisor
-->
<
aop:advisor
advice-ref
="methodServiceAdvisor"
pointcut-ref
="baseServiceMethods"
/>
</
aop:config
>
<
tx:annotation-driven
transaction-manager
="transactionManager"
/>
</
beans
>
配置文件里我把原來的聲明式配置內容注釋掉了,然后加入了<tx:annotation-driven transaction-manager="transactionManager"/>。
然后在cn.com.sharpxiajun.service.impl包下的UsersServiceImpl類加入@Transactional注解,代碼如下:
@SuppressWarnings("unchecked")
@Scope("prototype")
@Transactional
@Service("userService")
public
class
UsersServiceImpl
implements
UsersService {
......................................................
將數據庫里的數據都清除掉,運行測試類UsersServiceImplTest,測試service下的noTransactionMethod方法和updateUsers方法,結果顯示數據都沒有新增成功,這就說明事務綁定是成功了,這個簡單吧。
總結下了:事務很重要,我希望我這兩篇博客能給童鞋們一點幫助。
下一篇文章將把struts2框架引入到我寫的框架。
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061
微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元

