Spring框架(9) —— 三种事务管理机制的转账案例

#简介

  • 的三个案例是为了更好地理解Spring AOP的机制,以及复习数据库事务管理。具体的功能是实现数据库的转账操作,核心知识主要分为三部分:数据库的连接,事务管理,依赖注入和- ** <ü>案例一:直接在业务层添加代码,来进行事务管理</ù> ** - 数据库的管理问题:通过连接工具类来实现 - ** 事务管理</ u>:通过事务工具类,在服务业务层中实现** - 依赖注入:通过xml配置文件 - 测试:通过Junit测试 -** 案例二:通过代理模式增强代码,来进行事务管理</ u

** - 数据库的管理问题:通过连接工具类来实现 - ** 事务管理</ u>:通过事务工具类,在BeanFactory代理类中实现。(获取增强过的业务层接口)** - 依赖注入:通过xml配置文件 - 测试:通过Junit测试 - ** 案例三:通过SpringAOP,来进行事务管理</ u> ** - 数据库的管理问题:通过连接工具类来实现 - ** 事务管理</ u

:完全通过交易工具类实现(AOP,高内聚)** - 依赖注入:通过xml配置文件+注解 - 测试:通过Spring Junit测试

项目环境

数据库代码

  • 创建数据库spring,在数据库中创建 Account表。
# 创建数据库
CREATE DATABASE spring;

# 使用数据库
USE spring;

# 创建表
CREATE TABLE account(
	id INT PRIMARY KEY AUTO_INCREMENT,
	NAME VARCHAR(40),
	money FLOAT
)CHARACTER SET utf8 COLLATE utf8_general_ci;

# 插入数据
INSERT INTO account(NAME,money) VALUES('Cat',1000);
INSERT INTO account(NAME,money) VALUES('Dog',1000);
INSERT INTO account(NAME,money) VALUES('Rat',1000);

Maven依赖

<dependencies>
        <!-- Spring 框架 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <!-- Spring AOP -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.7</version>
        </dependency>
        <!-- MySQL数据库驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.6</version>
        </dependency>
        <!-- 数据库连接池 -->
        <dependency>
            <groupId>c3p0</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.1.2</version>
        </dependency>
        <!-- 操作数据库的工具包 -->
        <dependency>
            <groupId>commons-dbutils</groupId>
            <artifactId>commons-dbutils</artifactId>
            <version>1.4</version>
        </dependency>
        <!-- Spring 测试 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <!-- 单元测试 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
    </dependencies>

业务层实现事务

目录结构

  • src
    • main

      • java

        • cn.water

          • momain:用于存放实体类。

            • Account.java(实体类)
          • dao:用于存放持久层的接口和实现类。(具体的CRUD操作代码)

            • AccountDao.java(持久层接口)
            • AccountDaoImp.java(持久层实现类)
          • service:用于存放业务层的接口和实现类。

            • AccountService.java(业务层接口)
            • AccountServiceImp.java(业务层接口)
          • utils:用于存放工具类

            • ConnectionUtils.java(数据库连接工具类)
            • TransactionManager.java(事务管理工具类)
      • resources:用于存放配置文件

      • Beans.xml(Spring配置文件)

    • test:用于测试。

      • cn.water.test
        • SpringTest.java(测试类)

实体类

  • 封装数据库查询操作的结果集

Account.java

package cn.water.domain;


public class Account {

    /* 成员变量 */
    private Integer id;
    private String name;
    private Float money;

    /* 构造函数 */
    public Account() {
    }

    public Account(Integer id, String name, Float money) {
        this.id = id;
        this.name = name;
        this.money = money;
    }

    /* 设值函数 */
    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Float getMoney() {
        return money;
    }

    public void setMoney(Float money) {
        this.money = money;
    }

    /* toString */
    @Override
    public String toString() {
        return "Account{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", money=" + money +
                '}';
    }
}

持久层

  • 实现具体的数据库操作代码

AccountDao.java

package cn.water.dao;

import cn.water.domain.Account;

import java.util.List;


public interface AccountDao {

    /** 查询单个 */
    Account findByID(int id);
    /** 修改 */
    void update(Account account);


}

AccountDaoImp.java

package cn.water.dao;


