Spring > 基于Annotation(注解)的声明式事务控制

1 > POM文件项目依赖导入和bean.xml的编写

(你要是问我为啥用单词Annotation,我只能告诉你是为了好看,上一篇基于xml的声明式事务控制)

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>spring_dome</artifactId>
        <groupId>com.parent</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>spring_transcation_base_is_annotation</artifactId>

    <dependencies>
        <!-- 导入spring IOC依赖 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>

        <!-- spring aop -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>

        <!-- spring aop -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</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.26</version>
        </dependency>

        <!-- aop -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.7</version>
        </dependency>

        <!-- 单元测试 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
    </dependencies>
</project>

bean.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/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:component-scan base-package="com.xx03"/>

    <!-- 创建事务管理器 -->
    <bean id="transactionManger" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <constructor-arg ref="dataSource"/>
    </bean>
    <!-- 设置声明式事务注解配置的开启,这里需要用到事务管理控制器 -->
    <tx:annotation-driven transaction-manager="transactionManger"/>

    <!-- Dao层实现类 -->
    <bean id="accountDao" class="com.xx03.dao.AccountDaoImpl"/>

    <!-- 创建数据源 -->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://159.75.19.211:3306/dome02"/>
        <property name="username" value="root"/>
        <property name="password" value="111222333"/>
    </bean>

    <!-- 创建JdbcTemplate -->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <constructor-arg ref="dataSource"/>
    </bean>
</beans>

2 > 需要用到的类

在这里插入图片描述

2.1 > Dao层接口及其实现类

2.1.1 > AccountDao接口

package com.xx03.dao;

import com.xx03.domain.Account;

import java.util.List;

public interface AccountDao {
    
    
    // find all account
    List<Account> findAllAccount();

    // find account by Id
    Account findAccountById(Integer Id);

    // update Account
    Integer updateAccount(Account account);

    // transfer source to target
    Integer updateSourceToTarget(Account source , Account target , Float transferMoney);
}

2.1.2 > AccountDaoImpl实现类

package com.xx03.dao;

import com.xx03.domain.Account;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository("accountDao")
public class AccountDaoImpl implements AccountDao {
    
    

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Override
    public List<Account> findAllAccount() {
    
    
        return jdbcTemplate.query("select * from account", new BeanPropertyRowMapper<Account>(Account.class));
    }

    @Override
    public Account findAccountById(Integer Id) {
    
    
        return jdbcTemplate.queryForObject("select * from account where id = ?", new BeanPropertyRowMapper<Account>(Account.class), Id);
    }

    @Override
    public Integer updateAccount(Account account) {
    
    
        /*
         * 一次判断account对象中那些属性存在 哪些属性不存在
         * */
        StringBuilder sb = new StringBuilder("update account set ");
        // 判断 account 对象是否为空
        if (account == null || account.getId() == null) {
    
    
            // 对象为空 抛出异常 触发事务,进行回滚操作
            System.out.println("Id is null ~");
            throw new RuntimeException("Execute Fail ~~ : by ID Field is null ~~");
        } else {
    
    
            // account不为空
            if (account.getName() == null) {
    
    
                // name is null ---- name值为空,输出提示
                System.out.println("Name is null ~");

                // 并且判断money是否为空
                if (account.getMoney() != null) {
    
    
                    // money value not null  ----  不为空,进行sql语句的拼接
                    System.out.println("Money is : " + account.getMoney());
                    sb.append("money = " + account.getMoney() + " ");
                } else {
    
    
                    // money value is null --- 当name,金额都为空时,抛出异常,触发回滚操作
                    System.out.println("Money is null ~");
                    throw new RuntimeException("Need's Values is null ~ \ncannot update ~ \nBy id is : " + account.getId());
                }
            } else {
    
    
                // name is not null 名字不为空 ,拼接sql
                System.out.println("Name is : " + account.getName());
                sb.append("name = '" + account.getName() + "' ");
                if (account.getMoney() != null) {
    
    
                    // money value not null 金额不为空,拼接字符串
                    System.out.println("Money is : " + account.getMoney());
                    sb.append(", money = " + account.getMoney() + " ");
                } else {
    
    
                    // money value is null
                    System.out.println("Money is null ~");
                }

            }
            // 拼接为哪个id更新
            sb.append("where id = " + account.getId());
            String sql_update = sb.toString();
            System.out.println("SQL is : " + sql_update);
            int i = 0;
            i = jdbcTemplate.update(sql_update); // 返回更新的行数
            return i;
        }
    }

