JAVA编程118——事务控制/动态代理

一、目录结构

在这里插入图片描述

二、数据库结构及测试数据

在这里插入图片描述

三、代码详解

1、xml配置文件:pom.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">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.mollen</groupId>
    <artifactId>exam</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>

        <dependency>
            <groupId>c3p0</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.1.2</version>
        </dependency>

        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.2.2</version>
        </dependency>
        
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.38</version>
        </dependency>

        <dependency>
            <groupId>commons-dbutils</groupId>
            <artifactId>commons-dbutils</artifactId>
            <version>1.6</version>
        </dependency>
        
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.7.0</version>
                <configuration>
                    <target>1.7</target>
                    <source>1.7</source>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

2、数据源配置文件:db.properties
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.jdbcUrl=jdbc:mysql://localhost:3306/my_db2
jdbc.user=root
jdbc.password=root
3、实体类:Account.java
package com.mollen.bean;

/**
 * @ClassName: Account
 * @Auther: Mollen
 * @CreateTime: 2018-11-06  11:53:29
 * @Description:
 */
public class Account {
    private int id;
    private String name;
    private double money;

    public Account() {
    }

    public Account(int id, String name, double money) {
        this.id = id;
        this.name = name;
        this.money = money;
    }

    @Override
    public String toString() {
        return "Account{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", money=" + money +
                '}';
    }
    
    //...getter/setter
}
4、dao层:

1、Dao接口:AccountDao.java

package com.mollen.dao;

import com.mollen.bean.Account;

/**
 * @ClassName: AccountDao
 * @Auther: Mollen
 * @CreateTime: 2018-11-06  11:55:11
 * @Description:
 */
public interface AccountDao {

    /**
     * 1.根据用户名查询账户信息
     * @return
     */
    public Account findByName(String name) throws Exception;

    /**
     * 2.更新账户信息
     */
    public void upDate(String name,double money) throws Exception;

}

2、Dao实现类:AccountDaoImpl.java

package com.mollen.dao.impl;

import com.mollen.bean.Account;
import com.mollen.dao.AccountDao;
import com.mollen.utils.JdbcUtils;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;

import java.sql.SQLException;

/**
 * @ClassName:  AccountDaoImpl
 * @Auther:     Mollen
 * @CreateTime: 2018-11-06  11:58:35
 * @Description:
 */
public class AccountDaoImpl implements AccountDao {

    QueryRunner queryRunner = new QueryRunner();

    public void setQueryRunner(QueryRunner queryRunner) {
        this.queryRunner = queryRunner;
    }

    /**
     * 1.根据用户名查询账户信息
     * @return
     */
    public Account findByName(String name) throws SQLException{
            return   queryRunner.query(JdbcUtils.getConnection(),"SELECT * FROM account WHERE name = ?",new BeanHandler<Account>(Account.class),name);
    }

    /**
     * 2.更新账户信息
     */
    public void upDate(String name,double money) throws SQLException{
            queryRunner.update(JdbcUtils.getConnection(),"UPDATE account SET money = ? WHERE name = ?",money,name);
    }

}

5、Service层:

1、Service层接口:AccountService.java

package com.mollen.service;

/**
 * @ClassName: AccountService
 * @Auther: Mollen
 * @CreateTime: 2018-11-06  14:13:50
 * @Description:
 */
public interface AccountService {

    /**
     * 转账业务
     * @param source
     * @param target
     * @param money
     * @throws Exception
     */
    public void transfer(String source,String target,double money) throws Exception;

}

2、Service接口实现类:AccountServiceImpl.java

package com.mollen.service.impl;

import com.mollen.bean.Account;
import com.mollen.dao.AccountDao;
import com.mollen.dao.impl.AccountDaoImpl;
import com.mollen.service.AccountService;

/**
 * @ClassName: AccountServiceImpl
 * @Auther: Mollen
 * @CreateTime: 2018-11-06  14:17:16
 * @Description:
 */
public class AccountServiceImpl implements AccountService {

