12.1、回顾事务
- 事务在项目的开发过程中非常重要,设计到数据的一致性的问题,不容马虎
- 事务管理是企业级应用程序开发中必备技术,用来确保数据的完整性和一致性
所谓的事务就是 把一系列的动作当成一个独立的工作单元,这些动作要么全部完成,要么全部不起作用。
事务四个属性ACID
- 原子性(Atomicity)
事务是原子性操作,由一系列动作组成,事务的原子性确保动作要么全部完成,要么全部不起作用
-
一致性(Consistency)
一旦所有事务的动作完成,事务就要被提交。数据和组员处于一种满足业务规则的一致性状态中
-
隔离性(Isolation)
可能多个事务会同时处理相同的数据,因此每个事务都应该与其他的事务隔离开来,防止数据损坏
-
持久性(Durability)
事务一旦完成,无论系统发生什么错误,结果都不会收到影响。通常情况下,事务的结果被写到持久化存储器中
11.2、测试
我们接着之前的案例,给userDao接口增加两个新的方法,删除和添加用户
测试说明:由于新增加两个用户方法(添加和删除),所以我们在调用查询方法的时候去完成一个事务;事务为先去增加一个用户,然后删除这个用户,最后输出查询结果。
- 实体类 user.java
package com.yao.pojo; import lombok.Data; @Data public class user { private int id; private String name; private String pwd; public user(int id, String name, String pwd) { this.id = id; this.name = name; this.pwd = pwd; } @Override public String toString() { return "user{" + "id=" + id + ", name='" + name + '\'' + ", pwd='" + pwd + '\'' + '}'; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPwd() { return pwd; } public void setPwd(String pwd) { this.pwd = pwd; } }
- 接口 UserMapper
public interface UserMapper { public List<user> selectUser(); public user selectbyid( @Param("name") String name, @Param("pwd") String pwd ); //添加一个用户 public int addUser(user user); //删除一个用户 public int deleteUser(@Param("id")int id); }
- 接口实现类 UserMapperImpl.java
public class UserMapperImpl1 extends SqlSessionDaoSupport implements UserMapper { public List<user> selectUser() { user user = new user(5,"eee","123123"); UserMapper mapper = getSqlSession().getMapper(UserMapper.class); mapper.addUser(user); mapper.deleteUser(user.getId()); return mapper.selectUser(); } public user selectbyid(String name, String pwd) { UserMapper mapper = getSqlSession().getMapper(UserMapper.class); return mapper.selectbyid(name,pwd); } public int addUser(user user) { UserMapper mapper = getSqlSession().getMapper(UserMapper.class); return mapper.addUser(user); } public int deleteUser(int id) { UserMapper mapper = getSqlSession().getMapper(UserMapper.class); return mapper.deleteUser(id); } }
- mybatis配置文件 mybatis-config.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <!-- 别名--> <typeAliases> <package name="com.yao.pojo"/> </typeAliases> </configuration>
- mybatis-spring 配置 spring-dao.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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- DataSource:使用spring的数据源替换mybatis的配置 c3p0 dbcp druid--> <!-- 我们这里使用spring提供的jdbc 数据源--> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC"/> <property name="username" value="root"/> <property name="password" value="rootroot"/> </bean> <!-- sqlSessionFactory 需要注入一个数据源、绑定一个mybatis --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <!-- 数据源dataSource--> <property name="dataSource" ref="dataSource"/> <!-- 绑定mybatis--> <property name="configLocation" value="classpath:mybatis-config.xml"/> <property name="mapperLocations" value="classpath:com/yao/mapper/UserMapper.xml"/> </bean> <!-- sqlSessionTemplate 就是我们在mybatis使用的sqlsession--> <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate"> <!-- 只能使用构造去进行注入sqlsessionFactory--> <constructor-arg index="0" ref="sqlSessionFactory"/> </bean>
- 配置spring 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: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/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"> <import resource="spring-dao.xml"/> <bean id="userMapper" class="com.yao.mapper.UserMapperImpl1"> <property name="sqlSessionFactory" ref="sqlSessionFactory"/> </bean> <!-- 配置声明式事务--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <constructor-arg ref="dataSource" /> </bean> <!--结合aop实现事务的织入--> <!-- 配置事务通知--> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <!-- 给那些方法配置事务--> <!-- 配置事务的传播特性--> <tx:attributes> <tx:method name="add" propagation="REQUIRED"/> <tx:method name="delete" propagation="REQUIRED"/> <tx:method name="update" propagation="REQUIRED"/> <tx:method name="query" read-only="true"/> <tx:method name="*" propagation="REQUIRED"/> </tx:attributes> </tx:advice> <!-- 配置事务切入--> <aop:config> <aop:pointcut id="txPointCut" expression="execution(* com.yao.mapper.*.*(..))"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/> </aop:config> </beans>
- 测试
@Test public void test1(){ ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); UserMapper userMapper = context.getBean("userMapper", UserMapper.class); // List<user> userList = userMapper.selectUser(); // for (user user : userList) { // System.out.println(user); // } // System.out.println(userMapper.addUser(new user(4, "eeee", "123123"))); List<user> userList = userMapper.selectUser(); for (user user : userList) { System.out.println(user); } // System.out.println(userMapper.deleteUser(3)); }
以上代码为最终代码,完成相应的事务注册和回滚操作。
测试方法:在UserMapper.xml配置文件中,去修改一下删除操作的代码,使得删除操作会报错,但此时去查看数据表,发现增加操作并没有确认执行或者可以说进行了回滚操作。
在没有运用spring声明事务的时候,我们按照上述的测试方法测试发现,程序报错,但数据表中会多出一条测试数据, 没能完成回滚操作,不符合事务的属性。