1.为什么使用事务管理
在应用中,对于任何数据处理的事务,都必须要保护数据的完整性。如果一个账户出现了丢失数据,譬如余额不准,交易金额丢失等,这种致命的错误必然导致用户的反感。而事务正式用来保证应用系统中数据的完整性。
2.事务遵守的原则
使用事务管理保证的几个原则:
我们常常应该在实现的系统中保证在不牺牲性能的原则下,保证ACID原则(原子性、一致性、隔离性、持久性)。
(1)原子性:在我们的事务中可能会出现很多的操作,可能出现操作成功和操作失败两种情况,原子性就是要保证如果操作成功就一定保持所有的操作都成功。如果失败就要保证所有的操作都失败。(术语:操作必须得回滚)。
(2)一致性:譬如我们在转账的时候,如果张三给李四转账,张三转了1000块,那么张三的账户少了1000元,那么李四的账户就必须要保证多了1000元。两边必须一致,也就是转账这个事务要保持一致性。
(3)隔离性:有时事务出现并发性时,要保证多个事务之间互不影响,就是一个事务的订单还未提交,另一个事务不会让订单提交了。两个事务隔离。发现其实隔离性和一致性是相关的。如果隔离性很好,那么一致性也很好。
(4)持久性:就是如果张三给李四转账1000元,如果张三已经转账成功,那么持久性就要保证数据不会丢失。不会因为其他修改而发生错误的改变。
3.XML方式实现spring的事务管理
搭建环境pom.xml:
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.9</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>4.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.3</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>4.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.2.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.38</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.2</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.2</version>
</dependency>
</dependencies>
Model模型User.java:
public class User implements Serializable {
private Integer id;
private String username;
private String gender;
public User() {
super();
// TODO Auto-generated constructor stub
}
public User(Integer id, String username, String gender) {
super();
this.id = id;
this.username = username;
this.gender = gender;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
@Override
public String toString() {
return "User [id=" + id + ", username=" + username + ", gender="
+ gender + "]";
}
}
dao层,UserDao.java:
public class UserDao {
private JdbcTemplate jdbcTemplate;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public void save(User user) {
String sql = "insert into myuser(id,username,gender) values(?,?,?)";
jdbcTemplate.update(
sql,
new Object[] { user.getId(), user.getUsername(),
user.getGender() }, new int[] { Types.INTEGER,
Types.VARCHAR, Types.VARCHAR });
}
}
service层,UserService.java:
public class UserService {
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void saveUser(User user) {
userDao.save(user);
int i = 1 / 0;
userDao.save(user);
}
}
解释:如果我们不使用事务控制就仍然会保存用户到数据库,如果我们加了事务控制就会因为异常的错误,让用户不会添加到数据库中。这样就保持了原子性。
配置文件bean.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:p="http://www.springframework.org/schema/p"
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.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
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 1. 数据源对象: C3P0连接池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/basicjdbc?characterEncoding=utf-8"></property>
<property name="user" value="root"></property>
<property name="password" value=""></property>
<property name="initialPoolSize" value="3"></property>
<property name="maxPoolSize" value="10"></property>
<property name="maxStatements" value="100"></property>
<property name="acquireIncrement" value="2"></property>
</bean>
<!-- 配置jdbcTemplate工具类实例 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置Dao层 -->
<bean id="userDao" class="cn.spy.transaction.xml.UserDao">
<property name="jdbcTemplate" ref="jdbcTemplate"></property>
</bean>
<!-- 配置Service层 -->
<bean id="userService" class="cn.spy.transaction.xml.UserService">
<property name="userDao" ref="userDao"></property>
</bean>
<!-- 配置事务管理器类 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置事务增强 -->
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="*saveUser*" read-only="false"/>
</tx:attributes>
</tx:advice>
<!-- aop配置 -->
<aop:config>
<aop:pointcut expression="execution(* cn.spy.transaction.xml.UserService.*(..))" id="pt"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="pt"/>
</aop:config>
</beans>
解释:spring的事务管理,其实还是使用了spring的aop切面编程。(如果让我们去动手实现spring的事务管理的话,肯定是在哪个方法上面定义切点表达式进行监控,如果报出异常,那么我们就为了保持原子性,不进行回滚了。),所以还需要配置spring的aop。
至于aop不了解的话:http://blog.csdn.net/ya_1249463314/article/details/63684853
bean.xml里面也配置了DataSourceTransactionManager,这个是配置jdbc连接使用的管理器。配置事务增强就是定义在哪个方法上使用事务管理。
至于注入,使用的上面set方式注入的属性。
至于注入不了解的话:http://blog.csdn.net/ya_1249463314/article/details/53153030
测试类:
public class App {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext(
"cn/spy/transaction/xml/bean.xml");
UserService userService = (UserService) ac.getBean("userService");
User user = new User(14, "谭盐盐", "女");
userService.saveUser(user);
}
}
结果:
解释:由于数字异常,事务控制导致数据未添加进去,保证了原子性。
4.注解方式实现spring的事务管理
UserDao.java:
@Repository
public class UserDao {
@Autowired
private JdbcTemplate jdbcTemplate;
public void save(User user) {
String sql = "insert into myuser(id,username,gender) values(?,?,?)";
jdbcTemplate.update(
sql,
new Object[] { user.getId(), user.getUsername(),
user.getGender() }, new int[] { Types.INTEGER,
Types.VARCHAR, Types.VARCHAR });
}
}
UserService.java:
@Service
public class UserService {
@Autowired
private UserDao userDao;
@Transactional(readOnly=false)
public void saveUser(User user) {
userDao.save(user);
int i = 1 / 0;
userDao.save(user);
}
}
解释:Transactional注解常用的属性:
1)readOnly=false:开启读写事务。
(2)timeout=-1:事务的超时时间(-1代表超时时间不限制)。
(3)noRollbackFor=ArithmeticException.class:遇到数学异常不回滚。
(4)isolation=Isolation.DEFAULT:事务的隔离级别,数据库的默认。
(5)propagation=Propagation.REQUIRED:事务的传播行为。
bean.xml:
<!-- 1. 数据源对象: C3P0连接池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/basicjdbc?characterEncoding=utf-8"></property>
<property name="user" value="root"></property>
<property name="password" value=""></property>
<property name="initialPoolSize" value="3"></property>
<property name="maxPoolSize" value="10"></property>
<property name="maxStatements" value="100"></property>
<property name="acquireIncrement" value="2"></property>
</bean>
<!-- 配置jdbcTemplate工具类实例 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 开启注解扫描 -->
<context:component-scan base-package="cn.spy.transaction.annotation"></context:component-scan>
<!-- 指定注解方式实现事务 -->
<tx:annotation-driven transaction-manager="txManager"/>
<!-- 配置事务管理器类 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
解释:由于采用注解注入的方式,所以要配置扫描注解,并且要开启注解方式实现事务。至于事务管理器类是必须要配置的。
测试类:
public class App {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext(
"cn/spy/transaction/annotation/bean.xml");
UserService userService = (UserService) ac.getBean("userService");
User user = new User(14, "谭盐盐", "女");
userService.saveUser(user);
}
}
结果:
结果:由于数字异常,导致数据没有保存到数据库,维持了事务的原子性。