    private AccountDao accountDao = new AccountDaoImpl();

    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }

    public void transfer(String source, String target, double money) throws Exception {
        //1.查询转出账户的账户余额
        Account source_account = accountDao.findByName(source);
        //2.转出账户的余额减少
        source_account.setMoney(source_account.getMoney()-money);
        //3.修改转出账户余额
        accountDao.upDate(source,source_account.getMoney());

        //手动抛出异常
        //int i = 10 /0 ;

        //1.查询转入账户的余额
        Account target_account = accountDao.findByName(target);
        //2.转入账户余额增加
        target_account.setMoney(target_account.getMoney()+money);
        //3.修改转入账户的余额
        accountDao.upDate(target,target_account.getMoney());

    }
}

6、创建jdbc工具类:JdbcUtils.java

package com.mollen.utils;

import com.mchange.v2.c3p0.ComboPooledDataSource;

import javax.sql.DataSource;
import java.beans.PropertyVetoException;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.util.Properties;

/**
 * @ClassName:  JdbcUtils
 * @Auther:     Mollen
 * @CreateTime: 2018-11-06  14:49:43
 * @Description:
 *
 */
public class JdbcUtils {

    //1.创建线程标志对象存储对象(保证事务过程中用的是同一个连接)
    private static ThreadLocal<Connection> threadLocal = new InheritableThreadLocal<>();

