spring ——事务管理
目录
说明
本例子不是web项目,主要内容是介绍四个spring事务管理方式。
需要引入spring-jdbc.jar(事务管理器)和spring-tx.jar(事务管理模板)
使用了连接池,资源文件,spring JDBC模板
项目
在四种事务管理的实现中有些内容是重复的,在下面只展示一次。
重复的内容有一下部分
资源文件jdbc.properties
代码片`.
jdbc.driverClassName=com.hxtt.sql.access.AccessDriver
jdbc.url=jdbc:Access:///F://HrMS.accdb
jdbc.username=
jdbc.password=
说明:
key值要和配置文件的${ }中的值一样。
DAO层类AccountDAO.java
代码片`.
package text_transaction;
import java.math.BigDecimal;
import org.springframework.jdbc.core.JdbcTemplate;
//DAO层
public class AccountDAO {
JdbcTemplate jdbcTemplate;//JDBC模板
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public AccountDAO() {
}
public void inMoney(String name, double money) {
// 转入
String sql = "update account set money = money + ? where name= ?";
jdbcTemplate.update(sql, money, name);
System.out.println("给" + name + "转入" + money + "元钱");
}
public void outMoney(String name, double money) throws MoneyException {
// 转出
//查询余额
double conMoney = jdbcTemplate.queryForObject("select money from account where name=?", Double.class, name);
BigDecimal data1 = new BigDecimal(conMoney);
BigDecimal data2 = new BigDecimal(money);
if(data1.compareTo(data2)==-1){//比较double类型的数的大小
throw new MoneyException("余额不足");//手动抛出异常
}
String sql = "update account set money = money - ? where name= ?";
jdbcTemplate.update(sql, money, name);
System.out.println("从" + name + "转出" + money + "元钱");
}
}
说明:
1. 使用了springJDBC模板。
2. 类的包名记得根据下面的配置文件进行修改。
自定义异常类
代码片
.
package text_transaction1;
//自定义异常
public class MoneyException extends Exception {
String message;
public MoneyException(String message) {
this.message=message;
}
@Override
public String getMessage() {
// TODO Auto-generated method stub
return message;
}
}
说明:
- 在赚钱事务中,需要判断余额情况,所以在余额不足时需要主动抛出异常。
测试类text.java
代码片
.
package text_transaction;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class text {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("text_transaction/applicationContext.xml");
AccountService accountService = (AccountService) ctx.getBean("accountService");
//转出账号,转入账号,金额
accountService.transfer("李四", "张三", 1000);
}
}
说明:
- 用主方法进行测试运行的,没有用Junit。
- 类的包名记得根据下面的配置文件进行修改。
- 创建上下文对象时引用配置文件的路径也要根据实际情况修改。
【下面分别展示 四种不同的部分】
第一种——编程式的事务管理
通过事务管理模板来实现对事务的管理,在当前类中操作方法。
配置文件
代码片
.
<?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: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.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 进行jdbc事务管理时:在配置url时,数据库的路径要写成 F://HrMS.accdb,而不能写成F:HrMS.accdb,写错了找不到;但是配置hibernate事务时F:HrMS.accdb写也行-->
<!-- 配置连接池,引入外部属性文件
<context:property-placeholder location="text_transaction1/jdbc.properties" />
-->
<!-- 配置连接池 c3p0
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driverClassName}"></property>
<property name="jdbcUrl" value="${jdbc.url}"></property>
<property name="user" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
-->
<!-- 配置连接池 Apache
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
-->
<!-- 配置连接池 ODBC jdk 1.8一下版本可以使用
<bean id="dataSource" class="XXX.XXX.XXX">
<property name="driverClass" value="sun.jdbc.odbc.JdbcOdbcDriver"></property>
<property name="jdbcUrl" value="jdbc:odbc:driver={Microsoft Access Driver (*.mdb)};DBQ=F://HrMS.accdb "></property>
<property name="user" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
-->
<!-- 引入资源文件 jdbc.properties -->
<bean
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<value>text_transaction1/jdbc.properties</value>
</property>
</bean>
<!-- 配置连接池 spring自带的类 -->
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
<!-- 事务管理器 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 事务管理模板 -->
<bean id="transactionTemplate"
class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="transactionManager" />
</bean>
<!-- Service层,注入事务管理模板 -->
<bean id="accountService" class="text_transaction1.AccountService">
<property name="transactionTemplate" ref="transactionTemplate" />
<property name="accountDAO" ref="accountDAO" />
</bean>
<!-- spring jdbc模板 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- DAO层,注入jdbc模板 -->
<bean id="accountDAO" class="text_transaction1.AccountDAO">
<property name="jdbcTemplate" ref="jdbcTemplate" />
</bean>
</beans>
说明:
- 上述介绍了两种引用外部资源文件的方式;三种使用不同缓冲池的方式;两种配置access数据库的方式。
- 进行jdbc事务管理时:在配置url时,数据库的路径要写成 F://HrMS.accdb,而不能写成F:HrMS.accdb,写错了找不到;但是配置hibernate事务时F:HrMS.accdb写也行。
- ODBC jdk 1.8一下版本可以使用
- 使用Access_JDBC30.jar JDBC驱动是有限制的,免费版:查询和插入的次数是受限制的
事务管理代码
代码片
.
package text_transaction1;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
import text_transaction1.MoneyException;
//Service层
public class AccountService {
AccountDAO accountDAO;
TransactionTemplate transactionTemplate;
//注入DAO类
public void setAccountDAO(AccountDAO accountDAO) {
this.accountDAO = accountDAO;
}
//注入事务管理模板
public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
this.transactionTemplate = transactionTemplate;
}
public void transfer(String outName,String inName,double money) {
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
// TODO Auto-generated method stub
try {
accountDAO.outMoney(outName, money);
// int d=1/0;//除0异常
accountDAO.inMoney(inName, money);
} catch (MoneyException e) {
System.out.println("转出异常:"+e.getMessage());
}
}
});
}
}
说明:
- 使用事务管理模板类进行事务管理,在transactionTemplate的execute(…)方法中使用匿名类,在doInTransactionWithoutResult(…)方法中进行事务管理。
- TransactionStatus :记录事务运行的具体状态。
第二种——基于代理的事务管理的方式
使用事务的代理来管理事务,包装了一个代理对象,增强其功能,用代理bean来代替目标bean进行操作
配置文件
代码片
.
<?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: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.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 引入资源文件 jdbc.properties -->
<bean
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<value>text_transaction2/jdbc.properties</value>
</property>
</bean>
<!-- 配置连接池 spring自带的类 -->
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
<!-- 事务管理器 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 事务管理的代理工厂 -->
<bean id="accountServiceProxyFactory"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<!-- 代理目标 -->
<property name="target" ref="accountService"></property>
<!-- 事务管理器 -->
<property name="transactionManager" ref="transactionManager"></property>
<!-- 设置事务的属性 -->
<property name="transactionAttributes">
<props>
<!-- 设置事务的属性:传播行为REQUIRED,只读 -->
<!-- <prop key="get*">PROPAGATION_REQUIRED,readOnly</prop> -->
<!-- 事务管理代理目标的transfer方法,属性是:传播行为REQUIRED和隔离级别默认,回滚异常-Exception,不回滚异常+Exception -->
<prop key="transfer">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
<!-- Service层,注入事务管理模板 -->
<bean id="accountService" class="text_transaction2.AccountService">
<property name="accountDAO" ref="accountDAO" />
</bean>
<!-- spring jdbc模板 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- DAO层,注入jdbc模板 -->
<bean id="accountDAO" class="text_transaction2.AccountDAO">
<property name="jdbcTemplate" ref="jdbcTemplate" />
</bean>
</beans>
说明:
可以定义管理事务的属性
注意
测试类中和其他的方式有所不同:
------这里get的bean是代理的bean,然后强制转换为目标bean来使用的。------
package text_transaction2;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class text {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("text_transaction2/applicationContext.xml");
AccountService accountService = (AccountService) ctx.getBean("accountServiceProxyFactory");//使用代理bean来表示目标
//转出账号,转入账号,金额
accountService.transfer("张三", "李四", 500);
}
事务管理代码
代码片
.
package text_transaction2;
import text_transaction2.MoneyException;
//Service层
public class AccountService {
AccountDAO accountDAO;
// 注入DAO类
public void setAccountDAO(AccountDAO accountDAO) {
this.accountDAO = accountDAO;
}
public void transfer(String outName,String inName,double money) {
try {
accountDAO.outMoney(outName, money);
// int d=1/0;//除0异常
accountDAO.inMoney(inName, money);
} catch (MoneyException e) {
System.out.println("转出异常:"+e.getMessage());
}
}
}
说明:
下面三种方式的事务管理代码是相同的。
第三种——基于AOP+tx的事务管理
采用AOP的切面方式来管理一类事务,匹配切点的对象都会进行事务管理
配置文件
代码片
.
<?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: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.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 引入资源文件 jdbc.properties -->
<bean
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<value>text_transaction3/jdbc.properties</value>
</property>
</bean>
<!-- 配置连接池 spring自带的类 -->
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
<!-- 事务管理器 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 事务的通知transactionManager,增强的通知 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!-- 事务管理的属性 -->
<tx:attributes>
<!-- name:目标的方法,
propagation:传播行为,
isolation:隔离级别,
read-only:只读
rollback-for:异常回滚
no-rollback-for 异常不回滚
timeout:过期时间,-1为不过期-->
<tx:method name="transfer"
propagation="REQUIRED"
isolation="DEFAULT"
read-only="false"
timeout="-1"/>
</tx:attributes>
</tx:advice>
<aop:config>
<!-- 切点 -->
<aop:pointcut expression="execution(* text_transaction3.AccountService.*(..))" id="pointcut"/>
<!-- 切面 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut"/>
</aop:config>
<!-- Service层,注入事务管理模板 -->
<bean id="accountService" class="text_transaction3.AccountService">
<property name="accountDAO" ref="accountDAO" />
</bean>
<!-- spring jdbc模板 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- DAO层,注入jdbc模板 -->
<bean id="accountDAO" class="text_transaction3.AccountDAO">
<property name="jdbcTemplate" ref="jdbcTemplate" />
</bean>
</beans>
说明:
可以定义管理事务的属性
事务管理代码
代码片
.
同上二
第四种——基于注解的事务管理
通过注解来标识需要事务管理的bean或者方法
配置文件
代码片
.
<?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: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.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 开启注解扫描 -->
<context:component-scan base-package="text_transaction4"/>
<!-- 引入资源文件 jdbc.properties -->
<bean
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<value>text_transaction4/jdbc.properties</value>
</property>
</bean>
<!-- 配置连接池 spring自带的类 -->
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
<!-- 事务管理器 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- Service层,注入事务管理模板 -->
<bean id="accountService" class="text_transaction4.AccountService">
<property name="accountDAO" ref="accountDAO" />
</bean>
<!-- spring jdbc模板 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- DAO层,注入jdbc模板 -->
<bean id="accountDAO" class="text_transaction4.AccountDAO">
<property name="jdbcTemplate" ref="jdbcTemplate" />
</bean>
<!-- 开启注解事务 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
事务管理代码
代码片
.
package text_transaction4;
import org.springframework.transaction.annotation.Transactional;
import text_transaction4.MoneyException;
//Service层
@Transactional(
isolation=Isolation.DEFAULT,
propagation=Propagation.REQUIRED,
readOnly=false,
timeout=-1)
public class AccountService {
AccountDAO accountDAO;
// 注入DAO类
public void setAccountDAO(AccountDAO accountDAO) {
this.accountDAO = accountDAO;
}
public void transfer(String outName,String inName,double money) {
try {
accountDAO.outMoney(outName, money);
// int d=1/0;//除0异常
accountDAO.inMoney(inName, money);
accountDAO.conMoney();
} catch (MoneyException e) {
System.out.println("转出异常:"+e.getMessage());
}
}
}
说明
使用了注解@Transactional,可以定义管理事务的属性
总结
1. 在进行测试时出现了很多异常,如找不到数据库,因为spring引入的包不是一个版本的出现的异常。
2. 事务是原子操作,可以对数据的操作进行保护。
3. 耐心分析异常很重要。
**引用包是最大的困难(一步一步来)**