一.事务的隔离级别和传播行为
1.事务的隔离级别:
默认级别:DEFAULT,属于下面四种级别的某一种
- 可重复读:REPEATABLE_READ
- 读未提交:READ_UNCOMMITTED
- 读已提交:READ_COMMITTED,是oracle数据库默认级别
- 串行化:SERIALIZABLE
2.事务传播行为:
REQUIRED:支持当前事务,如果当前没有事务,就新建一个事务
SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式运行
MANDATORY:支持当前事务,如果当前没有事务,就抛出异常
REQUERS_NEW:新建事务,如果当前在事务中,把当前事务挂起
NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起
NEVER:以非事务方式运行,如果当前存在事务,抛出异常。
二.事务管理的分类
1.编程式事务管理:在代码中显示调用beginTransaction、commit、rollback等事务处理相关的方法
2.声明式事务管理:通过AOP技术实现。对方法前后进行拦截,在目标方法开始之前创建一个事务,执行完目标方法之后根据执行情况提交或回滚事务。
声明式事务管理更简单方便,有利于后期代码维护
三.声明式事务管理
1.基于XML方式的声明式事务管理
建表:
create table account(
id number primary key,
name varchar2(255),
money number
);
insert into account values(1,‘张三’,1000)
insert into account values(2,‘李四’,300);
使用的jar包:
实体类:
@Data
public class Account {
private int id;
private String name;
private Double money;
}
数据源文件:dataSource.properties
jdbc.driverClass=oracle.jdbc.OracleDriver
jdbc.url=jdbc:oracle:thin:@localhost:1522:xe
jdbc.username=jy
jdbc.password=jy
initialSize=5
maxActive=10
DAO层接口:
public interface AccountDao {
//存钱
void incrementMoney(long id,double money);
//取钱
void decrmentMoney(long id,double money);
//查询所有账户
void findAll();
}
DAO层实现类:
@Repository
public class AccountDaoImpl implements AccountDao{
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public void incrementMoney(long id, double money) {
String sql ="update account set money = money + ? where id = ?";
jdbcTemplate.update(sql, money,id);
}
@Override
public void decrmentMoney(long id, double money) {
String sql = "update account set money = money - ? where id = ?";
jdbcTemplate.update(sql, money,id);
}
@Override
public List<Account> findAll() {
String sql = "select * from account";
RowMapper<Account> rowMapper = new BeanPropertyRowMapper<>(Account.class);
List<Account> accounts = jdbcTemplate.query(sql, rowMapper);
return accounts;
}
}
业务层接口:
public interface IAccountService {
//转账
void trans(int fromId,int toId,double money);
List<Account> findAll();
}
业务层实现类:
@Service
public class AccountServiceImpl implements IAccountService{
@Autowired
private AccountDao accountDao;
@Override
public void trans(int fromId, int toId, double money) {
accountDao.decrmentMoney(fromId, money);
//此处用于测试事务
int i = 10/0;
accountDao.incrementMoney(toId, money);
}
@Override
public List<Account> findAll() {
return accountDao.findAll();
}
}
事务管理相关标签:
<context:property-placeholder>
:使得Spring容器将配置文件加载进来
属性:
- location:配置文件的位置。location=“classpath:文件名”
<tx:advice>
:配置事务通知
属性:
- id:事务通知的唯一标识
- transaction-manager:值为事务管理器的id值,表示事务通知是哪个事务管理器的通知
<tx:attributes>
:<tx:advice>
的子元素,用于配置事务属性
<tx:method>
:配置切入点,即要管理的方法。(service实现类中的方法)
属性:
- name:指定方法的名字,*代表任意方法
- isolation:指定事务隔离级别
- propagation:指定事务传播行为
- read-only:指定是否为只读事务。只读不会自动提交事务
- rollback-for:指定一个异常,发生指定异常才会回滚,产生其他异常,事务不回滚。
- no-rollback-for:指定一个异常,产生该异常事务不回滚,产生其他异常事务回滚
rollback-for="java.lang.RuntimeExcetion“:表示发生运行时异常才会回滚,其他异常不回滚
<aop:advisor>
:配置切面(通知+切入点)
属性:
- advice-ref:表示关联的通知,值为事务通知的id值
- pointcut-ref:表示关联的切入点,值为切入点的id值
配置文件:
<?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:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
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">
<!-- 指定需要扫描的包,使注解生效 -->
<context:component-scan base-package="com.spring.jdbc"/>
<!-- 导入dataSource.properties文件 -->
<context:property-placeholder location="classpath:dataSource.properties"/>
<!-- 创建数据源并配置 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<!-- 数据库驱动 -->
<property name="driverClassName" value="${jdbc.driverClass}"/>
<!-- 连接数据库的URL -->
<property name="url" value="${jdbc.url}"/>
<!-- 连接数据库的用户名 -->
<property name="username" value="${jdbc.username}"/>
<!-- 连接数据库的密码 -->
<property name="password" value="${jdbc.password}"/>
</bean>
<!--创建JDBC模板并配置 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!-- 关联数据源 -->
<property name="dataSource" ref="dataSource"/>
</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="*" isolation="DEFAULT" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!-- 编写AOP,让Spring自动对目标对象生成代理 -->
<aop:config>
<!-- 定义切入点 -->
<aop:pointcut expression="execution(* com.spring.jdbc..*.*(..))" id="pt"/>
<!-- 关联切入点和通知 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="pt"/>
</aop:config>
</beans>
测试类:
@RunWith(SpringRunner.class)
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
public class AccountTest {
@Autowired
private IAccountService accountService;
@Test
public void query() {
List<Account> list = accountService.findAll();
for(Account account:list) {
System.out.println(account);
}
}
@Test
public void testTrans() {
accountService.trans(1, 2, 300);
}
}
业务层的转账方法中,转出和转入之间设置了一个异常,
如果applicationContext.xml中不进行事务管理,则会执行转出操作,不执行转入操作。
如果进行事务管理,发生异常时事务会回滚,转入转出都不执行
进行事务管理输出结果:
没进行事务管理输出结果:
2.基于注解方式的事务管理
实现方式:在业务层实现类上加注解@Transactional,在配置文件中加标签`tx:annotation-driven
@Transactional
- 该注解用于进行事务管理,可作用于类、类方法、接口、接口方法上。
- 作用于类上,该类的所有public方法都具有该事务特性。
- 注解在方法上的优先级>类上>接口上
<tx:annotation-driven>
:为事务管理器注解驱动器
属性:transaction-manager:指定是哪个事务管理器
基于XML方式的事务管理需要在配置文件中添加tx:annotation-driven标签,事务才能生效
(1)使用配置文件实现:
业务层实现类:
@Service
@Transactional(isolation = Isolation.DEFAULT,propagation = Propagation.REQUIRED,readOnly = false)
public class AccountServiceImpl implements IAccountService{
@Autowired
private AccountDao accountDao;
@Override
public void trans(int fromId, int toId, double money) {
accountDao.decrmentMoney(fromId, money);
//此处用于测试事务
int i = 10/0;
accountDao.incrementMoney(toId, money);
}
@Override
@Transactional(isolation = Isolation.DEFAULT,propagation = Propagation.SUPPORTS,readOnly = true)
public List<Account> findAll() {
return accountDao.findAll();
}
}
配置文件:
<?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:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
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">
<!-- 指定需要扫描的包,使注解生效 -->
<context:component-scan base-package="com.spring.jdbc"/>
<!-- 导入dataSource.properties文件 -->
<context:property-placeholder location="classpath:dataSource.properties"/>
<!-- 创建数据源并配置 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<!-- 数据库驱动 -->
<property name="driverClassName" value="${jdbc.driverClass}"/>
<!-- 连接数据库的URL -->
<property name="url" value="${jdbc.url}"/>
<!-- 连接数据库的用户名 -->
<property name="username" value="${jdbc.username}"/>
<!-- 连接数据库的密码 -->
<property name="password" value="${jdbc.password}"/>
</bean>
<!--创建JDBC模板并配置 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!-- 关联数据源 -->
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 创建事务管理器对象并配置其数据源 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 为事务管理器注册注解驱动器 -->
<tx:annotation-driven transaction-manager="txManager"/>
</beans>
其余代码同上。
(2)使用配置类实现:
@EnableTransactionManagement:配置类中需要加上该标签,事务管理才能生效
@Configuration
@ComponentScan("com.spring.jdbc")
@EnableTransactionManagement
public class AccountConfig {
@Bean
public DataSource dataSource() {
//创建数据源
DruidDataSource dataSource= new DruidDataSource();
dataSource.setDriverClassName("oracle.jdbc.OracleDriver");
dataSource.setUrl("jdbc:oracle:thin:@localhost:1522:xe");
dataSource.setUsername("jy");
dataSource.setPassword("jy");
dataSource.setInitialSize(5);
dataSource.setMaxActive(10);
return dataSource;
}
@Bean
public JdbcTemplate jdbcTemplate() {
//创建模板类
JdbcTemplate jdbcTemplate = new JdbcTemplate();
//设置模板类的数据源
jdbcTemplate.setDataSource(dataSource());
return jdbcTemplate;
}
@Bean
public PlatformTransactionManager manager() {
//创建事务管理器
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
//设置事务管理器的数据源
transactionManager.setDataSource(dataSource());
return transactionManager;
}
}