一、MySQL事务
简单了解一下 MySQL 事务,参考文章:
MySQL事务主要用于处理操作量大,复杂度高的数据,事务中可能包含一个或多个SQL语句,这些语句要么不执行,要么全部执行成功。
- 事务必须满足四个条件(ACID):
(1)原子性(Atomicity, 或称不可分割性);
(2)一致性(Consistency);
(3)隔离性(Isolation);
(4)持久性(Durability)。 - 事务并发问题:
(1)脏读:一个事务读到另外一个事务还未提交(可能被回滚)的脏数据.
(2)不可重复读: 一个事务执行期间另一事务提交修改,导致第一个事务前后两次查询结果不一致.
(3)幻读: 一个事务执行期间另一事务提交添加数据,导致第一个事务前后两次查询结果到的数据条数不同. - 事务的隔离级别:
(1)读未提交:可能发生脏读、不可重复读、幻读。
(2)读已提交:可以避免发生脏读,但可能发生不可重复读、幻读。
(3)可重复读:可以避免发生脏读、不可重复读,但可能发生幻读。
(4)串行化:可以避免发生脏读、不可重复度、幻读。
二、Spring 整合 MySQL 事务
在执行一组数据库操作时,若在中间环节发生异常或其他中断现象,导致前一部分操作完成,但后一部分操作不能完成,为应对这些中断现象的发生,采用 MySQL 中的事务的概念,事务开始时启动事务,当出现中断现象时,将前面已经完成的操作再回退回去,仿佛一切都没有发生一样,若没有中断现象发生,则在事务的最后提交事务,将对数据库的更新持久化到数据库中。简单的说就是事务中的一组操作,要么全部执行成功,有么全部不执行。
- XML 方式整合 MySQL 事务:
- 导包:
com.springsource.com.mchange.v2.c3p0-0.9.1.2.jar
com.springsource.org.apache.commons.logging-1.1.1.jar
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
mysql-connector-java-8.0.11.jar
spring-aop-5.2.0.RELEASE.jar
spring-aspects-5.2.0.RELEASE.jar
spring-beans-5.2.0.RELEASE.jar
spring-context-5.2.0.RELEASE.jar
spring-core-5.2.0.RELEASE.jar
spring-expression-5.2.0.RELEASE.jar
spring-jdbc-5.2.0.RELEASE.jar
spring-test-5.2.0.RELEASE.jar
spring-tx-5.2.0.RELEASE.jar - Spring 配置文件中添加头部信息,主要还需要添加 aop 和 tx 的信息。
- 配置事务管理器:管理数据源的事务。
<bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
- 配置事务通知:
<tx:advice id="advice" transaction-manager="transactionManager">
<tx:attributes>
<!--
name:指定方法名,给指定的方法添加事务通知;
isolation:设置事务隔离级别;
propagation:设置;事务传播级别;
read-only:若为true表示只读,若为false表示可以读写。
rollback-for:设置默认回滚的异常
-->
<tx:method name="zhuanzhang" isolation="READ_UNCOMMITTED"
propagation="REQUIRED" read-only="false"
rollback-for="java.lang.Exception"/>
...
</tx:attributes>
</tx:advice>
注意点:最好手动设置 rollback-for 属性为 Exception,这样不管在遇到抛出何种异常时都能回滚事务。因为 Spring 默认抛出了未检查的 unchecked 异常(继承自 RuntimeException 的异常)或者 Error 才回滚事务,其他异常不会触发回滚事务。
- 配置 aop,为事务通知指定切入点:
<aop:config>
<aop:pointcut id="pc" expression="execution(public void com.gec.service.Impl.AccountServiceImpl.zhuanzhang(..))"/>
<aop:advisor advice-ref="advice" pointcut-ref="pc"/>
</aop:config>
- 注解方式整合 MySQL 事务:
- 前三步与 xml 的方式一样;
- 使用注解:@Transactional(rollbackFor = Exception.class, isolation = Isolation.READ_UNCOMMITTED,propagation = Propagation.REQUIRED,readOnly = false),若是放在类上,则表示全局配置,类中的方法都遵循该规则;若是放在方法上,则表示值针对该方法。
注意:这里以抛出 IOException 异常为例,这个异常属于 checked exceptions,若没有手动的设置 rollbackFor = Exception.class 的话,当方法抛出异常时,不会进行事务回滚,而这里因为手动设置的 rollbackFor = Exception.class ,所以能正常进行事务回滚。
//放在方法上表示只针对该方法
@Transactional(rollbackFor = Exception.class, isolation = Isolation.READ_UNCOMMITTED, propagation = Propagation.REQUIRED, readOnly = false)
@Override
public void zhuanzhang(int fromId, int toId, double money) throws IOException {
ad.lossMoney(money, fromId);
//减钱
//手动停电
File f = new File("D://test4.txt");//该文件实际不存在
BufferedReader br = new BufferedReader(new FileReader(f));
String s = null;
while((s = br.readLine()) != null){
System.out.println(s);
}
//加钱
ad.addMoney(money, toId);
}
- 在 xml 中开启注解事务支持:< tx:annotation-driven transaction-manager=“transactionManager”/>
<tx:annotation-driven transaction-manager="transactionManager"/>
三、Spring整合MyBatis框架
- 导包:
Spring包及依赖包:
com.springsource.org.apache.commons.logging-1.1.1.jar
com.springsource.org.aopalliance-1.0.0.jar
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
commons-logging-1.1.1.jar
commons-pool-1.5.6.jar
commons-dbcp-1.4.jar
mysql-connector-java-8.0.11.jar
spring-aop-5.1.2.RELEASE.jar
spring-aspects-5.1.2.RELEASE.jar
spring-beans-5.1.2.RELEASE.jar
spring-context-5.2.0.RELEASE.jar
spring-core-5.2.0.RELEASE.jar
spring-expression-5.2.0.RELEASE.jar
spring-jdbc-5.2.0.RELEASE.jar
spring-tx-5.2.0.RELEASE.jar
mybatis-spring-1.2.2.jar(Spring与MyBatis包)
MyBatis包及依赖包:
asm-3.3.1.jar
cglib-2.2.2.jar
javassist-3.17.1-GA.jar
log4j-1.2.17.jar
log4j-api-2.0-rc1.jar
log4j-core-2.0-rc1.jar
mybatis-3.2.7.jar
slf4j-api-1.7.5.jar
slf4j-log4j12-1.7.5.jar - Spring 配置文件中添加头部信息,主要还需要添加 context、aop、tx 的信息。
- 配置数据源,这里使用 dbcp 方式连接 MySQL 数据库:
<bean name="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/test"/>
<property name="username" value="root"/>
<property name="password" value="1234"/>
</bean>
- 配置 SqlSessionFactoryBean,相当于原先 MyBatis 中的 SqlSessionFactory 对象。
<bean name="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 获取数据库连接-->
<property name="dataSource" ref="dataSource"/>
<!-- 读取 MyBatis 配置文件-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<!-- 配置 Mapper 映射文件,也可直接在 MyBatis 配置文件中配置-->
<property name="mapperLocations">
<list>
<value>classpath:com/gec/mapper/UserMapper.xml</value>
</list>
</property>
</bean>
- 配置 SqlSessionTemplate,相当于原先 MyBatis 中的 SqlSession 对象。
<bean name="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
<!--需要以构造方法注入 SqlSessionFactory 对象-->
<constructor-arg name="sqlSessionFactory" ref="sqlSessionFactoryBean"/>
</bean>
配置完这一步后,就可以在测试类中获取 SqlSessionTemplate 对象,执行增删改查了。
- 为优化在 DAO 层过多的重复获取 SqlSessionTemplate 对象的操作,可以让 DAO 层的实现类继承 SqlSessionDaoSupport 类,通过父类的 getSession 方法获取 SqlSessionTemplate 对象。只需要给 DAO 的对象注入 SQLSessionFactoryBean 对象即可。
- Mapper 层实际上已经能实现对数据库的操作,所以为了删除 DAO 层可以做以下优化,通过 xml 中配置 MapperFactoryBean 对象,管理 Mapper 层接口,为 Mapper 层接口生成实现类。
<bean name="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<!-- 指定管理的 Mapper 接口-->
<property name="mapperInterface" value="com.gec.mapper.UserMapper"/>
<!-- 注入 SqlSessionFactory-->
<property name="sqlSessionFactory" ref="sqlSessionFactoryBean"/>
</bean>
- 步骤 7 的代码一次只能管理一个 Mapper 接口,为了避免写过多重读代码,可以替换为配置 MapperScannerConfigurer 对象,指定包路径,就会自动管理包下的所有 Mapper 接口,为每个 Mapper 接口 创建实现类,实现类的名称一般为首字母小写的接口名。
<!-- 扫描 mapper 包,自动为 mapper 创建实例化对象-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.gec.mapper"/>
</bean>