import cn.water.domain.Account;
import cn.water.utils.ConnectionUtils;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;


public class AccountDaoImp implements AccountDao {

    /* 成员方法 */
    private QueryRunner queryRunner;
    private ConnectionUtils connectionUtils;

    /* 设值方法 */
    public void setQueryRunner(QueryRunner queryRunner) {
        this.queryRunner = queryRunner;
    }

    public void setConnectionUtils(ConnectionUtils connectionUtils) {
        this.connectionUtils = connectionUtils;
    }


    /** 查询单个 */
    public Account findByID(int id) {
        try {
            /* QueryRunner.query */
            return queryRunner.query(
                    /* Connection对象 */
                    connectionUtils.getThreadConnection(),
                    /* SQL语句 */
                    "SELECT * FROM account WHERE id = ? ",
                    /* 结果集 */
                    new BeanHandler<Account>(Account.class),
                    id);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }


    /** 修改 */
    public void update(Account account) {
        try {
            /* QueryRunner.update */
            queryRunner.update(
                    /* Connection对象 */
                    connectionUtils.getThreadConnection(),
                    /* SQL语句 */
                    " UPDATE account SET name=?,money=? WHERE id=? ",
                    /* 参数 */
                    account.getName(),
                    account.getMoney(),
                    account.getId());
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }


}

数据库连接工具类

  • 获取当前线程上的Connection对象

ConnectionUtils.java

package cn.water.utils;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;

public class ConnectionUtils {

    /* 成员变量 */
    private ThreadLocal<Connection> threadLocal = new ThreadLocal<Connection>();
    private DataSource dataSource;

    /* 设值函数 */
    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    /** 获取Connection对象 */
    public Connection getThreadConnection(){
        try {
            /* 获取从当前线程上的Connection对象 */
            Connection connection = threadLocal.get();
            /* 如果当前线程没有绑定Connection */
            if (connection==null) {
                /* 从数据库连接池中,获取一个Connection对象 */
                connection = dataSource.getConnection();
                /* 并绑定至当前线程 */
                threadLocal.set(connection);
            }
            /* 返回此Connection对象 */
            return connection;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /** 将Connection和当前线程 解绑 */
    public void removeConnection(){
        threadLocal.remove();
    }

}

事务管理工具类

  • 对当前线程上的 Connection对象 进行事务管理的具体方法

TransactionManager.java

package cn.water.utils;

import java.sql.Connection;
import java.sql.SQLException;

public class TransactionManager {

    /* 成员变量 */
    private ConnectionUtils connectionUtils;
    private Connection connection;

    /* 设值函数 */
    public void setConnectionUtils(ConnectionUtils connectionUtils) {
        this.connectionUtils = connectionUtils;
    }

    /** 开启事务 */
    public void beginTransaction(){
        try {
            connectionUtils.getThreadConnection().setAutoCommit(false);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    /** 提交事务 */
    public void commit(){
        try {
            connectionUtils.getThreadConnection().commit();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    /** 回滚事务 */
    public void rollback(){
        try {
            connectionUtils.getThreadConnection().rollback();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    /** 释放连接 */
    public void release(){
        try {
            /* 返回至连接池 */
            connectionUtils.getThreadConnection().close();
            /* 将Connection对象与线程解绑 */
            connectionUtils.removeConnection();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }


}

业务层(TM)

  • 直接调用事务管理工具类的方法,实现事务管理

AccountService.java

package cn.water.service;


import cn.water.domain.Account;


public interface AccountService {

    /** 转账 */
    void transfer(int sourceId, int targetId, float money);

}

AccountServiceImp.java

package cn.water.service;

import cn.water.dao.AccountDao;
import cn.water.domain.Account;
import cn.water.utils.TransactionManager;

import java.util.List;


public class AccountServiceImp implements AccountService {

    /* 成员变量 */
    private AccountDao dao;
    private TransactionManager txManager;

    /* 设值方法 */
    public void setDao(AccountDao dao) {
        this.dao = dao;
    }
    public void setTxManager(TransactionManager txManager) {
        this.txManager = txManager;
    }

    /** 转账 */
    public void transfer(int sourceId, int targetId, float money) {
        try {
            /* 开启事务 */
            txManager.beginTransaction();
            /* 执行操作 */
            Account source = dao.findByID(sourceId);
            Account target = dao.findByID(targetId);
            Float sourceMoney = source.getMoney();
            Float targetMoney = target.getMoney();
            source.setMoney(sourceMoney - money);
//            int i = 10/0;
            target.setMoney(targetMoney + money);
            dao.update(source);
            dao.update(target);
            /* 提交事务 */
            txManager.commit();
        } catch (Exception e) {
            /* 回滚 */
            txManager.rollback();
            /* 抛出异常 */
            throw new RuntimeException();
        } finally {
            /* 释放连接 */
            txManager.release();
        }

    }


}

配置文件

  • ServiceImp(业务层)
    • DaoImp
  • DaoImp(持久层)
    • QueryRunner
  • QueryRunner(数据库操作类)
    • DataSource
  • TransactionManager(事务管理工具类)
    • ConnectionUtils
  • ConnectionUtils(数据库连接工具类)
    • DataSource
  • DataSource(数据库连接池)

Beans.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    
    <!-- DataSource -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="com.mysql.jdbc.Driver"/>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/spring"/>
        <property name="user" value="root"/>
        <property name="password" value="root"/>
    </bean>

    <!-- ConnectionUtils -->
    <bean id="connectionUtils" class="cn.water.utils.ConnectionUtils">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!-- TransactionManager -->
    <bean id="txManager" class="cn.water.utils.TransactionManager">
        <property name="connectionUtils"  ref="connectionUtils"/>
    </bean>

    <!-- QueryRunner -->
    <bean id="runner" class="org.apache.commons.dbutils.QueryRunner">
        <constructor-arg name="ds" ref="dataSource"/>
    </bean>

    <!-- Dao -->
    <bean id="daoImp" class="cn.water.dao.AccountDaoImp">
        <property name="connectionUtils" ref="connectionUtils"/>
        <property name="queryRunner" ref="runner"/>
    </bean>

    <!-- Service -->
    <bean id="serviceImp" class="cn.water.service.AccountServiceImp">
        <property name="dao" ref="daoImp"/>
        <property name="txManager" ref="txManager"/>
    </bean>


</beans>

测试类

SpringTest.java

package cn.water.test;

import cn.water.service.AccountService;
import cn.water.service.AccountServiceImp;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;


public class SpringTest {


    @Test
    public void testTransfer(){
        ApplicationContext app = new ClassPathXmlApplicationContext("Beans.xml");
        AccountService service = app.getBean("serviceImp", AccountServiceImp.class);
        service.transfer(1,2,10f);
    }



}

代理类实现事务

目录结构

  • src

    • main

      • java

        • cn.water

          • momain:用于存放实体类。

            • Account.java(实体类)
          • dao:用于存放持久层的接口和实现类。(具体的CRUD操作代码)

            • AccountDao.java(持久层接口)
            • AccountDaoImp.java(持久层实现类)
          • service:用于存放业务层的接口和实现类。

            • AccountService.java(业务层接口)
            • AccountServiceImp.java(业务层接口)
          • utils:用于存放工具类

            • ConnectionUtils.java(数据库连接工具类)
            • TransactionManager.java(事务管理工具类)
          • factory:用于存放工厂类

            • BeanFactory.java(代理工厂类)
      • resources:用于存放配置文件

  • Beans.xml(Spring配置文件)

  • test:用于测试。

    • cn.water.test
      • SpringTest.java(测试类)

实体类(不变)

  • 封装数据库查询操作的结果集

Account.java

package cn.water.domain;


public class Account {

    /* 成员变量 */
    private Integer id;
    private String name;
    private Float money;

    /* 构造函数 */
    public Account() {
    }

    public Account(Integer id, String name, Float money) {
        this.id = id;
        this.name = name;
        this.money = money;
    }

    /* 设值函数 */
    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Float getMoney() {
        return money;
    }

    public void setMoney(Float money) {
        this.money = money;
    }

    /* toString */
    @Override
    public String toString() {
        return "Account{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", money=" + money +
                '}';
    }
}

持久层

  • 实现具体的数据库操作代码
  • 引入 ConnectionUtils ,获取 Connection对象,在调用QueryRunner类的方法时传入。
    • 我们不难看出,QueryRunner类和Connection对象的关系:
      • 一、向QueryRunner类传入DataSource对象;
      • 二、调用QueryRunner类的方法时传入Connection对象。

AccountDao.java

package cn.water.dao;

import cn.water.domain.Account;

import java.util.List;


public interface AccountDao {

    /** 查询单个 */
    Account findByID(int id);
    /** 修改 */
    void update(Account account);


}

AccountDaoImp.java

package cn.water.dao;


import cn.water.domain.Account;
import cn.water.utils.ConnectionUtils;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;


public class AccountDaoImp implements AccountDao {

    /* 成员方法 */
    private QueryRunner queryRunner;
    private ConnectionUtils connectionUtils;

    /* 设值方法 */
    public void setQueryRunner(QueryRunner queryRunner) {
        this.queryRunner = queryRunner;
    }

    public void setConnectionUtils(ConnectionUtils connectionUtils) {
        this.connectionUtils = connectionUtils;
    }


    /** 查询单个 */
    public Account findByID(int id) {
        try {
            /* QueryRunner.query */
            return queryRunner.query(
                    /* Connection对象 */
                    connectionUtils.getThreadConnection(),
                    /* SQL语句 */
                    "SELECT * FROM account WHERE id = ? ",
                    /* 结果集 */
                    new BeanHandler<Account>(Account.class),
                    id);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }


    /** 修改 */
    public void update(Account account) {
        try {
            /* QueryRunner.update */
            queryRunner.update(
                    /* Connection对象 */
                    connectionUtils.getThreadConnection(),
                    /* SQL语句 */
                    " UPDATE account SET name=?,money=? WHERE id=? ",
                    /* 参数 */
                    account.getName(),
                    account.getMoney(),
                    account.getId());
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }



}

数据库连接工具类(不变)

  • 获取当前线程上的Connection对象(不变)

ConnectionUtils.java

package cn.water.utils;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;

public class ConnectionUtils {

    /* 成员变量 */
    private ThreadLocal<Connection> threadLocal = new ThreadLocal<Connection>();
    private DataSource dataSource;

    /* 设值函数 */
    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    /** 获取Connection对象 */
    public Connection getThreadConnection(){
        try {
            /* 获取从当前线程上的Connection对象 */
            Connection connection = threadLocal.get();
            /* 如果当前线程没有绑定Connection */
            if (connection==null) {
                /* 从数据库连接池中,获取一个Connection对象 */
                connection = dataSource.getConnection();
                /* 并绑定至当前线程 */
                threadLocal.set(connection);
            }
            /* 返回此Connection对象 */
            return connection;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /** 将Connection和当前线程 解绑 */
    public void removeConnection(){
        threadLocal.remove();
    }

}

事务管理工具类(不变)

  • 对当前线程上的 Connection对象 进行事务管理的具体方法

TransactionManager.java

package cn.water.utils;

import java.sql.Connection;
import java.sql.SQLException;

public class TransactionManager {

    /* 成员变量 */
    private ConnectionUtils connectionUtils;

    /* 设值函数 */
    public void setConnectionUtils(ConnectionUtils connectionUtils) {
        this.connectionUtils = connectionUtils;
    }

    /** 开启事务 */
    public void beginTransaction(){
        try {
            connectionUtils.getThreadConnection().setAutoCommit(false);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    /** 提交事务 */
    public void commit(){
        try {
            connectionUtils.getThreadConnection().commit();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    /** 回滚事务 */
    public void rollback(){
        try {
            connectionUtils.getThreadConnection().rollback();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    /** 释放连接 */
    public void release(){
        try {
            /* 返回至连接池 */
            connectionUtils.getThreadConnection().close();
            /* 将Connection对象与线程解绑 */
            connectionUtils.removeConnection();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }


}

业务层

  • 直接调用事务管理工具类的方法,实现事务管理

AccountService.java

package cn.water.service;


import cn.water.domain.Account;


public interface AccountService {

    /** 转账 */
    void transfer(int sourceId, int targetId, float money);

}

AccountServiceImp.java

package cn.water.service;

import cn.water.dao.AccountDao;
import cn.water.domain.Account;

import java.util.List;


public class AccountServiceImp implements AccountService {

    /* 成员变量 */
    private AccountDao dao;

    /* 设值方法 */
    public void setDao(AccountDao dao) {
        this.dao = dao;
    }


    /** 转账 */
    public void transfer(int sourceId, int targetId, float money) {
        Account source = dao.findByID(sourceId);
        Account target = dao.findByID(targetId);
        Float sourceMoney = source.getMoney();
        Float targetMoney = target.getMoney();
        source.setMoney(sourceMoney - money);
        dao.update(source);
//        int i = 10/0;
        target.setMoney(targetMoney + money);
        dao.update(target);
    }


}

代理工厂类(TM)

  • 代理模式:创建代理对象,并增强代码
  • 工厂模式:返回一个代理对象。

ProxyFactory.java

package cn.water.factory;

import cn.water.service.AccountService;
import cn.water.utils.TransactionManager;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;


public class BeanFactory {

    /* 成员变量 */
    private AccountService service;
    private TransactionManager txManager;

    /* 设值函数 */
    public void setService(AccountService service) {
        this.service = service;
    }

    public void setTxManager(TransactionManager txManager) {
        this.txManager = txManager;
    }

    /** 获取代理对象 */
    public AccountService getProxyService(){
        return (AccountService) Proxy.newProxyInstance(
                /* 类加载器 */
                service.getClass().getClassLoader(),
                /* 接口 */
                service.getClass().getInterfaces(),
                /** InvocationHandler接口:事务管理 */
                new InvocationHandler() {
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        /* 查询方法不需要进行事务管理 */
                        if ("find".equals(method.getName())){
                            return method.invoke(service,args);
                        /* 增删改方法进行进行事务管理 */
                        }else {
                            try {
                                /* 开启事务 */
                                txManager.beginTransaction();
                                /* 执行操作 */
                                Object result = method.invoke(service, args);
                                /* 提交事务 */
                                txManager.commit();
                                /* 返回结果集 */
                                return result;
                            } catch (Exception e) {
                                /* 回滚 */
                                txManager.rollback();
                                /* 抛出异常 */
                                throw new RuntimeException();
                            } finally {
                                /* 释放连接 */
                                txManager.release();
                            }
                        }
                    }
                }
        );
    }


}

配置文件

  • ProxyService(代理类)
  • BeanFactory(代理工厂类)
    • ServiceImp
    • TransactionManager
  • ServiceImp(业务层)
    • DaoImp
  • DaoImp(持久层)
    • QueryRunner
    • ConnectionUtils
  • QueryRunner(数据库操作类)
    • ConnectionUtils 能够为 DaoImp 提供 Connection对象,因此不必再为 QueryRunner 提供 DataSource
    • 但要转而为 DaoImp 提供 ConnectionUtils
  • TransactionManager(事务管理工具类)
    • ConnectionUtils
  • ConnectionUtils(数据库连接工具类)
    • DataSource
  • DataSource(数据库连接池)

Beans.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    
    <!-- DataSource -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="com.mysql.jdbc.Driver"/>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/spring"/>
        <property name="user" value="root"/>
        <property name="password" value="root"/>
    </bean>

    <!-- ConnectionUtils -->
    <bean id="connectionUtils" class="cn.water.utils.ConnectionUtils">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!-- TransactionManager -->
    <bean id="txManager" class="cn.water.utils.TransactionManager">
        <property name="connectionUtils"  ref="connectionUtils"/>
    </bean>

    <!-- QueryRunner -->
    <bean id="runner" class="org.apache.commons.dbutils.QueryRunner"></bean>

    <!-- Dao -->
    <bean id="daoImp" class="cn.water.dao.AccountDaoImp">
        <property name="connectionUtils" ref="connectionUtils"/>
        <property name="queryRunner" ref="runner"/>
    </bean>

    <!-- Service -->
    <bean id="serviceImp" class="cn.water.service.AccountServiceImp">
        <property name="dao" ref="daoImp"/>
    </bean>

    <!-- BeanFactory -->
    <bean id="factory" class="cn.water.factory.BeanFactory">
        <property name="service" ref="serviceImp"/>
        <property name="txManager" ref="txManager"/>
    </bean>

    <!-- 代理service -->
    <bean id="proxyService" factory-bean="factory" factory-method="getProxyService"></bean>



</beans>

测试类

  • Spring框架整合junit

SpringTest.java

package cn.water.test;

import cn.water.domain.Account;
import cn.water.service.AccountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import javax.annotation.Resource;


@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:Beans.xml")
public class SpringTest {

    /* 获取代理对象 */
    @Resource(name = "proxyService")
    private AccountService service;

    @Test
    public  void testTransfer(){
        service.transfer(1,2,10f);
    }


}

通知类实现事务(AOP)

目录结构

  • src
    • main

      • java
        • cn.water
          • dao:用于存放持久层的接口和实现类。(具体的CRUD操作代码)
            • AccountDao.java(持久层接口)
            • AccountDaoImp.java(持久层实现类)
          • momain:用于存放实体类。
            • Account.java(实体类)
          • service:用于存放业务层的接口和实现类。
            • AccountService.java(业务层接口)
            • AccountServiceImp.java(业务层接口)
          • utils:用于存放工具类
            • ConnectionUtils.java(数据库连接工具类)
            • TransactionManager.java(事务管理工具类)
      • resources:用于存放配置文件
        • Beans.xml(Spring配置文件)
    • test:用于测试。

      • cn.water.test
        • SpringTest.java(测试类)

实体类(不变)

  • 封装数据库查询操作的结果集

Account.java

package cn.water.domain;


public class Account {

    /* 成员变量 */
    private Integer id;
    private String name;
    private Float money;

    /* 构造函数 */
    public Account() {
    }

    public Account(Integer id, String name, Float money) {
        this.id = id;
        this.name = name;
        this.money = money;
    }

    /* 设值函数 */
    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Float getMoney() {
        return money;
    }

    public void setMoney(Float money) {
        this.money = money;
    }

    /* toString */
    @Override
    public String toString() {
        return "Account{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", money=" + money +
                '}';
    }
}

持久层

  • 实现具体的数据库操作代码

AccountDao.java

package cn.water.dao;

import cn.water.domain.Account;

import java.util.List;


public interface AccountDao {

    /** 查询单个 */
    Account findByID(int id);
    /** 修改 */
    void update(Account account);


}

AccountDaoImp.java

package cn.water.dao;


import cn.water.domain.Account;
import cn.water.utils.ConnectionUtils;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;


@Component("daoImp")
public class AccountDaoImp implements AccountDao {

    /* 成员方法 */
    @Resource(name = "runner")
    private QueryRunner queryRunner;

    @Resource(name = "connectionUtils")
    private ConnectionUtils connectionUtils;



    /** 查询单个 */
    public Account findByID(int id) {
        try {
            /* QueryRunner.query */
            return queryRunner.query(
                    /* Connection对象 */
                    connectionUtils.getThreadConnection(),
                    /* SQL语句 */
                    "SELECT * FROM account WHERE id = ? ",
                    /* 结果集 */
                    new BeanHandler<Account>(Account.class),
                    id);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }


    /** 修改 */
    public void update(Account account) {
        try {
            /* QueryRunner.update */
            queryRunner.update(
                    /* Connection对象 */
                    connectionUtils.getThreadConnection(),
                    /* SQL语句 */
                    " UPDATE account SET name=?,money=? WHERE id=? ",
                    /* 参数 */
                    account.getName(),
                    account.getMoney(),
                    account.getId());
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }



}

数据库连接工具类(不变)

  • 获取当前线程上的Connection对象

ConnectionUtils.java

package cn.water.utils;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;

public class ConnectionUtils {

    /* 成员变量 */
    private ThreadLocal<Connection> threadLocal = new ThreadLocal<Connection>();
    private DataSource dataSource;

    /* 设值函数 */
    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    /** 获取Connection对象 */
    public Connection getThreadConnection(){
        try {
            /* 获取从当前线程上的Connection对象 */
            Connection connection = threadLocal.get();
            /* 如果当前线程没有绑定Connection */
            if (connection==null) {
                /* 从数据库连接池中,获取一个Connection对象 */
                connection = dataSource.getConnection();
                /* 并绑定至当前线程 */
                threadLocal.set(connection);
            }
            /* 返回此Connection对象 */
            return connection;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /** 将Connection和当前线程 解绑 */
    public void removeConnection(){
        threadLocal.remove();
    }

}

事务管理工具类(TM)

  • 使用Spring框架的AOP机制,进行事务管理

TransactionManager.java

package cn.water.utils;

import org.aspectj.lang.annotation.*;

import javax.annotation.Resource;
import java.sql.Connection;
import java.sql.SQLException;


@Aspect
public class TransactionManager {

    /* 成员变量 */
    @Resource(name = "connectionUtils")
    private ConnectionUtils connectionUtils;

    /** 切入点 */
    @Pointcut(value = "execution(* cn.water.utils.*.*(..))")
    public void all(){}

    /** 开启事务(前置通知) */
    @Before("all()")
    public void beginTransaction(){
        try {
            connectionUtils.getThreadConnection().setAutoCommit(false);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    /** 提交事务(后置通知) */
    @AfterReturning("all()")
    public void commit(){
        try {
            connectionUtils.getThreadConnection().commit();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    /** 回滚事务(异常通知) */
    @AfterThrowing("all()")
    public void rollback(){
        try {
            connectionUtils.getThreadConnection().rollback();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    /** 释放连接(最终通知) */
    @After("all()")
    public void release(){
        try {
            /* 返回至连接池 */
            connectionUtils.getThreadConnection().close();
            /* 将Connection对象与线程解绑 */
            connectionUtils.removeConnection();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }


}

业务层

  • 直接调用事务管理工具类的方法,实现事务管理

AccountService.java

package cn.water.service;


import cn.water.domain.Account;


public interface AccountService {

    /** 转账 */
    void transfer(int sourceId, int targetId, float money);

}

AccountServiceImp.java

package cn.water.service;

import cn.water.dao.AccountDao;
import cn.water.domain.Account;
import cn.water.utils.TransactionManager;

import java.util.List;


public class AccountServiceImp implements AccountService {

    /* 成员变量 */
    private AccountDao dao;
    private TransactionManager txManager;

    /* 设值方法 */
    public void setDao(AccountDao dao) {
        this.dao = dao;
    }
    public void setTxManager(TransactionManager txManager) {
        this.txManager = txManager;
    }

    /** 转账 */
    public void transfer(int sourceId, int targetId, float money) {
        try {
            /* 开启事务 */
            txManager.beginTransaction();
            /* 执行操作 */
            Account source = dao.findByID(sourceId);
            Account target = dao.findByID(targetId);
            Float sourceMoney = source.getMoney();
            Float targetMoney = target.getMoney();
            source.setMoney(sourceMoney - money);
//            int i = 10/0;
            target.setMoney(targetMoney + money);
            dao.update(source);
            dao.update(target);
            /* 提交事务 */
            txManager.commit();
        } catch (Exception e) {
            /* 回滚 */
            txManager.rollback();
            /* 抛出异常 */
            throw new RuntimeException();
        } finally {
            /* 释放连接 */
            txManager.release();
        }

    }


}

配置文件

  • QueryRunner(数据库操作类)
  • DataSource(数据库连接池)
  • 开启Spring注解扫描
  • 开启SpringAOP注解支持

Beans.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.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">

    <!-- DataSource -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="com.mysql.jdbc.Driver"/>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/spring"/>
        <property name="user" value="root"/>
        <property name="password" value="root"/>
    </bean>

    <!-- QueryRunner -->
    <bean id="runner" class="org.apache.commons.dbutils.QueryRunner"></bean>

    <!-- 开启Spring注解扫描 -->
    <context:component-scan base-package="cn.water"/>

    <!-- 开启SpringAOP注解支持 -->
    <aop:aspectj-autoproxy/>

</beans>

测试类

SpringTest.java

package cn.water.test;

import cn.water.service.AccountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import javax.annotation.Resource;


@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:Beans.xml")
public class SpringTest {

    @Resource(name = "serviceImp")
    private AccountService service;

    @Test
    public  void testTransfer(){
         服务。传递(1210F; } } ``
    







发布了68 篇原创文章 · 获赞 2 · 访问量 1927

猜你喜欢

转载自blog.csdn.net/qq_40981851/article/details/104178134