    //3.获取线程池连接对象
    public static Connection getConnection(){
        //获取标志
        Connection connection = threadLocal.get();
        try {
            if (connection == null) {
                connection  = JdbcUtils.getDataSource().getConnection();
                //存入标志
                threadLocal.set(connection);
            }
            return connection;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    //4.获取数据源
    public static DataSource getDataSource() throws IOException, PropertyVetoException {

        ComboPooledDataSource dataSource = new ComboPooledDataSource();

        Properties properties = new Properties();
        InputStream is = JdbcUtils.class.getClassLoader().getResourceAsStream("db.properties");
        properties.load(is);

        dataSource.setDriverClass(properties.getProperty("jdbc.driverClass"));
        dataSource.setJdbcUrl(properties.getProperty("jdbc.jdbcUrl"));
        dataSource.setUser(properties.getProperty("jdbc.user"));
        dataSource.setPassword(properties.getProperty("jdbc.password"));

        return dataSource;
    }

    //5.解除线程与连接绑定
    public static void remove(){
        threadLocal.remove();
    }
}

7、创建事务类:TransactionManager.java

package com.mollen.utils;

import java.sql.SQLException;

/**
 * @ClassName: TransactionManager
 * @Auther: Mollen
 * @CreateTime: 2018-11-06  14:49:58
 * @Description:
 */
public class TransactionManager {

    /**
     * 1.开启事务
     */
    public static void startTransaction(){
        try {
            JdbcUtils.getConnection().setAutoCommit(false);
            System.out.println("事务开启!");
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    /**
     * 2.提交事务
     */
    public static void commitTransaction(){
        try {
            JdbcUtils.getConnection().commit();
            System.out.println("事务提交!");
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    /**
     * 3.回滚事务
     */
    public static void rollbackTransaction(){
        try {
            JdbcUtils.getConnection().rollback();
            System.out.println("事务回滚!");
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    /**
     * 4.关闭连接
     */
    public static void close(){
        try {
            JdbcUtils.getConnection().close();
            System.out.println("连接关闭!");
        } catch (SQLException e) {
            e.printStackTrace();
        }

    }
}

8、创建动态代理测试类

package com.mollen.test;

import com.mollen.service.AccountService;
import com.mollen.service.impl.AccountServiceImpl;
import com.mollen.utils.TransactionManager;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.junit.Test;

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


/**
 * @ClassName: MyTest
 * @Auther: Mollen
 * @CreateTime: 2018-11-06  18:41:31
 * @Description:
 */

public class MyTest {
    AccountService accountService = new AccountServiceImpl();


    /**
     * 1、第一种方案:Jdk动态代理方式进行事务控制 --> 真实对象必须实现接口
     */
    @Test
    public void transfer1() {
        /**
         *  ClassLoader loader, 真实对象的类加载器
         *  Class<?>[] interfaces, 真实对象实现的所有接口
         *  InvocationHandler h  代理对象的处理类
         */
        AccountService accountService_proxy = (AccountService) Proxy.newProxyInstance(
                accountService.getClass().getClassLoader(),
                accountService.getClass().getInterfaces(),
                new InvocationHandler() {
                    /**
                     * 调用代理对象任意方法,InvocationHandler中的invoke()都会执行
                     * @param proxy  代理对象
                     * @param method 代理对象调用的方法
                     * @param args  代理对象调用方法时掺入的参数
                     * @return
                     * @throws Throwable
                     */
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        //声明一个事务执行结果
                        Object obj = null;
                        try {
                            //1.开启事务--业务执行之前
                            TransactionManager.startTransaction();
                            //2.执行业务--执行业务操作
                            obj = method.invoke(accountService, args);
                            //3.提交事务--业务正常执行
                            TransactionManager.commitTransaction();
                        } catch (Exception e) {
                            e.printStackTrace();
                            //4.回滚事务--业务执行异常
                            TransactionManager.rollbackTransaction();
                        } finally {
                            //5.关闭连接
                            TransactionManager.close();
                        }
                        return obj;
                    }
                });
        try {
            accountService_proxy.transfer("aaa", "bbb", 100);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 2、第二种方案:使用cglib动态代理技术
     */
    @Test
    public void transfer2() {
        //1. 创建cglib的核心对象
        Enhancer enhancer = new Enhancer();
        //2. 设置产生的代理对象的父类
        enhancer.setSuperclass(accountService.getClass());
        //3. 设置调用代理对象的回调函数
        enhancer.setCallback(new MethodInterceptor() {
            /**
             *
             * @param proxy  代理对象
             * @param method 代理对象执行的方法
             * @param args   代理对象调用方法时传入的参数
             * @param methodProxy  调用方法的代理对象  一般没什么用
             * @return
             * @throws Throwable
             */
            @Override
            public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                Object obj = null;
                try {
                    //1. 在转账业务执行之前  开启事物
                    TransactionManager.startTransaction();
                    //2. 执行原有的转账业务
                    obj = method.invoke(accountService, args);
                    //3. 转账业务执行成功-- 提交事物
                    TransactionManager.commitTransaction();
                } catch (Exception e) {
                    e.printStackTrace();
                    //4. 转账业务执行失败---回滚事物
                    TransactionManager.rollbackTransaction();
                } finally {
                    TransactionManager.close();
                }
                return obj;
            }
        });
        AccountService accountService_proxy = (AccountService) enhancer.create();
        try {
            accountService_proxy.transfer("aaa", "bbb", 100);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 3、第三种方案:使用cglib动态代理技术第二种方式
     */
    @Test
    public void transfer3() {

        AccountService accountService_proxy = (AccountService) Enhancer.create(
                accountService.getClass(),
                new MethodInterceptor() {
                    /**
                     *
                     * @param proxy  代理对象
                     * @param method 代理对象执行的方法
                     * @param args   代理对象调用方法时传入的参数
                     * @param methodProxy  调用方法的代理对象  一般没什么用
                     * @return
                     * @throws Throwable
                     */
                    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {

                        Object obj = null;
                        try {
                            //1. 在转账业务执行之前  开启事物
                            TransactionManager.startTransaction();
                            //2. 执行原有的转账业务
                            obj = method.invoke(accountService, args);
                            //3. 转账业务执行成功-- 提交事物
                            TransactionManager.commitTransaction();
                        } catch (Exception e) {
                            e.printStackTrace();
                            //4. 转账业务执行失败---回滚事物
                            TransactionManager.rollbackTransaction();
                        } finally {
                            TransactionManager.close();
                        }
                        return obj;
                    }
                });
        try {
            accountService_proxy.transfer("aaa", "bbb", 100);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

猜你喜欢

转载自blog.csdn.net/mollen/article/details/83782763