事务相关知识
事务特性:ACID
事务并发的问题:脏读、不可重复读(行记录)、幻读(虚读)(表记录)
事务的隔离级别:1读未提交、2读已经提交数据 4 可重复读 8串行化
Spring实现事务
Spring需要干的事情
(1)封装相关的代码:打开事务、提交事务、回滚事务
(2)提供 事务操作的对象
因为在不同的平台,操作事务的代码各不相同,
Spring提供一个接口,名字:PlatformTransactionManager接口,针对不同的平台提供不同的实现类。
(3)数据库相关的设置
隔离级别、是否只读、事务的传播行为
传播行为
Spring实现事务方法一:XML配置,模拟转账
数据库准备:
项目结构
UserDao
package com.hyx.k_transaction_xml.dao;
public interface UserDao {
public void reduceBalance(Integer userId, Double money);
public void increaseBalance(Integer userId, Double money);
}
UserDaoImpl
package com.hyx.k_transaction_xml.dao.impl;
import com.hyx.k_transaction_xml.dao.UserDao;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
public class UserDaoImpl extends JdbcDaoSupport implements UserDao {
//减少余额
public void reduceBalance(Integer userId, Double money) {
this.getJdbcTemplate().update( "update tb_user set balance = balance - ? where uid = ?",money,userId );
}
//增加余额
public void increaseBalance(Integer userId, Double money) {
this.getJdbcTemplate().update( "update tb_user set balance = balance + ? where uid = ?",money,userId );
}
}
UserService
package com.hyx.k_transaction_xml.service;
public interface UserService {
public void transfer(Integer to, Integer from, double money) ;
}
UserServiceImpl
package com.hyx.k_transaction_xml.service.impl;
import com.hyx.k_transaction_xml.dao.UserDao;
import com.hyx.k_transaction_xml.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao ;
public void transfer(Integer to, Integer from, double money){
userDao.increaseBalance(to,money );
int i = 5/0; //模拟错误
userDao.reduceBalance( from,money );
}
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
}
Spring配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.2.xsd ">
<!--读取配置文件-->
<context:property-placeholder location="classpath:db.properties"></context:property-placeholder>
<!--C3P0连接池-->
<bean name="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test?characterEncoding=UTF-8"></property>
<property name="user" value="${jdbc.username}"></property>
<property name="password" value="123"></property>
</bean>
<!--核心事务管理器-->
<bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--事务管理对象-->
<bean name="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="transactionManager"></property>
</bean>
<!--通知:事务代码,我们需要将事务代码 织入 service 中方法 -->
<tx:advice id ="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="save*" isolation="DEFAULT" read-only="false" propagation="REQUIRED"/>
<tx:method name="persist*" isolation="DEFAULT" read-only="false" propagation="REQUIRED"/>
<tx:method name="modify*" isolation="DEFAULT" read-only="false" propagation="REQUIRED"/>
<tx:method name="update*" isolation="DEFAULT" read-only="false" propagation="REQUIRED"/>
<tx:method name="get*" isolation="DEFAULT" read-only="true" propagation="REQUIRED"/>
<tx:method name="find*" isolation="DEFAULT" read-only="true" propagation="REQUIRED"/>
<tx:method name="delete*" isolation="DEFAULT" read-only="false" propagation="REQUIRED"/>
<tx:method name="remove*" isolation="DEFAULT" read-only="false" propagation="REQUIRED"/>
<tx:method name="transfer*" isolation="DEFAULT" read-only="false" propagation="REQUIRED"/> </tx:attributes>
</tx:advice>
<!--将通知织入目标-->
<aop:config>
<aop:pointcut id="hyxTc" expression="execution(* com.hyx.k_transaction_xml.service.impl.*ServiceImpl.*(..))"></aop:pointcut>
<aop:advisor advice-ref="txAdvice" pointcut-ref="hyxTc"></aop:advisor>
</aop:config>
<!--DAO-->
<bean name="userDao" class="com.hyx.k_transaction_xml.dao.impl.UserDaoImpl">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--Service-->
<bean name="userService" class="com.hyx.k_transaction_xml.service.impl.UserServiceImpl">
<property name="userDao" ref="userDao"></property>
</bean>
</beans>
测试类
package com.hyx.k_transaction_xml;
import com.hyx.k_transaction_xml.service.UserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:k_tran_xml.xml")
public class Demo {
@Autowired
private UserService userService;
@Test
public void fun(){
userService.transfer(1,2,50);
}
}
当UserServiceImpl中的int i= 5 / 0;没有注释时,无法完成数据库操作
注释后,可以对数据库进行操作
Spring实现事务方法二:注解实现,模拟转账
首先操作UserServiceImpl为其添加注解
package com.hyx.l_transaction_annotaion.service.impl;
import com.hyx.l_transaction_annotaion.dao.UserDao;
import com.hyx.l_transaction_annotaion.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
//isolation设置事务隔离级别,设置事务传播方式,readOnly是否只读数据库
@Transactional(isolation = Isolation.DEFAULT,propagation = Propagation.REQUIRED,readOnly = false)
@Repository
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao ;
public void transfer(Integer to, Integer from, double money){
userDao.increaseBalance(to,money );
// int i = 5/0; //模拟错误
userDao.reduceBalance( from,money );
}
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
}
改写Spring配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.2.xsd ">
<!--读取配置文件-->
<context:property-placeholder location="classpath:db.properties"></context:property-placeholder>
<!--C3P0连接池-->
<bean name="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test?characterEncoding=UTF-8"></property>
<property name="user" value="${jdbc.username}"></property>
<property name="password" value="123"></property>
</bean>
<!--核心事务管理器-->
<bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--事务管理对象-->
<bean name="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="transactionManager"></property>
</bean>
<!--开启注解事务管理-->
<tx:annotation-driven />
<!--注解扫描-->
<context:component-scan base-package="com.hyx.l_transaction_annotaion"></context:component-scan>
<!--DAO-->
<bean name="userDao" class="com.hyx.l_transaction_annotaion.dao.impl.UserDaoImpl">
<property name="dataSource" ref="dataSource"></property>
</bean>
</beans>
测试类
package com.hyx.l_transaction_annotaion;
import com.hyx.l_transaction_annotaion.service.UserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:l_tran_parl.xml")
public class Demo {
@Autowired
private UserService userService;
@Test
public void fun(){
userService.transfer(1,2,50);
}
}
当UserServiceImpl中的int i= 5 / 0;没有注释时,无法完成数据库操作
注释后,可以对数据库进行操作