事务是数据库操作最基本单元,逻辑上一组操作,要么都成功,如果有一个失败所有操作都失败
事务四个特性(ACID)
就以上面的银行转账为例我们来解释Spring中的事务相关内容
事务是数据库操作最基本单元,因此首先肯定要连接数据库啦,修改配置文件
druidbean.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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">
<context:component-scan base-package="com.amhu.yx.spring">context:component-scan>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
destroy-method="close">
<property name="url" value="jdbc:mysql://localhost:3308/springdb" />
<property name="username" value="root" />
<property name="password" value="" />
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource">property>
bean>
beans>
service 种注入dao
UserService
@Service
public class UserService {
//注入dao
@Autowired
private UserDao userDao;
}
在dao注入JdbcTemplate
UserDaoImpl
@Repository
public class UserDaoImpl implements UserDao{
@Autowired
private JdbcTemplate jdbcTemplate;
}
在dao中创建数据库中增加和减少钱的方法,在service中创建转账的方法(调用dao中方法)
UserDao
public interface UserDao {
void outCount();
void entryCount();
}
UserDaoImpl
@Repository
public class UserDaoImpl implements UserDao{
@Autowired
private JdbcTemplate jdbcTemplate;
//从出账账户中扣钱
@Override
public void outCount() {
String sql = "update user set money=money-? where username=?";
jdbcTemplate.update(sql,100,"jack");
}
//从入账账户中加钱
@Override
public void entryCount() {
String sql = "update user set money=money+? where username=?";
jdbcTemplate.update(sql,100,"tom");
}
}
``
@Service
public class UserService {
//注入dao
@Autowired
private UserDao userDao;
//转账的方法
public void accountMoney() {
userDao.outCount();//jack少了100块钱
userDao.entryCount();//tom增加了100块钱
}
}
测试方法:testAccountMoney
@Test
public void testAccountMoney() {
ApplicationContext context = new ClassPathXmlApplicationContext("druidbean.xml");
UserService userService= context.getBean("userService", UserService.class);
userService.accountMoney();
}
上面的代码看似没有问题,但是如果代码执行过程中出现异常,那么该怎么办呢?
我们在转账方法中模拟一个异常:
这里出账方法是正常执行的,但是因为中间有一个异常,入账方法没有得到执行,运行测试代码:
可以看到jack少了100元,但是tom却没有增加钱,实际生活中,如果你转账的时候不小心出现异常,钱唰一下就没了,这谁顶得住?
我们这里就用到了事务来解决这个问题:
将代码放在异常捕获中,如果出现异常,就进行事务回滚,比如上面将jack的钱退还给jack。
//转账的方法
public void accountMoney() {
try {
//第一步,开启事务
//第二部 进行业务操作
userDao.outCount();//jack少了100块钱
int i = 10 / 0;//模拟异常
userDao.entryCount();//tom增加了100块钱
//如果无异常发生,提交事务
}catch (Exception e){
//出现异常,事务回滚
}
}
1、事务添加到 JavaEE 三层结构里面 Service 层(业务逻辑层)
2、在 Spring 进行事务管理操作有两种方式:
CTRL + H
就可以查看相关类的继承结构图:
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource " ref="dataSource">property>
bean>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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">
</beans>
(2)开启事务注解
<tx:annotation-driven transaction-manager="transactionManager">tx:annotation-driven>
(3)在service类上(获取service类的方法上)添加事务注解@Transactional
@Transactional
既可以添加到类上,也可以添加到方法上修改数据库中表:
重新开始运行测试代码,注意我们并没有修改代码,并且去掉刚刚加上的try-catch语句,恢复到之前的样子,与之前的区别只是添加了@Transactional
注解:
再次运行,查看结果:
发现没有任何改变。
propagation
: 事务传播行为
多事务方法直接进行调用,这个过程中事务是如何进行管理的。
事务方法:对数据库表数据进行变化的方法
ioslation
: 事务隔离级别
(1) 事务有特性称为隔离性,多十五操作之间不会产生影响,不考虑隔离性会产生多种问题
(2) 有三个读问题
(3)通过设置事务隔离级别,解决读问题
mysql中默认级别是:REPEATABLE_READ
timeout
: 超时时间
规定时间内事务未提交,进行回滚
默认值是-1(不超时)
自己设置以秒为单位
readOnly
: 是否只读
读:查询操作
写:添加修改删除操作
readOnly默认值false
,
若设置为true
后,事务只能进行查询操作
rollbackFor
: 回滚
设置出现中哪些异常进行事务回滚
noRollbackFor
: 不回滚
设置出现中哪些异常不进行事务回滚
1、在 spring 配置文件中进行配置
第一步 配置事务管理器
第二步 配置通知
第三步 配置切入点和切面
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource">property>
bean>
<tx:advice id="txadvice">
<tx:attributes>
<tx:method name="accountMoney" propagation="REQUIRED"/>
tx:attributes>
tx:advice>
<aop:config>
<aop:pointcut id="pt" expression="execution(*
com.atguigu.spring5.service.UserService.*(..))"/>
<aop:advisor advice-ref="txadvice" pointcut-ref="pt"/>
aop:config>
不用配置文件,完全使用注解
TxConfig
package com.amhu.yx.spring.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;
@Configuration//配置类
@ComponentScan(basePackages = "com.amhu.yx.spring")//注解扫描
@EnableTransactionManagement //开启事务
public class TxConfig {
//创建数据库连接池
@Bean
public DruidDataSource getDruidDataSource(){
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setDriverClassName("com.mysql.jdbc.Driver");
druidDataSource.setUrl("jdbc:mysql://localhost:3308/springdb");
druidDataSource.setUsername("root");
druidDataSource.setPassword("");
return druidDataSource;
}
//创建JdbcTemplate对象
@Bean
public JdbcTemplate getJdbcTemplate(DataSource dataSource){
//到ioc容器中根据类型注入datasSource
JdbcTemplate jdbcTemplate = new JdbcTemplate();
//注入dataSource
jdbcTemplate.setDataSource(dataSource);
return jdbcTemplate;
}
//创建事务管理器
@Bean
public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource){
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
transactionManager.setDataSource(dataSource);
return transactionManager;
}
}
测试方法 testConfig()
@Test
public void testConfig(){
ApplicationContext context =
new AnnotationConfigApplicationContext(TxConfig.class);
UserService userService = context.getBean("userService", UserService.class);
userService.accountMoney();
}
其它代码与之前相同:
UserDao
public interface UserDao {
void outCount();
void entryCount();
}
UserDaoImpl
@Repository
public class UserDaoImpl implements UserDao{
@Autowired
private JdbcTemplate jdbcTemplate;
//从出账账户中扣钱
@Override
public void outCount() {
String sql = "update user set money=money-? where username=?";
jdbcTemplate.update(sql,100,"jack");
}
//从入账账户中加钱
@Override
public void entryCount() {
String sql = "update user set money=money+? where username=?";
jdbcTemplate.update(sql,100,"tom");
}
}
UserService
@Service
@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.REPEATABLE_READ)
public class UserService {
//注入dao
@Autowired
private UserDao userDao;
//转账的方法
public void accountMoney() {
userDao.outCount();//jack少了100块钱
userDao.entryCount();//tom增加了100块钱
}
}