AOP应用——事务管理
来源:互联网 发布:易语言游戏多开器源码 编辑:程序博客网 时间:2024/06/06 19:35
相信大家对Spring的事务管理并不陌生,再推荐一篇AOP切入点表达式的文章,我把重点放在AOP实现上。在项目开发过程中通常用Hibernate或MyBatis(iBatis)作为持久层,因此Demo基于Spring+MyBatis实现转账业务。时间很宝贵,先上代码。
spring-mybatis.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:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" 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/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!--service扫描目录配置--> <!-- name-generator:解决类名相同时ioc容器有beanid相同的问题--> <context:component-scan base-package="db.mybatis" name-generator="extend.CustomBeanNameGenerator"> <context:include-filter type="aspectj" expression="db.mybatis.service..*"/> </context:component-scan> <context:property-placeholder location="jdbc.properties"/> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="user" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> <property name="jdbcUrl" value="${jdbc.url}"/> </bean> <bean class="org.mybatis.spring.SqlSessionFactoryBean" id="sqlSessionFactory"> <property name="dataSource" ref="dataSource"/> <property name="mapperLocations" value="classpath:db/mybatis/mappers/**/*.xml"/> </bean> <bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate"> <constructor-arg ref="sqlSessionFactory"/> </bean> <!-- dao扫描配置--> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="db.mybatis.dao"/> <property name="sqlSessionTemplateBeanName" value="sqlSessionTemplate"/> </bean> <!--事务管理--> <bean id="dbTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <constructor-arg name="dataSource" ref="dataSource"/> </bean> <aop:config proxy-target-class="true"> <aop:advisor advice-ref="transactionInterceptor" pointcut="execution(* db.mybatis.service..*Impl.*(..))"/> </aop:config> <tx:advice id="transactionInterceptor" transaction-manager="dbTransactionManager"> <tx:attributes> <tx:method name="add*" propagation="REQUIRED"/> <tx:method name="insert*" propagation="REQUIRED"/> <tx:method name="save*" propagation="REQUIRED"/> <tx:method name="update*" propagation="REQUIRED"/> <tx:method name="modify*" propagation="REQUIRED"/> <tx:method name="edit*" propagation="REQUIRED"/> <tx:method name="delete*" propagation="REQUIRED"/> <tx:method name="remove*" propagation="REQUIRED"/> <tx:method name="get*" read-only="true" propagation="SUPPORTS"/> <tx:method name="find*" read-only="true" propagation="SUPPORTS"/> <tx:method name="load*" read-only="true" propagation="SUPPORTS"/> <tx:method name="search*" read-only="true" propagation="SUPPORTS"/> <tx:method name="*" propagation="SUPPORTS"/> </tx:attributes> </tx:advice></beans>
项目目录结构如下
这一部分要完成的任务就是上面那些了,我们开始动手吧。
1、开发所需jar包
建议用maven或Gradle(eclipse暂不支持)管理。如果用maven管理,只要添加以下依赖。
<dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.4</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <!-- mysql驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.6</version> </dependency> <!-- spring --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.2.1.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>4.2.1.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>4.2.1.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-expression</artifactId> <version>4.2.1.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>4.2.1.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>4.2.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>4.2.1.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>4.2.1.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>4.2.1.RELEASE</version> </dependency> <!-- spring end --> <!-- c3p0 --> <dependency> <groupId>c3p0</groupId> <artifactId>c3p0</artifactId> <version>0.9.1.2</version> </dependency>
2、配置spring-mybatis.xml
配置完之后和最开始放上去是一样的,这里主要对一些配置做说明:
(1)<context:component-scan>
里的name-generator属性
默认情况下IOC容器是根据类名来命名的,不同模块相似的功能很多类名是一样的,这时候beanid不唯一,项目不能正常启动。
配置的类代码如下:
public class CustomBeanNameGenerator implements BeanNameGenerator { @Override public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) { return definition.getBeanClassName().replaceAll("\\.", "").replace("Impl",""); }}
(2)<context:include-filter>
其type类型有五种,建议使用aspejct和regex。这样你不需要在每个类加类似@Service这样的注解;在不同的上下文中扫描目录明确。
(3) <tx:method>
推荐一篇文章,请点击<tx:method>
标签。
3、开始开发
dao层的xml和接口
<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" ><mapper namespace="db.mybatis.dao.AccountDao"> <resultMap id="BaseResultMap" type="db.mybatis.model.Account"> <id column="id" property="id"/> <result column="user" property="user"/> <result column="money" property="money"/> </resultMap> <select id="getRecords" resultMap="BaseResultMap"> SELECT id,user,money FROM account </select> <select id="getMoneyByUser" resultType="double"> SELECT money FROM account where user = #{user} </select> <update id="updateMoneyByUser"> update account set money=${money} where user = #{user} </update></mapper>
public interface AccountDao { List<Account> getRecords(); double getMoneyByUser(String user); int updateMoneyByUser(@Param("money")double money,@Param("user")String user);}
service的接口和实现类
public interface AccountService { /** * 查询所有的人员记录 * @return */ List<Account> getRecords(); /** * 转账 * @param user1 转钱的人 * @param user2 收钱的人 * @param money 转出去的钱 * @return */ int transAccount(String user1,String user2,double money);}
public class AccountServiceImpl implements AccountService { @Resource private AccountDao accountDao; @Override public List<Account> getRecords() { return accountDao.getRecords(); } @Override public int transAccount(String user1, String user2, double money) { int result = 0; double money1 = accountDao.getMoneyByUser(user1); if (money1 > money) { result += accountDao.updateMoneyByUser(money1 - money, user1); double money2 = accountDao.getMoneyByUser(user2); result += accountDao.updateMoneyByUser(money2 + money, user2); } else { System.out.println(user1 + "的余额不足"); } return result; }}
测试类
public class UnitTestBase { private ClassPathXmlApplicationContext context; private String springXmlPath; public UnitTestBase(String springXmlPath) { this.springXmlPath = springXmlPath; } @Before public void before() { if (StringUtils.isEmpty(springXmlPath)) { springXmlPath = "classpath:applicationContext.xml"; } try { context = new ClassPathXmlApplicationContext(springXmlPath.split("[,\\s]+]")); context.start(); } catch (BeansException e) { e.printStackTrace(); } } @After public void after() { if (context != null) context.destroy(); } protected <T extends Object> T getBean(String beanId) { try { return (T) context.getBean(beanId); } catch (BeansException e) { e.printStackTrace(); return null; } } protected <T extends Object> T getBean(Class<T> clazz) { try { return context.getBean(clazz); } catch (BeansException e) { e.printStackTrace(); return null; } }}
public class TestDB extends UnitTestBase { public TestDB() { super("classpath*:spring-mybatis.xml"); } @Test public void testDB() throws Exception { AccountService accountService = getBean("dbmybatisserviceimplAccountService"); System.out.println("转账前:"); List<Account> records = accountService.getRecords(); printCollection(records); int transCount = accountService.transAccount("张三", "李四", 200); System.out.println("转账后:"); records = accountService.getRecords(); printCollection(records); System.out.println("更新记录总数:" + transCount); } private void printCollection(Collection<?> collection) { Iterator<?> iterator = collection.iterator(); while (iterator.hasNext()) { System.out.println(iterator.next().toString()); } }}
第一次执行结果如下:
现在将AccountServiceImpl中transAccount方法改为
@Override public int transAccount(String user1, String user2, double money) { int result = 0; double money1 = accountDao.getMoneyByUser(user1); if (money1 > money) { result += accountDao.updateMoneyByUser(money1 - money, user1); result += 1 / 0; double money2 = accountDao.getMoneyByUser(user2); result += accountDao.updateMoneyByUser(money2 + money, user2); } else { System.out.println(user1 + "的余额不足"); } return result; }
再把测试方法改为
AccountService accountService = getBean(AccountService.class); System.out.println("转账前:"); List<Account> records = accountService.getRecords(); printCollection(records); int transCount = 0; try { transCount = accountService.transAccount("张三", "李四", 200); } catch (Exception e) { System.out.println("转账失败:"+e.getClass().getName()+" "+e.getCause()); } System.out.println("转账后:"); records = accountService.getRecords(); printCollection(records); System.out.println("更新记录总数:" + transCount);
再测试下我们的程序,结果如下:
这时候张三转出去了200,但李四没收到200
我们在spring-mybatis.xml配置文件中<tx:attributes>
加上<tx:method name="transAccount" propagation="REQUIRED"/>
再测试下我们的程序,结果如下:
AOP通过配置XML在事务管理的应用到此结束。
4、事务管理注解实现
在spring-mybatis.xml文件中添加标签<tx:annotation-driven transaction-manager="dbTransactionManager"/>
开启注解事务,只需在需要加事务的类或方法上加注解@Transactional即可。
说明:
注解跟AOP配置文件实现可共存,也可只使用一个;
建议只使用一个,而且使用注解,因为在开发过程中aspectj配置很容易无效,而注解的代理模式(默认模式)不会出现这个问题。关于这个问题请阅读,我测试只用一个文件还是遇到了这样的问题。
- AOP应用——事务管理
- Spring事务管理——AOP注解事务管理
- AOP应用之------事务管理
- Spring事务管理—AOP/Annotation
- AOP第二部分应用-事务管理
- Spring事务管理—aop:pointcut expression解析
- Spring事务管理—aop:pointcut expression解析
- Spring事务管理—aop:pointcut expression解析
- Spring事务管理—aop:pointcut expression解析
- Spring事务管理—aop:pointcut expression解析
- Spring事务管理—aop:pointcut expression解析
- Spring事务管理—aop:pointcut expression解析
- Spring事务管理—aop:pointcut expression解析
- Spring事务管理—aop:pointcut expression解析
- Spring事务管理—aop:pointcut expression解析
- Spring事务管理—aop:pointcut expression解析
- Spring事务管理—aop:pointcut expression解析
- Spring事务管理—aop:pointcut expression解析
- Eclipse一直Building Workspace
- java中的构造函数
- Error:Execution failed for task ':app:clean'. > Unable to delete directory:......
- tomcat热部署配置
- 事务
- AOP应用——事务管理
- div 移动
- 已知需要找给顾客的零钱金额为N,当前钱币的面值种类为1,9,10三种,求找给顾客尽量少的钱币数的找零方法,给出程序算法设计思路
- SQL语句中SUM函数返回NULL的解决办法
- [31] Vijos P1495 笨小猴(模拟)
- adb已配置完成,却无法调试手机
- TFS的原理及应用
- JS中undefined、null以及NaN之间的区别,以及对象属性赋值的面试题,undefinednan
- 浅析淘宝首页菜单加载