基于注解的方式实现事务的声明式声明

一.使用spring的注解来声明事务

1.1 说明

此案例要和上一篇文章,基于xml实现事务的配置,要做对比,实现的功能是一样的,但是xml方式和基于注解方式的写法配置都是有区别的。

1.2 主要流程说明

spring中基于注解的声明式事务控制的控制步骤:

1配置事务管理器

2.开启spring的注解事务的支持

3.在需要事务支持的地方使用@Transaction注解

二 项目案例

2.1 工程结构

2.2 配置pom文件

 <!-- log4j的日志-->
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
    <!-- spring-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.0.2.RELEASE</version>
    </dependency>
    <!-- spring-jdbc -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>5.0.2.RELEASE</version>
    </dependency>
    <!-- spring的事务管理-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-tx</artifactId>
      <version>5.0.2.RELEASE</version>
    </dependency>
    <!-- mysql的驱动包-->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.6</version>
    </dependency>
    <!-- spring 切面包-->
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>1.8.7</version>
    </dependency>

2.3 dao层

1.接口

package com.ljf.spring.tx.anno.dao;

import com.ljf.spring.tx.anno.domain.Account;

public interface AccountDao {
    /**
     * 根据名称查询账户
     * @param accountName
     * @return
     */
    Account findAccountByName(String accountName);

    /**
     * 更新账户
     * @param account
     */
    void updateAccount(Account account);
}

2.实现层

注解的方式可以和上篇文章xml的方式进行对别

package com.ljf.spring.tx.anno.dao.impl;

import com.ljf.spring.tx.anno.dao.AccountDao;
import com.ljf.spring.tx.anno.domain.Account;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import org.springframework.stereotype.Repository;

import java.util.List;

/**
 * @ClassName: AccountDaoImpl
 * @Description: TODO
 * @Author: liujianfu
 * @Date: 2021/02/22 18:02:28 
 * @Version: V1.0
 **/
@Repository("accountDao")
public class AccountDaoImpl  implements AccountDao {
    @Autowired
    private JdbcTemplate jdbcTemplate;
    @Override
    public Account findAccountByName(String accountName) {
        List<Account> accounts = jdbcTemplate.query("select id id ,account_name accountName,money money from tb_account where account_name = ?",new BeanPropertyRowMapper<Account>(Account.class),accountName);
        if(accounts.isEmpty()){
            return null;
        }
        if(accounts.size()>1){
            throw new RuntimeException("结果集不唯一");
        }
        return accounts.get(0);
    }

    @Override
    public void updateAccount(Account account) {
       jdbcTemplate.update("update tb_account set account_name=?,money=? where id=?",account.getAccountName(),account.getMoney(),account.getId());
    }
}

2.4 service层

1.接口

package com.ljf.spring.tx.anno.service;

public interface AccountService {
    /**
     * 转账
     * @param sourceName    转成账户名称
     * @param targetName    转入账户名称
     * @param money         转账金额
     */
    public   void transferMoney(String sourceName, String targetName, Double money);
}

2.实现层

package com.ljf.spring.tx.anno.service.impl;

import com.ljf.spring.tx.anno.dao.AccountDao;
import com.ljf.spring.tx.anno.domain.Account;
import com.ljf.spring.tx.anno.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

/**
 * @ClassName: AccountServiceImpl
 * @Description: TODO
 * @Author: liujianfu
 * @Date: 2021/02/22 17:52:56 
 * @Version: V1.0
 **/
@Service("accountService")
@Transactional(propagation= Propagation.SUPPORTS,readOnly=true)//只读型事务的配置
public class AccountServiceImpl implements AccountService {
    //通过注解
    @Autowired
    private AccountDao actDao;

