事务五大基本原则ACID
- 原子性(Atomicity):
- 事务是一个原子操作单元,其对数据的修改要么全都执行,要么全都不执行。
- 如果事务执行失败,所有已执行的步骤都必须回滚(Rollback),数据库状态应返回到事务开始前的状态。
- 一致性(Consistency):
- 事务必须使数据库从一个一致性状态变换到另一个一致性状态。
- 一致性状态指的是数据库中的数据应满足完整性约束条件。
- 在事务开始和完成时,数据都必须保持一致状态。
- 隔离性(Isolation):
- 在事务完成之前,它修改的数据对其他事务是不可见的。
- 每个事务都在一个独立的、由DBMS提供的“事务环境”中执行,就如同在一个单独的数据库中运行一样。
- 隔离性防止了多个事务并发执行时由于交叉执行而导致数据的不一致。
- 持久性(Durability):
- 一旦事务提交,其对数据库的修改就应该是永久性的。
- 即使系统发生故障,提交的更改也不会丢失。
- 持久性通常通过DBMS的日志和恢复机制来实现。
xml方式实现事务
1、导入事务的依赖
<!--事务依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
2、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.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
">
<!-- 扫描properties文件-->
<context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
<!-- 包组件扫描-->
<context:component-scan base-package="com"></context:component-scan>
<!-- 数据源-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driver}"></property>
<!-- 连接数据库的url -->
<property name="url" value="${jdbc.url}"></property>
<!-- 连接数据库的用户名 -->
<property name="username" value="${jdbc.username}"></property>
<!-- 连接数据库的密码 -->
<property name="password" value="${jdbc.password}"></property>
</bean>
<!-- 2.配置jdbc模板 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!-- 默认必须使用数据源 -->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--userDao-->
<bean id="userDao" class="com.dao.impl.UserDaoImpl">
<!-- set注入-->
<property name="jdbcTemplate" ref="jdbcTemplate"></property>
</bean>
<!-- UserService-->
<bean id="userService" class="com.service.impl.UserServiceImpl">
<property name="userDao" ref="userDao"></property>
</bean>
<!-- 配置平台事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 通知事务增强-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!-- 设置事务属性信息-->
<tx:attributes>
<tx:method name="transferMoney" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false"/>
</tx:attributes>
</tx:advice>
<!-- 事务aop织入-->
<aop:config>
<!-- 切点-->
<aop:pointcut id="pt" expression="execution(* com.service.impl.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="pt"></aop:advisor>
</aop:config>
</beans>
3、实体类以及对应的dao层
//实体类
public class User {
private Integer id;
private String name;
private Double money;
//dao层
package com.dao.impl;
import com.dao.UserDao;
import com.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Component;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
public class UserDaoImpl implements UserDao {
private JdbcTemplate jdbcTemplate;
public JdbcTemplate getJdbcTemplate() {
return jdbcTemplate;
}
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
@Override
public int addUser(User user) {
String sql="insert into t_user(name,money) values(?,?)";
return jdbcTemplate.update(sql,user.getName(),user.getMoney());
}
@Override
public int updateUser(User user) {
String sql="update t_user set name=?,money=? where id=?";
return jdbcTemplate.update(sql,user.getName(),user.getMoney(),user.getId());
}
@Override
public int delUser(int id) {
String sql="delete from t_user where id=?";
return jdbcTemplate.update(sql,id);
}
@Override
public List<User> querryAll() {
String sql="select * from t_user";
return jdbcTemplate.query(sql, new RowMapper<User>() {
public User mapRow(ResultSet rs, int rowNum) throws SQLException {
User user = new User();
user.setId(rs.getInt("id")); // 假设User有一个setId方法
user.setName(rs.getString("name")); // 假设User有一个setName方法
user.setMoney(rs.getDouble("money"));
// 设置其他User的属性...
return user;
}
});
}
@Override
public int addMoney(String userName, Double addmoney) {
String sql="update t_user set money=money+? where name=?";
return jdbcTemplate.update(sql,addmoney,userName);
}
@Override
public int delMoney(String userName, Double delmoney) {
String sql="update t_user set money=money-? where name=?";
return jdbcTemplate.update(sql,delmoney,userName);
}
}
4、service层
package com.service.impl;
import com.dao.UserDao;
import com.service.UserService;
public class UserServiceImpl implements UserService {
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void transferMoney(String addUserName, String delUserName, double money) {
userDao.delMoney(delUserName,money);
int i=1/0;
userDao.addMoney(addUserName,money);
System.out.println("转账完成!");
}
}
注解方式实现事务,事务隔离级别,传播行为
传播行为
隔离级别
-
脏读(Dirty Read):
脏读是指一个事务读取了另一个尚未提交的事务的修改。由于另一个事务可能最终会被撤销(Rollback),所以这种读取到的数据是“脏”的,因为它可能会因为另一个事务的回滚而被改变。例如,事务A修改了一行数据,但尚未提交。此时,事务B读取了事务A修改后的数据。如果事务A因为某种原因被撤销,那么事务B读取到的数据就是脏数据。
-
不可重复读(Non-repeatable Read):
不可重复读是指在一个事务内,多次读取同一数据返回的结果有所不同。这通常是因为另一个并发事务在第一次读取和第二次读取之间修改了该数据。例如,事务A第一次读取了某行数据,然后事务B修改了该数据并提交。接着,事务A再次读取同一行数据,发现数据已经被改变。
-
幻读(Phantom Read):
幻读是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还存在没有修改的数据行,就好象发生了幻觉一样。具体来说,幻读发生在以下情况:
- 事务A按照一定条件查询得到了N条记录。
- 此时事务B插入或删除了符合事务A查询条件的若干条记录。
- 然后事务A再次按照相同条件查询,发现查询结果的记录数发生了变化(变多或变少)。
例子查看:http://t.csdnimg.cn/MVbTX
全注解形似实现实现事务
1、包结构
2、配置类
JdbcConfig
package com.demo.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import javax.sql.DataSource;
@PropertySource("classpath:jdbc.properties")
@Configuration
public class JdbcConfig {
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
// 配置数据源
@Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(driver);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
// 配置JdbcTemplate
@Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
}
SpingConfig
package com.demo.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.*;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;
@Configuration//配置文件注解,相当于applicatonContext.xml
@ComponentScan("com.demo")//包扫描,扫描注解
@PropertySource("classpath:jdbc.properties")//读取jdbcproperties文件
@Import(JdbcConfig.class)//导入其他配置类
//开启事务
@EnableTransactionManagement
public class SpringConfig {
@Autowired
private DataSource dataSource;
@Bean
public PlatformTransactionManager transactionManager() {
return new DataSourceTransactionManager(dataSource); // 对于JDBC
}
}
3、实体类及dao层
public class User {
private Integer id;
private String name;
private Double money;
package com.demo.dao.impl;
import com.demo.dao.UserDao;
import com.demo.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
@Repository
public class UserDaoImpl implements UserDao {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public int addUser(User user) {
String sql="insert into t_user(name,money) values(?,?)";
return jdbcTemplate.update(sql,user.getName(),user.getMoney());
}
@Override
public int updateUser(User user) {
String sql="update t_user set name=?,money=? where id=?";
return jdbcTemplate.update(sql,user.getName(),user.getMoney(),user.getId());
}
@Override
public int delUser(int id) {
String sql="delete from t_user where id=?";
return jdbcTemplate.update(sql,id);
}
@Override
public List<User> querryAll() {
String sql="select * from t_user";
return jdbcTemplate.query(sql, new RowMapper<User>() {
public User mapRow(ResultSet rs, int rowNum) throws SQLException {
User user = new User();
user.setId(rs.getInt("id")); // 假设User有一个setId方法
user.setName(rs.getString("name")); // 假设User有一个setName方法
user.setMoney(rs.getDouble("money"));
// 设置其他User的属性...
return user;
}
});
}
@Override
public int addMoney(String userName, Double addmoney) {
String sql="update t_user set money=money+? where name=?";
return jdbcTemplate.update(sql,addmoney,userName);
}
@Override
public int delMoney(String userName, Double delmoney) {
String sql="update t_user set money=money-? where name=?";
return jdbcTemplate.update(sql,delmoney,userName);
}
}
4、service层
两个实现类,测试传播行为使用
service1
package com.demo.service.impl;
import com.demo.dao.UserDao;
import com.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Transactional(propagation = Propagation.REQUIRED)
@Override
public void transferMoney(String addUserName, String delUserName, double money) {
userDao.delMoney(delUserName,money);
userDao.addMoney(addUserName,money);
//int i=1/0;
System.out.println("转账完成!");
}
}
service2
package com.demo.service.impl;
import com.demo.dao.UserDao;
import com.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Service
public class UserService2Impl implements UserService {
@Autowired
private UserDao userDao;
@Autowired
private UserService userServiceImpl;
//@Transactional(isolation = Isolation.DEFAULT, propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
@Transactional(propagation = Propagation.REQUIRED)
@Override
public void transferMoney(String addUserName, String delUserName, double money) {
userServiceImpl.transferMoney("wsy1","wsy2",500);
}
}
*5、传播行为测试(service2调用service1)
详细见此链接:
REQUIRED:
service2调用service1,service1没有事务,则service1在新的事务中运行
service1
service2
资金不变
SUPPORTS:
无论s2是否有事务,s1都已非事务运行
s1
s2
MANDATORY:
如果s2方法在事务中运行,则s1方法将继承s2方法的事务。如果s2方法没有事务,则s1方法将抛出异常。
s1
s2
REQUIRES_NEW:
s1 REQUIRES_NEW s2无事务
无论s2是否在事务中运行,s1都会生成一个新的事物运行
NOT_SUPPORTED:
s2 REQUIRED ,s1 NOT_SUPPORTED
无论s2是否有事务,s1都已非事务方式运行
NEVER:
s2 NEVER,s1 NEVER
如果s2在事务中运行,s1运行时抛出异常,如果s2没有事务,s1以非事务运行
NESTED:
s1 NESTED s2 NESTED
如果s2在事务中运行,s1作为s2的嵌套事务运行,如果s2没有事务,s1生成新事务运行
*6、事务不发生
事务被catch掉:
在事务中使用tay catch 解决异常,会把事务处理掉,此时的事务不生效,资金发生改变