上一章节,我使用了Spring的JDBCTemplate完成了对数据库的操作。但是在一般的情况下,我们对数据库操作时都需要进行事务管理的操作,防止数据库中的数据出现读取到错误提交的数据,如A转账给B,A扣款了,B没有收到款(脏读)等问题。
事务需要满足4大特性:
- 原子性 完成对数据的修改 要么一起执行,要么一起不执行 强调整体
- 一致性 存储的数据约束应该一致
- 隔离性 防止冲突 并发的事务是相互隔离 不互相影响的
- 持久性 确保已经提交的事务内容不丢失
涉及:Mysql5.7,Eclipse javaEE,Spring3.2,JDK1.7
此处使用的练习案例是转账。实现A对B进行转账,如果期间出现了异常则事务回滚。
首先,需要创建一个表 这里我们就只用到用户名和账内金额两个字段即可
CREATE TABLE `transfer` (
`username` varchar(15) DEFAULT NULL,
`money` int(10) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
insert into transfer (`username`, money) value ('pp', 10000);
insert into transfer (`username`, money) value ('qq', 10000);
在其中存储两个用户,设定账户都为10000。
在java中创建一个项目,导入相应的包(除了Spring必要包外 tx事务处理包,orm包,jdbc包,mysql驱动包,c3p0等)
Dao类
package com.hpp.dao;
public interface TransferDao {
public void in(String inner, Integer money);
public void out(String outer, Integer money);
}
Dao实现类
package com.hpp.dao.impl;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import com.hpp.dao.TransferDao;
//需要继承JdbcDaoSupport 其中有dataSource变量 在xml文件中注入数据源
public class TransferDaoImpl extends JdbcDaoSupport implements TransferDao{
@Override
public void in(String inner, Integer money) {
this.getJdbcTemplate().update("update transfer set money=money-? where username=? ",
money,inner);
}
@Override
public void out(String outer, Integer money) {
this.getJdbcTemplate().update("update transfer set money=money+? where username=? ",
money,outer);
}
}
Service类
package com.hpp.service;
public interface TransferService {
void transfer(String outer, String inner, Integer money);
}
Service实现类
package com.hpp.service.impl;
import com.hpp.dao.TransferDao;
import com.hpp.service.TransferService;
public class TransferServiceImpl implements TransferService{
private TransferDao transferDao;
public void setTransferDao(TransferDao transferDao) {
this.transferDao = transferDao;
}
@Override
public void transfer(final String outer, final String inner, final Integer money) {
transferDao.in(outer, money);
// 手动设置的异常
// int i = 1/0;
transferDao.out(inner, money);
}
}
JdbcInfo.properties文件,此处用到xml文件加载数据库参数,让数据库参数可配置
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.jdbcUrl=jdbc:mysql://127.0.0.1:3306/mydb
jdbc.user=root
jdbc.password=8469
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:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
<!-- 读取properties文件 -->
<context:property-placeholder location="classpath:com/hpp/properties/JdbcInfo.properties"/>
<!-- 加载数据源 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driverClass}"></property>
<property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
<property name="user" value="${jdbc.user}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<bean id="transferDao" class="com.hpp.dao.impl.TransferDaoImpl">
<property name="dataSource" ref="dataSource"></property>
</bean>
<bean id="transferService" class="com.hpp.service.impl.TransferServiceImpl">
<property name="transferDao" ref="transferDao"></property>
</bean>
<!-- 事务管理器 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 事务通知 -->
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<!--
对指定方法进行事务管理
propagation 取值:
REQUIRED为默认值 支持当前事务,A如果有事务,B将使用该事务;如果A没有事务,B将创建一个新的事务
-->
<tx:method name="transfer" propagation="REQUIRED" isolation="DEFAULT"/>
</tx:attributes>
</tx:advice>
<!-- AOP编程 -->
<aop:config>
<aop:advisor advice-ref="txAdvice" pointcut="execution(* com.hpp.service..*.*(..))"/>
</aop:config>
</beans>
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<!--
对指定方法进行事务管理
propagation 取值:
REQUIRED为默认值 支持当前事务,A如果有事务,B将使用该事务;如果A没有事务,B将创建一个新的事务
-->
<tx:method name="transfer" propagation="REQUIRED" isolation="DEFAULT"/>
</tx:attributes>
</tx:advice>
<!-- AOP编程 -->
<aop:config>
<aop:advisor advice-ref="txAdvice" pointcut="execution(* com.hpp.service..*.*(..))"/>
</aop:config>
</beans>
测试类
package com.hpp;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.hpp.service.TransferService;
public class TestTransfer {
@Test
public void test1() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
TransferService transferService = (TransferService) applicationContext.getBean("transferService");
transferService.transfer("pp", "qq", 1000);
}
}
当没有异常发生时,转账操作事务可以正常提交,当其中出现了异常,事务会回滚。此处我使用整数除以零来形成一个异常进行测试。
刚接触内容较浅显,主要是想让自己在将来使用的时候有这方面的印象,因此记下此学习记录。如有错误以及需要改进的地方 劳烦指出。