1.事务
1.1.事务的四大特性ACID
(1)原子性(Atomicity)事务所包含的操作,要么全部成功,要么全部失败回滚,具有不可分割性。
(2)一致性(Consistency)事务执行前和执行之后都必须处于一致性状态。
(3)隔离性(Isolation)多个并发事务之间要相互隔离。
(4)持久性(Durability)事务一旦提交,数据库中的数据就被持久化。
1.2.事务的并发问题
(1)脏读:当一个事务在修改数据还未提交时,另一个事务来访问该事务。
(2)不可重复读:一个事务在多次查询的间隔,被另一个事务修改并提交了数据,查询到不同的结果。
(3)幻读(虚读):一个事务在对所有数据进行修改时,另外一个事务插入了一条数据,导致修改完之后,发现仍有一条记录没有被修改。以为自己产生了幻觉。
1.3.事务的隔离级别
(1)Read uncommitted(读未提交)
(2)Read committed(读已提交):避免脏读
(3)Repeatable read(可重复度):避免脏读 不可重复读
(4)Serializable(串行化):避免幻读
2.spring的事务管理
2.1.准备数据表
2.2.准备转账的业务代码
package work.yuegege.domain;
public class Account {
private int id;
private String name;
private double account;
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 double getAccount() {
return account;
}
public void setAccount(double account) {
this.account = account;
}
@Override
public String toString() {
return "Account [id=" + id + ", name=" + name + ", account=" + account
+ "]";
}
}
package work.yuegege.dao;
public interface TransactionAccountDao {
public void addAccount(String name,double account);
public void subAccount(String name,double account);
}
package work.yuegege.daoImpl;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import work.yuegege.dao.TransactionAccountDao;
public class TransactionAccountDaoImpl extends JdbcDaoSupport implements TransactionAccountDao{
@Override
public void addAccount(String name, double account) {
super.getJdbcTemplate().update("update account set money=money+? where user=?",account,name);
}
@Override
public void subAccount(String name, double account) {
super.getJdbcTemplate().update("update account set money=money-? where user=?",account,name);
}
}
package work.yuegege.service;
public interface AccountService {
public void transaction(String from ,String to,double money);
}
2.3.编程式事务管理
package work.yuegege.service;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
import work.yuegege.dao.TransactionAccountDao;
public class AccountServiceImpl implements AccountService {
private TransactionAccountDao transactionAccountDao;
private TransactionTemplate transactionTemplate;
public void transaction(final String from , final String to , final double money) {
//这里运用编程式事务管理 TransactionTemplate封装了事务的打开,回滚,和事务的关闭方法。
//但这种方法不适合多业务的操作,因为每个业务都需要将代码放在execute()方法中。
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus arg0) {
transactionAccountDao.subAccount(from, money);
int i=1/0;
transactionAccountDao.addAccount(to, money);
}
});
}
public void setTransactionAccountDao(TransactionAccountDao transactionAccountDao) {
this.transactionAccountDao = transactionAccountDao;
}
public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
this.transactionTemplate = transactionTemplate;
}
}
applicationContext.xml
<?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:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.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 ">
<!-- 1.配置连接池 -->
<bean name="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="jdbcUrl" value="jdbc:mysql:///students"></property>
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="user" value="root"></property>
<property name="password" value="root"></property>
</bean>
<!--2.将JDBCTemplate放入spring容器 -->
<!-- <bean name="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean> -->
<!-- 编程式事务管理 -->
<!--1.配置核心事务管理器 -->
<bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--2.配置事务模板对象 -->
<bean name="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="transactionManager"></property>
</bean>
<!--3.将AccountDaoImpl放入spring容器 -->
<bean name="transactionAccountDao" class="work.yuegege.daoImpl.TransactionAccountDaoImpl">
<!-- <property name="jbbcTemplate" ref="jdbcTemplate"></property> -->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--4.将AccountServiceImpl放入spring容器 -->
<bean name="accountService" class="work.yuegege.service.AccountServiceImpl">
<property name="transactionAccountDao" ref="transactionAccountDao"></property>
<property name="transactionTemplate" ref="transactionTemplate"></property>
</bean>
</beans>
编写测试类
@Test
public void test8(){
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("work/yuegege/test/applicationContext.xml");
AccountService accountService=(AccountService) applicationContext.getBean("accountService");
accountService.transaction("lucy", "mengyue", 500);
System.out.println("转账成功");
}
结果:
因为转账过程发生了异常,所以回滚。转账没有成功
2.4.xml方式配置spring的声明式事务管理
package work.yuegege.service;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
import work.yuegege.dao.TransactionAccountDao;
public class AccountServiceImpl implements AccountService {
private TransactionAccountDao transactionAccountDao;
public void transaction(final String from , final String to , final double money) {
transactionAccountDao.subAccount(from, money);
int i=1/0;
transactionAccountDao.addAccount(to, money);
}
public void setTransactionAccountDao(TransactionAccountDao transactionAccountDao) {
this.transactionAccountDao = transactionAccountDao;
}
}
applicationContext.xml的配置
<?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:p="http://www.springframework.org/schema/p"
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.3.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 ">
<!-- 1.配置连接池 -->
<bean name="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="jdbcUrl" value="jdbc:mysql:///students"></property>
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="user" value="root"></property>
<property name="password" value="root"></property>
</bean>
<!--2.配置核心事务管理器 -->
<bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--3.配置事务通知 -->
<!-- isolation 表示事务的隔离级别 propagation事务的传播行为:默认值为:REQUIRED
read-only 表示是否只读 默认值:false -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="transaction" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false"/>
</tx:attributes>
</tx:advice>
<!--4.将通知织入目标对象 -->
<aop:config>
<!--配置切点 -->
<aop:pointcut expression="execution(* work.yuegege.service.*ServiceImpl.*(..))" id="txPointCut"/>
<!--配置切面 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
</aop:config>
<!-- 将TransactionAccountDaoImpl放入spring容器-->
<bean name="transactionAccountDao" class="work.yuegege.daoImpl.TransactionAccountDaoImpl">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--将accountService放入spring容器 -->
<bean name="accountService" class="work.yuegege.service.AccountServiceImpl">
<property name="transactionAccountDao" ref="transactionAccountDao"></property>
</bean>
</beans>
编写测试类:
//xml方式进行声明式事务管理测试
@Test
public void test9(){
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
AccountService accountService=(AccountService) applicationContext.getBean("accountService");
accountService.transaction("lucy", "mengyue", 500);
System.out.println("转账成功");
}
结果与上面一样。
2.5.注解方式配置spring的声明式事务管理
2.5.1.注入TransactionAccountDaoImpl和JdbcTemplate
在上面的业务代码中加上红色的注解
import javax.annotation.Resource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import org.springframework.stereotype.Component;
import work.yuegege.dao.TransactionAccountDao;
@Component("transactionAccountDao")
public class TransactionAccountDaoImpl implements TransactionAccountDao{
private JdbcTemplate jdbcTemplate;
@Resource(name = "jdbcTemplate")
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
2.5.2.注入AccountServiceImpl和事务
@Component("accountService")
public class AccountServiceImpl implements AccountService {
@Resource(name="transactionAccountDao")
private TransactionAccountDao transactionAccountDao;
@Transactional(isolation=Isolation.REPEATABLE_READ,propagation=Propagation.REQUIRED,readOnly=false)
public void transaction(final String from , final String to , final double money) {
2.5.3.applicationContext.xml配置文件
<?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:p="http://www.springframework.org/schema/p"
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.3.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 ">
<!-- 1.配置连接池 -->
<bean name="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="jdbcUrl" value="jdbc:mysql:///students"></property>
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="user" value="root"></property>
<property name="password" value="root"></property>
</bean>
<context:component-scan base-package="work.yuegege.*"></context:component-scan>
<!-- 开启注解管理事务 -->
<tx:annotation-driven/>
<!--2.将JDBCTemplate放入spring容器 -->
<bean name="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--配置核心事务管理器 -->
<bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
</beans>
2.5.4.测试类
@Test
public void test10(){
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("work/yuegege/service/applicationContext.xml");
AccountService accountService=(AccountService) applicationContext.getBean("accountService");
accountService.transaction("lucy", "mengyue", 500);
System.out.println("转账成功");
}
2.5.5结果
结果与上面一样。