标签:事务管理 Spring Java开发 AOP JDBC
Spring有一个基于AOP之上的事务管理模块,这个模块能够帮助我们在逻辑层中很方便的控制数据库的事务。在此之前我们介绍了Spring对JDBC的模板支持 以及 面向切面的Spring,而且也用Spring的AOP编写了一个简单的切面类用于控制事务,在此对其中一些相同的东西就不再赘述了。所以本文是硬文,就让我们单刀直入地学习如何使用Spring的事务管理模块为我们管理事务吧。首先配置依赖的jar包,我这里使用的是maven工程,所以配置pom.xml文件内容如下:
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.14.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.3.12.RELEASE</version>
</dependency>
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.44</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.13</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
</dependency>
</dependencies>
然后就是Spring配置文件,内容如下:
<?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:p="http://www.springframework.org/schema/p"
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.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
">
<!-- 配置Spring支持注解 -->
<context:annotation-config/>
<context:component-scan base-package="org.zero01"/>
<!-- 配置Spring支持AOP -->
<aop:aspectj-autoproxy/>
<!-- 配置Spring支持注解形式的事务管理 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
<!-- 配置Spring的事务管理员 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" p:dataSource-ref="dataSource"/>
<!-- 配置数据源 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
p:driverClass="com.mysql.jdbc.Driver"
p:jdbcUrl="jdbc:mysql:///school"
p:user="root"
p:password="your_password"
p:maxPoolSize="10"
p:minPoolSize="1"
p:loginTimeout="2000"
/>
</beans>
完成以上配置后,我们就可以使用Spring的事务管理了,最简单的使用方式就是在需要被管理的类里写上 @Transactional 注解即可,下面我们来测试一下配置是否正常:
逻辑层接口:
package org.zero01.service;
import org.zero01.pojo.Student;
import java.util.List;
public interface School {
public int enterSchool(Student student);
public int deleteStudentData(int sid);
public Student searchStudentData(int sid);
public List<Student> searchStudentsData();
public int alterStudentData(Student student);
}
写一个测试的实现类,用于测试 @Transactional 注解是否能成功进行事务管理的配置:
package org.zero01.service;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.zero01.pojo.Student;
import java.util.List;
@Service("test")
// 配置这个注解后,该类就受到Spring的事务管理了
@Transactional
public class TestService implements School {
public int enterSchool(Student student) {
return 0;
}
public int deleteStudentData(int sid) {
return 0;
}
public Student searchStudentData(int sid) {
return null;
}
public List<Student> searchStudentsData() {
return null;
}
public int alterStudentData(Student student) {
return 0;
}
}
测试代码如下:
package org.zero01.test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.zero01.service.School;
public class TestTran {
public static void main(String[] args) {
ApplicationContext app = new ClassPathXmlApplicationContext("app.xml");
School testService = (School) app.getBean("test");
// 查看是否是代理对象
System.out.println(testService.getClass().getName());
}
}
运行结果:
com.sun.proxy.$Proxy12
从打印结果中可以看到,拿出来的是代理对象,那就代表配置成功了,因为事务管理是基于AOP的而Spring的AOP又是基于动态代理的,拿到的是代理对象就证明已经代理上了。
以上使用到的 @Transactional 注解不仅可以写在类名上,还可以写在方法上,写在类上表示控制整个类,写在方法上表示只控制某个方法。因为有时候有些方法比较特殊,希望有不一样的管理方式,这就涉及到以下要提到的Spring的7种事务传播行为类型了,使用不同的事务传播行为类型能够进行不同的事务控制,下面就简单介绍一下这7种事务传播行为类型的使用方式:
@Service("test")
// 配置这个注解后,该类就受到Spring的事务管理了
@Transactional
public class TestService implements School {
@Transactional(propagation = Propagation.REQUIRED)
public int enterSchool(Student student) {
return 0;
}
...
@Service("test")
// 配置这个注解后,该类就受到Spring的事务管理了
@Transactional
public class TestService implements School {
@Transactional(propagation = Propagation.SUPPORTS)
public int enterSchool(Student student) {
return 0;
}
...
@Service("test")
// 配置这个注解后,该类就受到Spring的事务管理了
@Transactional
public class TestService implements School {
@Transactional(propagation = Propagation.MANDATORY)
public int enterSchool(Student student) {
return 0;
}
...
@Service("test")
// 配置这个注解后,该类就受到Spring的事务管理了
@Transactional
public class TestService implements School {
@Transactional(propagation = Propagation.REQUIRES_NEW)
public int enterSchool(Student student) {
return 0;
}
...
@Service("test")
// 配置这个注解后,该类就受到Spring的事务管理了
@Transactional
public class TestService implements School {
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public int enterSchool(Student student) {
return 0;
}
...
@Service("test")
// 配置这个注解后,该类就受到Spring的事务管理了
@Transactional
public class TestService implements School {
@Transactional(propagation = Propagation.NEVER)
public int enterSchool(Student student) {
return 0;
}
...
@Service("test")
// 配置这个注解后,该类就受到Spring的事务管理了
@Transactional
public class TestService implements School {
@Transactional(propagation = Propagation.NESTED)
public int enterSchool(Student student) {
return 0;
}
...
以上所介绍的这个类型相比于其他六中类型有些特殊,因为它可以进行事务嵌套,当嵌套事务后就分为一级事务和二级事务了,二级事务是一级事务的子事务。我们来举个例子吧,例如我现在有一个需求:
像这种类型的需求就可以使用嵌套事务类型,我们可以把从小明账户里减去1000金币,往小红账户里增加1000金币这一环节作为一级事务。而从小明的三个账户里任意一个账户里减少金币和从小红的两个账户里任意一个账户增加金币作为二级事务。
说明一下嵌套事务的回滚与提交:
把以上提到的类型整理成表格,Spring的7种事务传播行为类型: |
事务传播行为类型 | 说明 |
---|---|---|
PROPAGATION_REQUIRED | 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。 | |
PROPAGATION_SUPPORTS | 支持当前事务,如果当前没有事务,就以非事务方式执行。 | |
PROPAGATION_MANDATORY | 使用当前的事务,如果当前没有事务,就抛出异常。 | |
PROPAGATION_REQUIRES_NEW | 新建事务,如果当前存在事务,把当前事务挂起。 | |
PROPAGATION_NOT_SUPPORTED | 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 | |
PROPAGATION_NEVER | 以非事务方式执行,如果当前存在事务,则抛出异常。 | |
PROPAGATION_NESTED | 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类 似的操作。 |
除了以上所介绍的事务传播行为类型之外,在Spring的事务管理中也支持设置事务隔离级别,事务隔离级别也是在 @Transactional 注解中进行配置,下面简单介绍一下它们的配置方式:
@Service("test")
// 配置这个注解后,该类就受到Spring的事务管理了
@Transactional
public class TestService implements School {
@Transactional(isolation = Isolation.DEFAULT)
public int enterSchool(Student student) {
return 0;
}
...
@Service("test")
// 配置这个注解后,该类就受到Spring的事务管理了
@Transactional
public class TestService implements School {
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
public int enterSchool(Student student) {
return 0;
}
...
@Service("test")
// 配置这个注解后,该类就受到Spring的事务管理了
@Transactional
public class TestService implements School {
@Transactional(isolation = Isolation.READ_COMMITTED)
public int enterSchool(Student student) {
return 0;
}
...
ISOLATION_REPEATABLE_READ,这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻像读。它除了保证一个事务不能读取另一个事务未提交的数据外,还保证了避免下面的情况产生(不可重复读)。示例:
@Service("test")
// 配置这个注解后,该类就受到Spring的事务管理了
@Transactional
public class TestService implements School {
@Transactional(isolation = Isolation.REPEATABLE_READ)
public int enterSchool(Student student) {
return 0;
}
...
ISOLATION_SERIALIZABLE,这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行。除了防止脏读,不可重复读外,还避免了幻读。示例:
@Service("test")
// 配置这个注解后,该类就受到Spring的事务管理了
@Transactional
public class TestService implements School {
@Transactional(isolation = Isolation.SERIALIZABLE)
public int enterSchool(Student student) {
return 0;
}
...
把以上提到的事务隔离级别整理成表格,Spring中的事务隔离级别: |
隔离级别 | 说明 |
---|---|---|
ISOLATION_DEFAULT | 使用数据库默认的事务隔离级别 | |
ISOLATION_READ_UNCOMMITTED | 这是事务最低的隔离级别,它充许令外一个事务可以看到这个事务未提交的数据。这种隔离级别会产生脏读,不可重复读和幻读。 | |
ISOLATION_READ_COMMITTED | 保证一个事务修改的数据提交后才能被另外一个事务读取。另外一个事务不能读取该事务未提交的数据 | |
ISOLATION_REPEATABLE_READ | 这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻像读。它除了保证一个事务不能读取另一个事务未提交的数据外,还保证了避免下面的情况产生(不可重复读)。 | |
ISOLATION_SERIALIZABLE | 这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行。除了防止脏读,不可重复读外,还避免了幻读。 |
除了以上最重要的事务传播行为类型以及事务隔离级别的配置之外,@Transactional 还有其他几个属性,下面介绍最后剩下的几个属性:
@Service("test")
// 配置这个注解后,该类就受到Spring的事务管理了
@Transactional
public class TestService implements School {
// 开启只读模式
@Transactional(readOnly = true)
public int enterSchool(Student student) {
return 0;
}
...
@Service("test")
// 配置这个注解后,该类就受到Spring的事务管理了
@Transactional
public class TestService implements School {
// 设置事务超时时间为2秒
@Transactional(timeout = 2)
public int enterSchool(Student student) {
return 0;
}
...
@Service("test")
// 配置这个注解后,该类就受到Spring的事务管理了
@Transactional
public class TestService implements School {
// 配置单一异常类
@Transactional(rollbackFor = NullPointerException.class)
public int enterSchool(Student student) {
return 0;
}
// 配置多个异常类
@Transactional(rollbackFor = {NullPointerException.class, IndexOutOfBoundsException.class})
public int deleteStudentData(int sid) {
return 0;
}
...
@Service("test")
// 配置这个注解后,该类就受到Spring的事务管理了
@Transactional
public class TestService implements School {
// 配置单一异常类
@Transactional(rollbackForClassName = "RuntimeException")
public int enterSchool(Student student) {
return 0;
}
// 配置多个异常类
@Transactional(rollbackForClassName = {"RuntimeException", "Exception"})
public int deleteStudentData(int sid) {
return 0;
}
...
@Service("test")
// 配置这个注解后,该类就受到Spring的事务管理了
@Transactional
public class TestService implements School {
// 配置单一异常类
@Transactional(noRollbackFor = NullPointerException.class)
public int enterSchool(Student student) {
return 0;
}
// 配置多个异常类
@Transactional(noRollbackFor = {NullPointerException.class, IndexOutOfBoundsException.class})
public int deleteStudentData(int sid) {
return 0;
}
...
@Service("test")
// 配置这个注解后,该类就受到Spring的事务管理了
@Transactional
public class TestService implements School {
// 配置单一异常类
@Transactional(noRollbackForClassName = "RuntimeException")
public int enterSchool(Student student) {
return 0;
}
// 配置多个异常类
@Transactional(noRollbackForClassName = {"RuntimeException", "Exception"})
public int deleteStudentData(int sid) {
return 0;
}
以上我们就把 @Transactional 注解里所有的属性都介绍完了,把 @Transactional 注解的属性以及功能整理成表格如下:
参数名称 | 功能描述 |
---|---|
readOnly | 该属性用于设置当前事务是否为只读事务,设置为true表示只读,false则表示可读写,默认值为false。 |
rollbackFor | 该属性用于设置需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,则进行事务回滚。 |
rollbackForClassName | 该属性用于设置需要进行回滚的异常类名称数组,当方法中抛出指定异常名称数组中的异常时,则进行事务回滚。 |
noRollbackFor | 该属性用于设置不需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,不进行事务回滚。 |
noRollbackForClassName | 该属性用于设置不需要进行回滚的异常类名称数组,当方法中抛出指定异常名称数组中的异常时,不进行事务回滚。 |
propagation | 该属性用于设置事务的传播行为类型 |
isolation | 该属性用于设置底层数据库的事务隔离级别,事务隔离级别用于处理多事务并发的情况,通常使用数据库的默认隔离级别即可,通常情况下不需要进行设置 |
timeout | 该属性用于设置事务的超时秒数,默认值为-1表示永不超时 |
使用Spring的事务管理时注意的几点:
用 spring 事务管理器,由spring来负责数据库的打开,提交,回滚。默认遇到运行期例外 throw new RuntimeException();
会回滚,即遇到不受检查(unchecked)的例外时回滚;而遇到需要捕获的例外 throw new Exception();
不会回滚,即遇到受检查的例外时。所谓受检查的例外就是非运行时抛出的异常,编译器会检查到的异常叫受检查例外或说受检查异常。如下:
@Service("test")
@Transactional
public class TestService implements School {
// 由于是非运行时抛出的异常,所以不会回滚
public int enterSchool(Student student) {
throw new Exception();
}
// 运行时抛出的异常,则会回滚
public int deleteStudentData(int sid) {
throw new RuntimeException();
}
...
在这种情况下需要我们指定方式来让事务回滚,想要所有异常都回滚,可以加上 @Transactional( rollbackFor={Exception.class,其它异常})
。如果想要发生非运行时异常不回滚,则加上: @Transactional(notRollbackFor=RunTimeException.class)
如下示例:
// 指定回滚,遇到异常Exception时回滚
@Transactional(rollbackFor = Exception.class)
public int enterSchool(Student student) {
throw new Exception();
}
// 指定不回滚,即便遇到运行期例外(运行时异常)也不会回滚
@Transactional(noRollbackFor = Exception.class)
public int deleteStudentData(int sid) {
throw new RuntimeException();
}
@Transactional 注解应该只被应用到 public 可见度的方法上。 如果你在 protected、private 或者 package-visible 的方法上使用 @Transactional 注解,它也不会报错, 但是这个被注解的方法将不会展示已配置的事务设置。
@Transactional 注解可以被应用于接口定义和接口方法、类定义和类的 public 方法上。然而,请注意仅仅 @Transactional 注解的出现不足于开启事务行为,它仅仅 是一种元数据,能够被可以识别 @Transactional 注解和上述的配置适当的具有事务行为的beans所使用。上面的例子中,其实正是 元素的出现 开启 了事务行为。
以上我们已经介绍完了Spring基本的事务管理,下面我们来将之前基于AOP编写的代码重新使用JdbcTemplate + Spring的事务管理进行改造:
表格字段的封装类如下:
package org.zero01.pojo;
import org.springframework.stereotype.Component;
@Component("stu")
public class Student {
private int sid;
private String sname;
private int age;
private String sex;
private String address;
public int getSid() {
return sid;
}
public void setSid(int sid) {
this.sid = sid;
}
public String getSname() {
return sname;
}
public void setSname(String sname) {
this.sname = sname;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
package org.zero01.pojo;
import java.util.Date;
public class StudentLog {
private int log_id;
private int sid;
private String sname;
private int age;
private String sex;
private String address;
private String operation_type;
private Date log_time;
public int getLog_id() {
return log_id;
}
public void setLog_id(int log_id) {
this.log_id = log_id;
}
public int getSid() {
return sid;
}
public void setSid(int sid) {
this.sid = sid;
}
public String getSname() {
return sname;
}
public void setSname(String sname) {
this.sname = sname;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getOperation_type() {
return operation_type;
}
public void setOperation_type(String operation_type) {
this.operation_type = operation_type;
}
public Date getLog_time() {
return log_time;
}
public void setLog_time(Date log_time) {
this.log_time = log_time;
}
}
package org.zero01.dao;
import org.zero01.pojo.Student;
import java.util.List;
public interface DAO {
public int insert(Student student);
public int delete(int sid);
public Student selectById(int sid);
public List<Student> selectAll();
public int update(Student student);
}
package org.zero01.dao;
import org.zero01.pojo.StudentLog;
import java.util.List;
public interface LogDAO {
public int insert(StudentLog studentLog);
public int delete(int log_id);
public List<StudentLog> selectAll();
public int update(StudentLog studentLog);
}
package org.zero01.service;
import org.zero01.pojo.Student;
import java.util.List;
public interface School {
public int enterSchool(Student student);
public int deleteStudentData(int sid);
public Student searchStudentData(int sid);
public List<Student> searchStudentsData();
public int alterStudentData(Student student);
}
package org.zero01.dao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import org.zero01.pojo.Student;
import javax.sql.DataSource;
import java.util.List;
@Repository("stuDAO")
public class StudentDAO extends JdbcTemplate implements DAO {
@Autowired
public void setDataSource(DataSource dataSource) {
super.setDataSource(dataSource);
}
/**
* @Description: 添加学生数据
* @Param: 表格的字段封装对象
* @return: 返回插入行的id
* @Author: 01
* @Date: 2018/3/6
*/
public int insert(Student student) {
String sql = "INSERT INTO student(sname,age,sex,address) VALUES (?,?,?,?)";
update(sql, student.getSname(), student.getAge(), student.getSex(), student.getAddress());
int sid = queryForObject("SELECT LAST_INSERT_ID()", Integer.class);
return sid;
}
/**
* @Description: 删除某个学生数据
* @Param: 要删除行的id
* @return: 返回影响的行数
* @Author: 01
* @Date: 2018/3/6
*/
public int delete(int sid) {
String sql = "DELETE FROM student WHERE sid=?";
return update(sql, sid);
}
/**
* @Description: 按id查找某个学生的数据
* @Param: 要查询行的id
* @return: 返回查询出来的学生数据
* @Author: 01
* @Date: 2018/3/6
*/
public Student selectById(int sid) {
String sql = "SELECT * FROM student WHERE sid=?";
return queryForObject(sql, new Object[]{sid}, new StudentMapper());
}
/**
* @Description: 查询全部学生的数据
* @return: 返回查询出来的数据集合
* @Author: 01
* @Date: 2018/3/6
*/
public List<Student> selectAll() {
String sql = "SELECT * FROM student";
return query(sql, new StudentMapper());
}
/**
* @Description: 修改某个学生的数据
* @Param: 表格的字段封装对象
* @return: 返回影响行数
* @Author: 01
* @Date: 2018/3/6
*/
public int update(Student student) {
String sql = "UPDATE student SET sname=?,age=?,sex=?,address=? WHERE sid=?";
return update(sql, student.getSname(), student.getAge(), student.getSex(), student.getAddress(), student.getSid());
}
}
package org.zero01.dao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import org.zero01.pojo.StudentLog;
import javax.sql.DataSource;
import java.util.List;
@Repository("stuLogDAO")
public class StudentLogDAO extends JdbcTemplate implements LogDAO {
@Autowired
public void setDataSource(DataSource dataSource) {
super.setDataSource(dataSource);
}
/**
* @Description: 添加日志记录
* @Param: 表格的字段封装对象
* @return: 返回影响行数
* @Author: 01
* @Date: 2018/3/6
*/
public int insert(StudentLog studentLog) {
String sql;
if (studentLog.getOperation_type().equals("selectAll")) {
sql = "INSERT INTO studentlog(operation_type,log_time) VALUES (‘selectAll‘,sysdate())";
return update(sql);
}
sql = "INSERT INTO studentlog(sid,sname,age,sex,address,operation_type,log_time) VALUES (?,?,?,?,?,?,sysdate())";
return update(sql, studentLog.getSid(), studentLog.getSname(), studentLog.getAge(),
studentLog.getSex(), studentLog.getAddress(), studentLog.getOperation_type());
}
/**
* @Description: 删除日志记录
* @Param: 要删除行的id
* @return: 返回影响行数
* @Author: 01
* @Date: 2018/3/6
*/
public int delete(int log_id) {
String sql = "DELETE FROM studentlog WHERE log_id=?";
return update(sql, log_id);
}
/**
* @Description: 查询全部日志记录
* @return: 返回查询出来的数据集合
* @Author: 01
* @Date: 2018/3/6
*/
public List<StudentLog> selectAll() {
String sql = "SELECT * FROM studentlog";
return query(sql, new StudentLogMapper());
}
/**
* @Description: 修改某条日志记录
* @Param: 表格的字段封装对象
* @return: 返回影响行数
* @Author: 01
* @Date: 2018/3/6
*/
public int update(StudentLog studentLog) {
String sql = "UPDATE student SET sid=?,sname=?,age=?,sex=?,address=?,operation_type=?,log_time=? WHERE log_id=?";
return update(sql, studentLog.getSid(), studentLog.getSname(), studentLog.getAge(), studentLog.getSex(),
studentLog.getAddress(), studentLog.getOperation_type(), studentLog.getLog_time(), studentLog.getLog_id());
}
}
package org.zero01.dao;
import org.springframework.jdbc.core.RowMapper;
import org.zero01.pojo.Student;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* @program: spring-transaction
* @description: 配置student表格字段与Student对象属性的映射
* @author: 01
* @create: 2018-03-06 16:36
**/
public class StudentMapper implements RowMapper<Student> {
public Student mapRow(ResultSet resultSet, int i) throws SQLException {
Student student = new Student();
student.setSid(resultSet.getInt("sid"));
student.setSname(resultSet.getString("sname"));
student.setAge(resultSet.getInt("age"));
student.setSex(resultSet.getString("sex"));
student.setAddress(resultSet.getString("address"));
return student;
}
}
package org.zero01.dao;
import org.springframework.jdbc.core.RowMapper;
import org.zero01.pojo.StudentLog;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* @program: spring-transaction
* @description: 配置studentlog表格字段与StudentLog对象属性的映射
* @author: 01
* @create: 2018-03-06 16:32
**/
public class StudentLogMapper implements RowMapper<StudentLog> {
public StudentLog mapRow(ResultSet resultSet, int i) throws SQLException {
StudentLog studentLog = new StudentLog();
studentLog.setLog_id(resultSet.getInt("log_id"));
studentLog.setSid(resultSet.getInt("sid"));
studentLog.setSname(resultSet.getString("sname"));
studentLog.setAge(resultSet.getInt("age"));
studentLog.setSex(resultSet.getString("sex"));
studentLog.setAddress(resultSet.getString("address"));
studentLog.setOperation_type(resultSet.getString("operation_type"));
studentLog.setLog_time(resultSet.getTimestamp("log_time"));
return studentLog;
}
}
package org.zero01.service;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.zero01.dao.DAO;
import org.zero01.dao.LogDAO;
import org.zero01.pojo.Student;
import org.zero01.pojo.StudentLog;
import java.util.List;
@Service("schoolService")
// 配置这个注解后,该类就受到Spring的事务管理了
@Transactional
public class SchoolService implements School{
@Autowired
private DAO dao;
@Autowired
private LogDAO logDAO;
/**
* @Description: 映射两张表格中相同的字段
* @Author: 01
* @Date: 2018/3/6
*/
public StudentLog stuMap(Student student, String operation_type) {
StudentLog studentLog = new StudentLog();
if (student != null) {
studentLog.setSid(student.getSid());
studentLog.setSname(student.getSname());
studentLog.setAge(student.getAge());
studentLog.setSex(student.getSex());
studentLog.setAddress(student.getAddress());
}
studentLog.setOperation_type(operation_type);
return studentLog;
}
/**
* @Description: 入学
* @Param:
* @return:
* @Author: 01
* @Date: 2018/3/6
*/
public int enterSchool(Student student) {
int sid = dao.insert(student);
student.setSid(sid);
return logDAO.insert(stuMap(student, "add"));
}
/**
* @Description: 删除学生数据
* @Param:
* @return:
* @Author: 01
* @Date: 2018/3/6
*/
public int deleteStudentData(int sid) {
Student student = dao.selectById(sid);
if (student != null) {
student.setSid(sid);
dao.delete(sid);
} else {
return 0;
}
return logDAO.insert(stuMap(student, "delete"));
}
/**
* @Description: 搜索某个学生的资料
* @Param:
* @return:
* @Author: 01
* @Date: 2018/3/6
*/
public Student searchStudentData(int sid) {
Student student = dao.selectById(sid);
if (student != null) {
logDAO.insert(stuMap(student, "selectById"));
} else {
return null;
}
return student;
}
/**
* @Description: 搜索全部学生的资料
* @Param:
* @return:
* @Author: 01
* @Date: 2018/3/6
*/
public List<Student> searchStudentsData() {
List<Student> students = dao.selectAll();
logDAO.insert(stuMap(null, "selectAll"));
return students;
}
/**
* @Description: 修改某个学生的资料
* @Param:
* @return:
* @Author: 01
* @Date: 2018/3/6
*/
public int alterStudentData(Student studentNew) {
Student studentOld = dao.selectById(studentNew.getSid());
int row = dao.update(studentNew);
logDAO.insert(stuMap(studentOld, "alter"));
return row;
}
}
以上就改造完成了,经过测试没有什么问题,事务也能正常回滚,保证了两张表格数据的一致性。
标签:事务管理 Spring Java开发 AOP JDBC
原文地址:http://blog.51cto.com/zero01/2083681