Spring事务
本小记中学习的目标
-
Spring的数据库编程
-
编程式事务管理
-
声明式事务管理
一、Spring的数据库编程
Spring框架提供了jdbcTemplate,来简化JDBC的编程。在实际应用中这个技术并不常用,常用的数据库编程是使用MyBatis、Hibernate框架进行编程。但在了解事务前我们先了解关于Spring的数据库编程。
Spring的数据库编程使用的是Spring JDBC模块的core和dataSource包。
core包:是JDBC的核心功能包,包含常用的JdbcTemplate类
dataSource包:访问数据源的工具包
如果需要使用Spring JDBC来操作数据库,需要配置对应的数据源,同时需要把dataSrouce注入到jdbcTemplate中
配置文件的信息大致如下:
<!-- 配置数据源dataSource -->
<bean class="org.springframework.jdbc.datasource.DriverManagerDataSource" id="dataSource">
<!-- 数据库驱动 -->
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<!-- url -->
<property name="url" value="jdbc:
mysql://localhost:3306/test?characterEncoding=utf8"/>
<!-- 用户名 -->
<property name="username" value="root"/>
<!-- 密码 -->
<property name="password" value="root"/>
</bean>
<!-- 配置jdbcTemplate,把dataSrouce注入 -->
<bean class="org.springframework.jdbc.core.JdbcTemplate" id="jdbcTemplate">
<property name="dataSource" ref="dataSrouce"、>
</bean>
注意:在上面配置中我们可以看到当配置jdbcTemplate时,需要把dataSrouce进行注入,在数据访问层(Dao)中如果需要使用jdbcTemplate时也需要把jdbcTemplate注入到对应的Bean中
Spring JdbcTemplate中的常用方法
public int update(String sql,Object args[])
这个方法可以对数据库表进行增加、修改、删除等操作
sql:表示要执行的sql语句,语句中的?表示待传入的参数
args[]:设置sql语句中的参数
返回值:影响的记录行数
public List<T> query(String sql,RowMapper<T> rowMapper,Object args[])
这个方法可以对数据表进行查询
rowMapper是用来把结果集映射到自定义的类中(注:这个时候类的属性要与数据表的字段对应)
实例
1.新增一个Maven的jar工程
2.在pom.xml中添加对对应的jar依赖
<
dependencies
>
<!-- 添加Spring核心依赖包 Context -->
<
dependency
>
<
groupId
>org.springframework
</
groupId
>
<
artifactId
>spring-context
</
artifactId
>
<
version
>5.0.2.RELEASE
</
version
>
</
dependency
>
<!-- commons-logging-1.2 -->
<
dependency
>
<
groupId
>commons-logging
</
groupId
>
<
artifactId
>commons-logging
</
artifactId
>
<
version
>1.2
</
version
>
</
dependency
>
<!-- spring-
jdbc
-->
<
dependency
>
<
groupId
>org.springframework
</
groupId
>
<
artifactId
>spring-
jdbc
</
artifactId
>
<
version
>5.0.2.RELEASE
</
version
>
</
dependency
>
<!--
mysql
-connector-java -->
<
dependency
>
<
groupId
>
mysql
</
groupId
>
<
artifactId
>
mysql-connector-java
</
artifactId
>
<
version
>5.1.45
</
version
>
</
dependency
>
</
dependencies
>
注意:我们不仅仅要依赖spring-jdbc的包,同时由于我们操作的是mysql的数据库我们也需要把mysql的相关jar包依赖进来
3.新增一个实体类com.xiaoxie.pojo.Student
package com.xiaoxie.pojo;
public
class Student {
private Integer
id;
private String
name;
private Integer
age;
//getter和setter方法
public Integer getId() {
return
id;
}
public
void setId(Integer
id) {
this.
id =
id;
}
public String getName() {
return
name;
}
public
void setName(String
name) {
this.
name =
name;
}
public Integer getAge() {
return
age;
}
public
void setAge(Integer
age) {
this.
age =
age;
}
public String toString() {
return
"Student[id="+
id+
",name="+
name+
",age="+
age+
"]";
}
}
注意:这个实例中的属性需要与数据库中的字段完全对应
4.新增一个dao接口及其实现类 com.xiaoxie.dao.StudentDao、com.xiaoxie.dao.impl.StudentDaoImpl
/*dao的接口*/
package com.xiaoxie.dao;
import java.util.List;
import com.xiaoxie.pojo.Student;
public
interface StudentDao {
int update(String
sql,Object[]
params);
List<Student> query(String
sql,Object[]
params);
}
/*dao的实现类*/
package com.xiaoxie.dao.impl;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Repository;
import com.xiaoxie.dao.StudentDao;
import com.xiaoxie.pojo.Student;
//交由Spring的容器管理,需要使用@Repository注解,在Spring的bean配置文件中配置扫描的包即可
@Repository(
"studentDao")
public
class StudentDaoImpl
implements StudentDao {
//需要进行自动注入
@Autowired
private JdbcTemplate
JdbcTemplate;
/*更新方法:添加、修改、删除*/
@Override
public
int update(String
sql, Object[]
params) {
return
JdbcTemplate.update(
sql,
params);
}
/*查询方法*/
@Override
public List<Student> query(String
sql, Object[]
params) {
RowMapper<Student>
studentRowMapper =
new BeanPropertyRowMapper<Student>(Student.
class);
return
JdbcTemplate.query(
sql,
studentRowMapper,
params);
}
}
注意:
1.我们使用注解的方式进行配置,这里实现类需要加上@Repository,并指定这个bean的id,这样便可以交由Spring容器进行处理
2.我们需要例JdbcTemplate中的方法,需要把JdbcTemplate类的对象注入进来,使用@Autowired来实现属性的自动注入
5.添加Spring的配置文件beans.xml
xsi:schemaLocation=
"
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd
"
>
<
context:component-scan
base-package=
"com.xiaoxie.dao"
/>
<!-- 配置数据源 -->
<
bean
class=
"org.springframework.jdbc.datasource.DriverManagerDataSource"
id=
"dataSource"
>
<!--
mysql
数据库驱动 -->
<
property
name=
"
driverClassName"
value=
"com.mysql.jdbc.Driver"
/>
<!--
url
-->
<
property
name=
"
url"
value=
"jdbc:
mysql://localhost:3306/test?characterEncoding=utf8
"
/>
<!--
username
-->
<
property
name=
"
username"
value=
"root"
/>
<!-- password -->
<
property
name=
"
password"
value=
"root"
/>
</
bean
>
<!-- 配置jdbcTemplate -->
<
bean
class=
"org.springframework.jdbc.core.JdbcTemplate"
id=
"jdbcTemplate"
>
<
property
name=
"
dataSource"
ref=
"dataSource"
/>
</
bean
>
</
beans
>
注意:这里的配置有两个点,第一、我们使用了注解,所以需要配置扫描的包;第二、使用JdbcTemplate时需要做数据源的配置
6.新增测试类com.xiaoxie.test.Test,在其中新增测试方法
private
static
void jdbcTemplateTest() {
//加载Spring容器配置
ApplicationContext
context =
new ClassPathXmlApplicationContext(
"beans.xml");
//获取对象
StudentDao
studentDao = (StudentDao)
context.getBean(
"studentDao");
System.
out.println(
"------------更新信息----------");
String
updateSql =
"update student set age = age + 3 where 1=1 and id = ?";
//为
sql
语句的添加对应的参数
Object
params[]= {12};
//执行更新语句
int
rownum =
studentDao.update(
updateSql,
params);
System.
out.println(
"执行更新完成!一共影响记录行数:" +
rownum);
System.
out.println(
"-------------查询信息----------");
String
querySql =
"Select id,name,age from student";
List<Student>
list =
studentDao.query(
querySql,
null);
System.
out.println(
"查询结果如下:");
for (Student
student :
list) {
System.
out.println(
student);
}
}
7.在main方法中调用新增的测试方法,运行后在控制台打印的结果如下:
------------更新信息----------
执行更新完成!一共影响记录行数:1
-------------查询信息----------
查询结果如下:
Student[id=1,name=刘备,age=30]
Student[id=2,name=关公,age=29]
Student[id=3,name=张飞,age=26]
Student[id=10,name=诸葛亮,age=25]
Student[id=11,name=赵云,age=23]
Student[id=12,name=黄忠,age=39]
二、编程式事务管理
什么是编程式的事务管理?编程式就是指的在代码中直接显示使用beginTransaction、commit、rollback等与事务相关的方法。这种编程式的事务管理一般来说只适合于仅有少数的事务操作时使用。
基于底层api的编程式事务管理
这里指的底层api是指的根据PlatformTransactionManager、TransactionDefinition、TransactionStatus这几个核心接口,通过编程的方式来进行事务处理。
实例,接着上面的实例进行扩展
1.在Spring的配置文件中,添加上事务的管理功能
<!-- 添加事务管理 -->
<
bean
class=
"org.springframework.jdbc.datasource.DataSourceTransactionManager"
id=
"dstManager"
>
<
property
name=
"
dataSource"
ref=
"dataSource"
/>
</
bean
>
2.改造StudentDao接口新增一个默认方法,改造后的接口类如下
package com.xiaoxie.dao;
import java.util.List;
import com.xiaoxie.pojo.Student;
public
interface StudentDao {
int update(String
sql,Object[]
params);
List<Student> query(String
sql,Object[]
params);
default String TransactionTest() {
return
"";
}
}
3.新增一个StudentDao的实现类com.xiaoxie.dao.impl.StudentDaoByTransaction
package com.xiaoxie.dao.impl;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import com.xiaoxie.dao.StudentDao;
import com.xiaoxie.pojo.Student;
@Repository(
"studentDaoByTransaction")
public
class StudentDaoByTransactionImpl
implements StudentDao {
//把jdbcTemplate对象自动注入
@Autowired
private JdbcTemplate
jdbcTemplate;
@Autowired
private DataSourceTransactionManager
dstManager;
@Override
public
int update(String
sql, Object[]
params) {
return
jdbcTemplate.update(
sql,
params);
}
@Override
public List<Student> query(String
sql, Object[]
params) {
RowMapper<Student>
studentRowMapper =
new BeanPropertyRowMapper<Student>(Student.
class);
return
jdbcTemplate.query(
sql,
studentRowMapper,
params);
}
/*
* 测试编程式事务
* 返回值为执行后返回的结果
*/
@Override
public String TransactionTest() {
//使用默认事务定义
DefaultTransactionDefinition
df =
new DefaultTransactionDefinition();
//开启事务
TransactionStatus
ts =
dstManager.getTransaction(
df);
String
message =
"执行成功!";
try {
//删除表中指定的数据
String
deleteSql =
"delete from student where 1=1 and id=?";
Object
params[] = {12};
jdbcTemplate.update(
deleteSql,
params);
//插入数据
String
insertSql =
"insert into student values(?,?,?)";
Object
params1[] = {13,
"黄忠",36};
jdbcTemplate.update(
insertSql,
params1);
//再执行一次同样的插入语句,此时由于主键要唯一性导致执行失败
jdbcTemplate.update(
insertSql,
params1);
//执行完后提交事务
dstManager.commit(
ts);
}
catch(Exception
e) {
//当出现异常时进行事务的回滚
dstManager.rollback(
ts);
message =
"执行失败,事务回滚! " +
e.getMessage();
//
e.printStackTrace();
}
return
message;
}
}
注意:在这个实现类中我们新增一个方法,这个方法中我们使用到了事务,需要把事务管理的对象注入进来,也就是把第一步中我们配置的对象注入进来
4.在测试类中新增测试方法
private
static
void jdbcTemplateByTransactionTest() {
//加载Spring容器配置
ApplicationContext
context =
new ClassPathXmlApplicationContext(
"beans.xml");
//获取对象
StudentDao
studentDao = (StudentDao)
context.getBean(
"studentDaoByTransaction");
String
message =
studentDao.TransactionTest();
System.
out.println(
message);
}
5.在测试类的main方法中调用上述的方法,运行后控制台打印结果如下
执行失败,事务回滚! PreparedStatementCallback; SQL [insert into student values(?,?,?)Duplicate entry '13' for key 'PRIMARY'; nested exception is com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry '13' for key 'PRIMARY'
基于TransactionTemplate的编程式事务管理
上面基于底层API的事务管理,有个明显的缺点,在业务方法与数据库进行交互时需要编写事务相关的代码,多个业务方法则需要多次进行事务相关代码的添加。这样使得事务处理的代码会分散在各个业务方法中。使得代码散乱不够清晰。
TransactionTemplate的execuute方法有一个TransactionCallback接口类型的参数,这个接口中定义了doInTransaction方法。
通常来说使用匿名内部类的方式来实现这个接口,并在doInTransaction方法中书写对应的业务逻辑代码。在这个方法中可以使用默认的事务提交和回滚规则,在业务代码中是不需要调用任务的事务API相关的方法。doInTransaction方法有一个TransactionStatus类型的参数,可以在方法的任何位置调用这的SetRollbackOnly方法将事务标识为回滚,以执行事务回滚。
默认的事务规则如下:
当执行回调方法的过程中抛出未检查异常,或者显式调用了setRollbackOnly方法,则回滚事务
当执行完成或者抛出Checked类型的异常,则提交事务
实例:接着上面的实例进行扩展
1.在spring的配置文件中为事务管理器添加事务模板
<!-- 为事务管理器添加transactionTemplate -->
<
bean
class=
"org.springframework.transaction.support.TransactionTemplate"
id=
"transactionTemplate"
>
<
property
name=
"
transactionManager"
ref=
"dstManager"
/>
</
bean
>
2.新增一个StudentDao的实现类com.xiaoxie.dao.impl.StudentDaoByTransactionImpl
package com.xiaoxie.dao.impl;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;
import com.xiaoxie.dao.StudentDao;
import com.xiaoxie.pojo.Student;
@Repository(
"StudentTransactionTemplateDao")
public
class StudentDaoByTransactionTemplateImpl
implements StudentDao {
//自动注入JdbcTemplate对象
@Autowired
private JdbcTemplate
jdbcTemplate;
//自动注入TransactionTemplate对象
@Autowired
private TransactionTemplate
transactionTemplate;
//执行结果返回信息
String
message=
"执行成功!";
@Override
public
int update(String
sql, Object[]
params) {
return
jdbcTemplate.update(
sql,
params);
}
@Override
public List<Student> query(String
sql, Object[]
params) {
RowMapper<Student>
rowMapper =
new BeanPropertyRowMapper<Student>(Student.
class);
return
jdbcTemplate.query(
sql,
params,
rowMapper);
}
@Override
public String TransactionTest() {
//实现TransactionCallback接口
transactionTemplate.execute(
new TransactionCallback<Object>() {
@Override
public Object doInTransaction(TransactionStatus
arg0) {
try {
//删除数据
String
deleteSql =
"delete from student where 1=1 and id=?";
Object
params[] = {12};
jdbcTemplate.update(
deleteSql,
params);
//添加数据
String
insertSql =
"insert into student values(?,?,?)";
Object
params1[] = {12,
"黄忠",36};
jdbcTemplate.update(
insertSql,
params1);
//重复添加数据
jdbcTemplate.update(
insertSql,
params1);
}
catch(Exception
e) {
message =
"执行失败,事务回滚! " +
e.getMessage();
arg0.setRollbackOnly(); //指定回滚操作
}
return
message;
}
});
return
message;
}
}
注意:在TransactionCallback的接口中有默认的事务规则,在实现方法doInTransaction中编写对应的业务逻辑代码即可,关于事务的一些供都在模板中提供好了
3.在测试类中新增方法
private
static
void jdbcTemplateByTransactionTemplateTest() {
//加载Spring容器配置
ApplicationContext
context =
new ClassPathXmlApplicationContext(
"beans.xml");
//获取对象
StudentDao
studentDao = (StudentDao)
context.getBean(
"StudentTransactionTemplateDao");
String
message =
studentDao.TransactionTest();
System.
out.println(
message);
}
4.在测试类的main方法中调用上述新增的方法,运行后在控制台打印的结果如下
执行失败,事务回滚! PreparedStatementCallback; SQL [insert into student values(?,?,?)Duplicate entry '13' for key 'PRIMARY'; nested exception is com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry '13' for key 'PRIMARY'
三、声明式事务管理
声明式事务管理,Spring通过AOP的方式来实现的事务管理,它的原理就是在方法的执行前后进行拦截,在目标方法执行前开启事务,在执行完目标方法后再根据执行的情况提交或者回滚事务。
使用声明式事务管理,则完全可以把事务的管理与业务逻辑代码解耦出来,不需要在业务逻辑代码中加入任务事务的处理代码,只需要做相关的事务声明即可。
声明式事务管理对于事务的处理粒度只能到方法级别无法到代码块的级别,如果对代码块需要做声明式事务变通式的处理方式是把代码块剥离为对应的方法,再做事务的声明处理。
Spring中声明式事务管理有两种实现方式,一是使用xml配置文件的方式;另一种是使用@Transactional注解
基于xml配置文件的方式
实例:接着上面的实例进行扩展
1.新增一个StudentDao接口的实现类com.xiaoie.dao.impl.StudentDaoByXmlTransactionImpl
package com.xiaoxie.dao.impl;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Repository;
import com.xiaoxie.dao.StudentDao;
import com.xiaoxie.pojo.Student;
@Repository(
"studentDaoXmlTransaction")
public
class StudentDaoByXmlTransactionImpl
implements StudentDao{
@Autowired
private JdbcTemplate
jdbcTemplate;
@Override
public
int update(String
sql, Object[]
params) {
return
jdbcTemplate.update(
sql,
params);
}
@Override
public List<Student> query(String
sql, Object[]
params) {
RowMapper<Student>
rowMapper =
new BeanPropertyRowMapper<Student>(Student.
class);
return
jdbcTemplate.query(
sql,
rowMapper,
params);
}
@Override
public String TransactionTest() {
String
message =
"执行完成!";
try {
//删除数据
String
deleteSql =
"delete from student where 1=1 and id=?";
Object
params[] = {12};
jdbcTemplate.update(
deleteSql,
params);
//添加数据
String
insertSql =
"insert into student values(?,?,?)";
Object
params1[] = {13,
"黄忠",36};
jdbcTemplate.update(
insertSql,
params1);
//重复添加数据
jdbcTemplate.update(
insertSql,
params1);
}
catch(Exception
e) {
//throw new RuntimeException(); //这里要抛出RuntimeException才可以使用事务回滚
//手动让事务回滚
TransactionAspectSupport.
currentTransactionStatus().setRollbackOnly();
message=
"执行失败,事务回滚!" +
e.getMessage();
}
return
message;
}
2.在spring的配置文件中新增如下内容,使用通知添加事务管理,同时使用AOP的方式,配置切入点并与通知进行关联
<!-- 使用通知添加事务管理 -->
<
tx:advice
transaction-manager=
"dstManager"
id=
"myAdvice"
>
<
tx:attributes
>
<!-- 这里的*表示任何方法 -->
<
tx:method
name=
"*"
/>
</
tx:attributes
>
</
tx:advice
>
<!-- AOP,让spring为目标对象自动生成代理 -->
<
aop:config
>
<!-- 定义切入点 -->
<
aop:pointcut
expression=
"execution(* com.xiaoie.dao.impl.*.*(..))"
id=
"txPointCut"
/>
<!-- 把切入点与通知关联 -->
<
aop:advisor
advice-ref=
"myAdvice"
pointcut-ref=
"txPointCut"
/>
</
aop:config
>
注意:在使用这个时需要做两步操作:第一,需要在pom中添加aop相关的依赖jar包,第二,需要使用tx与aop的命名空间(Eclipse中在配置文件的Namespaces中勾选tx和aop)
3.在测试类中新增方法
private
static
void jdbcTemplateByXmlTransactionTest() {
//加载Spring容器配置
ApplicationContext
context =
new ClassPathXmlApplicationContext(
"beans.xml");
//获取对象
StudentDao
studentDao = (StudentDao)
context.getBean(
"studentDaoXmlTransaction");
String
message =
studentDao.TransactionTest();
System.
out.println(
message);
}
4.在main方法中调用上面的方法,运行测试类控制台打印结果如下
执行失败,事务回滚!PreparedStatementCallback; SQL [insert into student values(?,?,?)Duplicate entry '13' for key 'PRIMARY'; nested exception is com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry '13' for key 'PRIMARY'
基于@Transactional注解
@Transactional注解可以用于接口、接口方法、类、类方法之上,当用于类上的时候,则这个类对应的甩的public方法都会具有事务属性,在类上声明后,也可以在指定的方法上使用这个注解来覆盖类级别的定义。一般来说是不建议在接口和接口方法上使用这个注解。
在使用这个注解时如果不想对某个异常进行事务处理,可以使用如下注解声明
@Transactional(rollbackFor=Exception.class) //对RuntimeException回滚生效
实例:继续上面的实例进行扩展
1.在Spring的配置文件中添加对于事务管理器添加注解驱动器
<!-- 为事务管理添加注解驱动器 -->
<
tx:annotation-driven
transaction-manager
=
"dstManager"
/>
2.新增一个dao实现类com.xiaoxie.dao.impl.StudentDaoByAnnoationImpl,在其中添加注解@Transactional
package com.xiaoxie.dao.impl;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import com.xiaoxie.dao.StudentDao;
import com.xiaoxie.pojo.Student;
@Transactional(rollbackFor=Exception.
class) //使用Exception异常也可以回滚事务
@Repository(
"studentDaoByAnnotation")
public
class StudentDaoByAnnotationImpl
implements StudentDao {
@Autowired
JdbcTemplate
jdbcTemplate;
@Override
public
int update(String
sql, Object[]
params) {
return
jdbcTemplate.update(
sql,
params);
}
@Override
public List<Student> query(String
sql, Object[]
params) {
RowMapper<Student>
rowMapper =
new BeanPropertyRowMapper<Student>(Student.
class);
return
jdbcTemplate.query(
sql,
params,
rowMapper);
}
@Override
public String TransactionTest() {
String
message =
"执行成功!";
try {
//删除表中指定的数据
String
deleteSql =
"delete from student where 1=1 and id=?";
Object
params[] = {12};
jdbcTemplate.update(
deleteSql,
params);
//插入数据
String
insertSql =
"insert into student values(?,?,?)";
Object
params1[] = {13,
"黄忠",36};
jdbcTemplate.update(
insertSql,
params1);
//再执行一次同样的插入语句,此时由于主键要唯一性导致执行失败
jdbcTemplate.update(
insertSql,
params1);
//执行完后提交事务
}
catch(Exception
e) {
throw e;
//
message = "执行失败,事务回滚! " + e.getMessage();
// TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();//手动执行回滚
}
return
message;
}
}
3.在测试类中新增方法
private
static
void jdbcTemplateByAnnotationTransactionTest() {
//加载Spring容器配置
ApplicationContext
context =
new ClassPathXmlApplicationContext(
"beans.xml");
//获取对象
StudentDao
studentDao = (StudentDao)
context.getBean(
"studentDaoByAnnotation");
String
message =
"";
try {
message =
studentDao.TransactionTest();
}
catch (Exception
e) {
message =
"执行失败!事务回滚!" +
e.getMessage();
}
System.
out.println(
message);
}
4.在main方法中调用这个新增的方法,运行后在控制台打印结果如下
执行失败,事务回滚! PreparedStatementCallback; SQL [insert into student values(?,?,?)Duplicate entry '13' for key 'PRIMARY'; nested exception is com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry '13' for key 'PRIMARY'
这里关于声明式事务有如下需要说明的:
1.如果我们处理了try...catch了Exception异常,没有抛出RuntimeException异常则事务是不会回滚的
2.在事务中我们如果需要抛出Exception也可以回滚事务则需要在@Transactional后指定rollbackFor=Exceptoin.class
3.一定要抛出异常Spring才会做事务的回滚,如果对事务处理了则不会回滚
4.可以不抛出异常而使用手工的回滚事务:TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();//手动执行回滚
5.对于第4点当使用注解的方式时直接在catch到异常后调用手工回滚会报 Transaction rolled back because it has been marked as rollback-only异常,后以上述处理的时候是在catch时直接抛出异常,再没测试类中的对应方法上捕获对应的异常做处理(这里有些不明白)