案例名称:myspring_day03_tx
一句话说明:本文讲解如何在spring中通过xml配置方式,以aop思想为核心,来配置事务管理。
详细说明:此案例实现转账业务,service(业务实现类)在调用dao(数据库操作类)实现业务时,需要为某些方法添加事务管理,以保证数据的正确。此案例利用aop思想,通过xml配置的方式,将通知(即事务管理,由事务核心管理器提供,见配置文件第6步)织入目标(MoneyService类)的切点(transf方法),以达到增强切点(transf方法)的作用(为切点提供了事务管理)。
步骤:
1.准备dao接口及其实现类,依赖DataSource(继承JdbcDaoSupport,获得JdbcTemplate,使用其update执行sql语句)
package dao;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
/**
* dao类,提供操作数据库的方法,供service使用
* @author 74047
*/
public class MoneyDao extends JdbcDaoSupport implements dao {
public void addMoney(double money, Integer id) {
// 通过父类JdbcDaoSupport的getJdbcTemplate方法获得JdbcTemplate(jdbc模板),
//但是注意继承之后,会导致此类依赖dataSource,记得注入
getJdbcTemplate().update("update t_account set money=money+? where id = ?", money, id);
}
public void reduceMoney(double money, Integer id) {
getJdbcTemplate().update("update t_account set money=money-? where id = ?", money, id);
}
}
2.准备service接口及其实现类,依赖dao实现类(调用dao实现类的方法完成业务)
package service;
import dao.MoneyDao;
/**
* service类,通过调用dao的方法完成转账业务逻辑
* 此类是事务aop增强的目标类
* @author 74047
*
*/
public class MoneyService implements service {
//需要依赖dao
private MoneyDao dao;
/**
* 此为转账业务,也是事务aop增强的切点
*/
public void transf(Integer from, Integer to, Integer money) {
dao.reduceMoney(money, from);
//故意制造异常,测试事务是否生效
// int i = 1 / 0;
dao.addMoney(money, to);
}
public void setDao(MoneyDao dao) {
this.dao = dao;
}
}
3.配置文件(放在src下,名为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: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">
<!-- 1.配置连接池 -->
<bean name="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="jdbcUrl" value="jdbc:mysql:///springdb"></property>
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="user" value="root"></property>
<property name="password" value=""></property>
</bean>
<!-- 2.配置dao实现类,依赖连接池(1) -->
<bean name="moneyDaoImpl" class="dao.MoneyDao">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 3.配置service实现类,依赖dao实现类(2) -->
<bean name="moneyServiceImpl" class="service.MoneyService">
<property name="dao" ref="moneyDaoImpl"></property>
</bean>
<!-- 4.配置事务核心管理器,依赖连接池(1) -->
<bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 5.配置事务模板,依赖事务核心管理器(4) -->
<bean name="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="transactionManager"></property>
</bean>
<!-- 6.配置事务通知,依赖事务核心管理器(4) -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="transf" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false"/>
</tx:attributes>
</tx:advice>
<!-- 7.配置织入 -->
<aop:config>
<aop:pointcut expression="execution(* service.*Service.*(..))" id="txpc"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txpc"/>
</aop:config>
</beans>
4.测试
package demo;
import javax.annotation.Resource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import service.service;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class Demo {
// 用不了@Resource的话,可能需要导包com.springsource.javax.annotation-1.0.0.jar
@Resource(name = "moneyServiceImpl")
// 这里需要类型是接口类型,用实现类的类型会报下面的错
/*
* org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean
* named 'moneyServiceImpl' must be of type [service.MoneyService], but was
* actually of type [com.sun.proxy.$Proxy13]
*/
// 拓展阅读:https://stackoverflow.com/questions/14509142/beannotofrequiredtypeexception
private service ms;
@Test
public void fun1() {
ms.transf(1, 2, 100);
}
}
第一次使用spring的过程中遇到很多坑,挑选典型的若干错误如下:
1.准备dao接口及其实现类时,sql语句错误,且此类要实现接口
2.准备service接口及其实现类时,要为依赖的dao添加set 方法,且此类要实现接口
3.配置文件中,配置service实现类时,属性是引用类型时要用ref注入
4.配置文件中,配置事务模板时,要加id和transaction-manager属性
5.配置文件中,配置事务模板时,切点表达式的第一个*(表示返回值任意)之后要带一个空格execution(* service.*Service.*(..))
6.测试类Demo里获取service实现类进行事务操作时,service实现类的类型需要是接口类型(父类引用),且通过容器获得实现类对象时,需要使用@Resource(name="moneyServiceImpl")注解,如果不能使用此注解,则需要导入com.springsource.javax.annotation-1.0.0.jar包