    /**   北京,上海的钱均为500 ,现在实现北京向上海进行转账50,正确的结果为北京450,上海550,
     * 但是现在没有进行事务管理,在北京向上海进行转账,实现北京进行了减款操作,但是在上海进行加钱操作之前,出现了报错,后面代码无法执行,
     * 造成北京450,上海还是500
     * 转账
     * @param sourceName    转成账户名称
     * @param targetName    转入账户名称
     * @param money         转账金额
     *
     *  1.@Transactional 等价于
     *         <!-- 配置aop-->
     *     <aop:config>
     *         <!-- 配置切入点表达式-->
     *         <aop:pointcut id="pt1" expression="execution(* com.ljf.spring.tx.xml.service.impl.*.*(..))"></aop:pointcut>
     *         <!--建立切入点表达式和事务通知的对应关系 -->
     *         <aop:advisor advice-ref="txAdvice" pointcut-ref="pt1"></aop:advisor>
     *
     *     </aop:config>
     *  2.@Transactional(propagation= Propagation.REQUIRED,readOnly=false) 等价于
     *  <!-- 配置事务的通知-->
     *     <tx:advice id="txAdvice" transaction-manager="transactionManager">
     *         <tx:attributes>
     *             <tx:method name="*" propagation="REQUIRED" read-only="false" rollback-for="*"/>
     *             <tx:method name="find*" propagation="SUPPORTS" read-only="true"></tx:method>
     *         </tx:attributes>
     *     </tx:advice>
     *
     *     <!-- 配置aop-->
     *     <aop:config>
     *         <!-- 配置切入点表达式-->
     *         <aop:pointcut id="pt1" expression="execution(* com.ljf.spring.tx.xml.service.impl.*.*(..))"></aop:pointcut>
     *         <!--建立切入点表达式和事务通知的对应关系 -->
     *         <aop:advisor advice-ref="txAdvice" pointcut-ref="pt1"></aop:advisor>
     */
    @Override
    //需要的是读写型事务配置
    @Transactional(propagation= Propagation.REQUIRED,readOnly=false)
    public void transferMoney(String sourceName, String targetName, Double money) {
        System.out.println("正在实现转账功能 transfer....");
        //2.1根据名称查询转出账户
        Account source = actDao.findAccountByName(sourceName);
        //2.2根据名称查询转入账户
        Account target = actDao.findAccountByName(targetName);
        //2.3转出账户减钱
        System.out.println(":"+(source.getMoney()-money));
        source.setMoney(source.getMoney()-money);
        //2.4转入账户加钱
        target.setMoney(target.getMoney()+money);
        //2.5更新转出账户
        actDao.updateAccount(source);

       try{
           int i=1/0;
       }
       catch (Exception e){
           e.printStackTrace();

           System.out.println("进行了事务的回滚");
           throw new RuntimeException();
       }
        //2.6更新转入账户
        actDao.updateAccount(target);
    }
}

2.5 resouce的配置文件

2.5.1 jdbc的配置文件

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/nongda
jdbc.user=root
jdbc.password=

2.5.2 spring的配置文件

<?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:tx="http://www.springframework.org/schema/tx"
           xmlns:context="http://www.springframework.org/schema/context"
           xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.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
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">
     <context:property-placeholder location="classpath:spring-jdbc.properties"></context:property-placeholder>
    <!-- 1.配置数据源-->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="${jdbc.driver}"></property>
        <property name="url" value="${jdbc.url}"></property>
        <property name="username" value="${jdbc.user}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>
    <!-- 2.配置spring创建容器时要扫描的包-->
    <context:component-scan base-package="com.ljf.spring.tx"></context:component-scan>

    <!-- 3.配置JdbcTemplate-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <!-- spring中基于注解 的声明式事务控制配置步骤
         1、配置事务管理器
         2、开启spring对注解事务的支持
         3、在需要事务支持的地方使用@Transactional注解
      -->
    <!-- 4.配置事务管理器 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <!-- 5.开启spring对注解事务的支持-->
    <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
</beans>

2.6 调用

package com.ljf.spring.tx.anno;

import com.ljf.spring.tx.anno.service.AccountService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * Hello world!
 *
 */
public class App 
{
    public static void main( String[] args )
    {
        System.out.println( "Hello World!" );
        //1.获取核心容器对象
        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
        //2.根据id获取Bean对象
        AccountService as  = ac.getBean(AccountService.class);
        as.transferMoney("北京","上海",50.0);
        System.out.println(as);
    }
}

多次执行,北京、上海的钱数不变,回滚进行了操作,起到了作用!

猜你喜欢

转载自blog.csdn.net/u011066470/article/details/113974454
今日推荐