    @Override
    public Integer updateSourceToTarget(Account source, Account target, Float transferMoney) {
    
    
        // update line
        Integer update_line_number = 0;

        // 判断id是否为空
        if (source.getId() != null && target.getId() != null) {
    
    
            // 判断数据库中是否有这两个用户
            source = findAccountById(source.getId());
            target = findAccountById(target.getId());
            if (source!=null && target!=null){
    
    
                source.setMoney(source.getMoney() - transferMoney);
                target.setMoney(target.getMoney() + transferMoney);
                // 更新账户信息
                update_line_number += updateAccount(source);
                update_line_number += updateAccount(target);
            }else {
    
    
                // 获取到的数据有一方为空
                throw new RuntimeException("请检查转账所需的id是否正确 ~~");
            }
        }else {
    
    
            // id值为空
            throw new RuntimeException("请设置转账所需的id ~~");
        }
        return update_line_number;
    }
}

2.2 > Service层接口及其实现类

2.2.1 > AccountService接口

package com.xx03.service;

import com.xx03.domain.Account;

import java.util.List;

public interface AccountService {
    
    
    // find all account
    List<Account> findAllAccount();

    // find account by Id
    Account findAccountById(Integer Id);

    // update Account
    Integer updateAccount(Account account);

    // transfer source to target
    Integer updateSourceToTarget(Account source , Account target , Float transferMoney);
}

2.2.2 > AccountServiceImpl 实现类

package com.xx03.service.impl;

import com.xx03.dao.AccountDao;
import com.xx03.domain.Account;
import com.xx03.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Service("accountService")
@Transactional
public class AccountServiceImpl implements AccountService {
    
    

    @Autowired
    private AccountDao accountDao;

    @Override
    public List<Account> findAllAccount() {
    
    
        return accountDao.findAllAccount();
    }

    @Override
    public Account findAccountById(Integer Id) {
    
    
        return null;
    }

    @Override
    public Integer updateAccount(Account account) {
    
    
        return null;
    }

    @Override
    public Integer updateSourceToTarget(Account source, Account target, Float transferMoney) {
    
    
        Integer integer = 0;

        integer = accountDao.updateSourceToTarget(source, target, transferMoney);
        int i = 1 / 0;
        System.out.println(accountDao.findAccountById(source.getId()));
        System.out.println(accountDao.findAccountById(target.getId()));

        if (integer == 2) return integer;
        else return null;
    }
}

Dao层写的实际上写的有点问题,正常逻辑应该在Service层进行逻辑上的判断,但是,写Dome的时候,压根就没考虑有Service。。 不好意思。知道啥意思就成了

3 > 测试(首先测试无异常情况)

3.1 > 数据库信息

在这里插入图片描述

3.2 > 测试类信息

// source 转给 target 200f

package com.xx03.test;

import com.xx03.domain.Account;
import com.xx03.service.AccountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:bean.xml")
public class Test_01 {
    
    

    @Autowired
    private AccountService accountService;

    @Test
    public void method_01(){
    
    
        accountService.findAllAccount().forEach(System.out::println);
    }

    @Test
    public void method_02(){
    
    
        Account source = new Account();
        source.setId(1); 
        Account target = new Account();
        target.setId(2);
        Integer integer = accountService.updateSourceToTarget(source, target, 200f);
    }
}

3.3 > 测试结果(本次用到的为method_02,正常执行)

在这里插入图片描述

3.4 > 测试结果(本次用到的为method_02,异常执行)

异常位置
在这里插入图片描述
提示信息提示已经转账成功,但在转账成功后,发生了数学异常,Spring事务捕获到异常信息,并进行回滚
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_43309893/article/details/119777899