事务概念
1.什么是事务
(1)是数据库操作最基本的单元,逻辑上的一组操作,要么都成功,要么全失败
(2)典型场景:银行转账
2.事务特性
(1)原子性(要么都成功,一个失败都失败)
(2)一致性(总量相同)
(3)隔离性(多个事务之间互不影响)
(4)持久性(比如数据发生改变是永久性的)
事务操作(环境搭建)
1.创建数据库表,添加记录
2.创建service类,dao接口和实现类,完成对象创建和关系注入
(1)在service注入dao,在dao实现类jdbctemplate,在jdbctemplate注入datasource
3.在dao创建加钱和少钱的方法,在service创建转账的方法
(1)dao里
public interface UserDao {
public void addMoney();
public void reduceMoney();
}
(2)daoimpl里
@Autowired
private JdbcTemplate jdbcTemplate;
//少钱
@Override
public void reduceMoney() {
String sql = "update t_account set money=money-? where username=?";
jdbcTemplate.update(sql, 100, "lucy");
}
//多钱
@Override
public void addMoney() {
String sql = "update t_account set money=money+? where username=?";
jdbcTemplate.update(sql, 100, "mary");
}
(3)service里
@Autowired
private UserDao userDao;
//转账方法
public void accountMoney() {
//lucy少一百
userDao.reduceMoney();
//mary多一百
userDao.addMoney();
}
(4)测试
@Test
public void test() {
ApplicationContext context =
new ClassPathXmlApplicationContext("bean1.xml");
UserService userService = context.getBean("userService", UserService.class);
userService.accountMoney();
}
4.如果代码在执行过程中出现异常
出现钱少了但另一个人钱没有增加的问题
//转账方法
public void accountMoney() {
//lucy少一百
userDao.reduceMoney();
//模拟异常
int i = 10 / 0;
//mary多一百
userDao.addMoney();
}
(1)用事务来解决,都成功才成功
(2)事务操作过程(编程式,但通常使用声明式)
try {
//第一步开启事务
//第二步开启业务操作
//lucy少一百
userDao.reduceMoney();
//模拟异常
int i = 10 / 0;
//mary多一百
userDao.addMoney();
//第三步没有发生异常,提交事务
} catch (Exception e) {
//第四步出现异常,事务回滚
}
事务操作(Spring事务管理介绍)
1.事务一般添加到service(业务逻辑层)
2.在Spring里进行事务管理操作
(1)有两种方式:编程式和声明式事务管理(一般使用后者)
3. 声明式事务管理
(1)基于xml配置文件
(2)基于注解方式(通常使用)
4.在Spring进行声明式的事务管理,底层使用aop原理
5.Spring事务管理API
(1)提供了一个接口,代表事务管理器PlatformTransactionManager
这个接口为不同的框架提供了不同的实现类
jdbc模板这里使用的是DataSourceManager实现类
Spring声明式事务管理(基于注解)
1.在xml文件中配置事务管理器
<!-- 创建事务管理器,实际上就是创建这个地方使用的实现类的对象-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 注入数据源-->
<property name="dataSource" ref="dataSource"></property>
</bean>
2.在xml文件中开启事务注解
(1)引入名称空间tx
(2)<!--开启事务注解-->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
3.在service类(或方法)上面添加事务注解
(1)@Transactional,这个注解可以添加到类上或者方法上
(2)如果把注解添加到类上,则类的所有方法都添加事务
(3)如果添加到方法上,只为方法添加事务
(4)我遇到了的问题(视频里没有):
在过程中遇到了事务注解失效的问题,
由于数据库里的表默认为myisam,不支持事务操作,需要通过
ALTER TABLE t_account ENGINE = INNODB;设置成innodb
Spring声明式事务管理(参数配置)
1.@Transactional()括号里参数配置
2.(1)propagation:事务传播行为
多事务方法调用,过程中事务如何管理
事务方法指对数据库表数据进行变化的操作(比如查询不属于)
举例有一个add方法上有事务,并且在内部调用update方法
@Transactional
public void add(){update();} public void update(){}
Spring框架事务传播行为有七种:
(1)REQUIRED:如果add方法本身有事务,调用update方法后,update也使用add当前的事务
如果add方法没有事务,调用update方法后,创建新事务
(2)REQUIRED_NEW :使用add方法调用update方法,无论add是否有事务都会创建新的事务
还有多种不一一例举
@Transactional(propagation = Propagation.REQUIRED)
(2)ioslation:事务隔离级别
(1)有隔离性多事务操作之间才不会受影响
(2)不考虑隔离性会出现三个读的问题:脏读、不可重复读、虚读
(3)脏读:一个未提交事务读取到了另一个未提交事务的数据
例如两个人修改一条记录,一个人修改完的数据被另一个人看见了,
但数据进行了事务回滚,导致数据变化,读取的数据不准确。
(4)不可重复读:一个未提交事务读取到另一个提交事务的修改数据
(正常事务之间没有影响,只有提交事务之后才能读取到新数据)
(5)虚读:一个未提交事务读取到另一个提交事务的添加数据
(6)设置隔离性可以解决以上问题(mysql中默认为REPEATABLE READ)
READ UNCOMMITTED 三个问题都不能解决
READ COMMITTED 能解决脏读问题
REPEATABLE READ 不能解决虚读(幻读)问题
SERIALIZABLE 都能解决
@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.REPEATABLE_READ)
(3)timeout:超时时间
(1)事务设置必须在多长时间内提交,超时则会回滚
(2)默认值是-1(表示永不超时),秒为单位
(4)readonly:是否只读
(1)读:查询。写:添加修改删除
(2)默认值为false,表示读写都可以
(3)设置值为true,表示只读
(5)rollbackfor:回滚
(1)设置出现哪些异常进行事务回滚
(6)norollbackfor:不回滚
(1)设置出现哪些异常不进行回滚
Spring声明式事务管理(基于xml配置文件)
1.在xml文件中
第一步配置事务管理器
第二步配置通知(增强的部分在这就是事务)
第三步配置切入点(方法)和切面(事务加到方法的过程)
<!-- 1. 创建事务管理器,实际上就是创建这个地方使用的实现类的对象-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 注入数据源-->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 2.配置通知 -->
<tx:advice id="txadvice">
<!--配置事务的相关参数-->
<tx:attributes>
<!--指定某种规则的方法添加事务-->
<tx:method name="accountMoney" propagation="REQUIRED" />
<!--表示account开头的方法都添加事务-->
<!-- <tx:method name="account*"/>-->
</tx:attributes>
</tx:advice>
<!--3.配置切入点和切面-->
<aop:config>
<!--配置切入点-->
<aop:pointcut id="pt" expression="execution(* com.xlj.spring5.service.UserService.*(..))"/>
<!-- 配置切面-->
<aop:advisor advice-ref="txadvice" pointcut-ref="pt"></aop:advisor>
</aop:config>
Spring声明式事务管理(完全注解开发)
1.配置类
@Configuration//表示作为配置类
@ComponentScan(basePackages = "com.xlj")//开启组件扫描
@EnableTransactionManagement//开启事务
public class txconfig {
//创建数据库连接池
@Bean
public DruidDataSource getDruidDataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql:///user_db");
dataSource.setUsername("root");
dataSource.setPassword("root");
return dataSource;
}
//创建jdbctemplate对象
@Bean
public JdbcTemplate getJdbcTemplate(DataSource dataSource) {
//到ioc容器中根据类型找到datasource
JdbcTemplate jdbcTemplate = new JdbcTemplate();
//注入datasource
jdbcTemplate.setDataSource(dataSource);
return jdbcTemplate;
}
//创建事务管理器对象
@Bean
public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource) {
DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
dataSourceTransactionManager.setDataSource(dataSource);
return dataSourceTransactionManager;
}
}
2.测试
@Test
public void test2() {
ApplicationContext context =
new AnnotationConfigApplicationContext(TxConfig.class);
UserService userService = context.getBean("userService", UserService.class);
userService.accountMoney();
}