spring framework curriculum - Personal Stories ioc (xml), adding transfer method, and demonstrate affairs

1. Based on: https://www.cnblogs.com/ccoonngg/p/11223340.html

2. The method of increasing the transfer interface AccountService

    / * * 
     * Transfer 
     * @param sourceId Out account 
     * @param targetId into account 
     * @param money transfer amount 
     * / 
    void Transfer ( int sourceId, int the targetId, a float Money);

3. The increase in the implementation class AccountServiceImpl

    @Override
     public  void Transfer ( int sourceId, int the targetId, a float Money) {
         // 1. Find transfer account 
        the Account sourceAccount = accountDao.findAccountById (sourceId);
         // 2. Find into account 
        the Account targetAccount = accountDao.findAccountById (the targetId) ;
         // 3 turn out to reduce the amount of accounts 
        sourceAccount.setMoney (sourceAccount.getMoney () - Money);
         // 4. into account the amount of increase or decrease 
        targetAccount.setMoney (targetAccount.getMoney () + Money);
         // . 5. update roll-out account
        accountDao.updateAccount (sourceAccount);
         // 6. The update into account 
        accountDao.updateAccount (targetAccount); 
    }

4. The increase in unit test test classes

    @Test
    public void transfer(){
       as.transfer(1,2,100);
    }

The content execution unit after the test database

6. If, however, in the sixth step AccountServiceImpl Before inserting a wrong statement would, for example,

        // 5. Update Out account 
        accountDao.updateAccount (sourceAccount);
         int I = . 1 / 0 ;
         // 6. The update into account 
        accountDao.updateAccount (targetAccount);

7. The unit then once again with the following results

Because before the update roll-out accounts, the program is wrong, stop running, but previously have been performed

That is no transaction support

the reason

Under the current arrangement, Dbutils configuration object QueryRunner each will create a new QueryRunner

And each time a connection object come from the data source at the time of performing an operation

<!--配置QueryRunner-->
<bean id="runner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype">
<!--注入数据源-->
<constructor-arg name="ds" ref="dataSource"></constructor-arg>
</bean>

<!-- 配置数据源 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<!--连接数据库的必备信息-->
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/cong"></property>
<property name="user" value="root"></property>
<property name="password" value="123456"></property>
</bean>

As can be seen by the figure below it several times to interact with the database

8. The improvement, improved code become unrecognizable

So just as a new project in terms of

1. Create a maven project, import the relevant dependent

<?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.cong</groupId>
    <artifactId>spring_accountTransfer_transaction</artifactId>
    <version>1.0-SNAPSHOT</version>

    <packaging>jar</packaging>
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <!-- dbutils的依赖 -->
        <dependency>
            <groupId>commons-dbutils</groupId>
            <artifactId>commons-dbutils</artifactId>
            <version>1.4</version>
        </dependency>
        <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>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
    </dependencies>
</project>

2.在java目录下创建com.cong.utils.ConnectionUtils类

package com.cong.utils;

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

/**
 * 连接的工具类,它用于从数据源中获取一个连接,并且实现和线程的绑定
 *
 */
public class ConnectionUtils {
    //需要一个Connection类型的ThreadLocal,直接实例化出来
    private ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
    //这个DataSource不能new,只能等着spring为我们注入,所以需要seter方法
    private DataSource dataSource;
    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }
    //获取当前线程的连接
    public Connection getThreadConnection(){
        try{
            //1.先从ThreadLocal上获取
            Connection conn = tl.get();
            //2.判断当前线程上是否有连接
            if (conn == null) {
                //3.从数据源中获取一个连接,并且存入ThreadLocal中
                conn = dataSource.getConnection();
                tl.set(conn);
            }
            //4.返回当前线程上的连接
            return conn;
        }catch (Exception e){
            throw new RuntimeException(e);
        }
    }

    /**
     * 把连接和线程解绑
     */
    public void removeConnection() {
        tl.remove();
    }
}

3.在utils包下创建TransactionManager类

package com.cong.utils;

/**
 * 和事务管理相关的工具类,它包含了,开启事务,提交事务,回滚事务和释放连接
 * 这个类如何执行呢?与事务相关,首先要有connection,并且是当前线程的connection
 * 所以需要用到ConnectionUtils类
 * 并且提供一个setter方法,等着spring注入
 */
public class TransactionManager {

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

    /**
     * 开启事务
     */
    public  void beginTransaction(){
        try {
            connectionUtils.getThreadConnection().setAutoCommit(false);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     * 提交事务
     */
    public  void commit(){
        try {
            connectionUtils.getThreadConnection().commit();
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     * 回滚事务
     */
    public  void rollback(){
        try {
            connectionUtils.getThreadConnection().rollback();
        }catch (Exception e){
            e.printStackTrace();
        }
    }


    /**
     * 释放连接
     */
    public  void release(){
        try {
            connectionUtils.getThreadConnection().close();//还回连接池中
            connectionUtils.removeConnection();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

4.在java目录下创建com.cong.pojo.Account类

package com.cong.pojo;

public class Account {
    private int id;
    private String name;
    private float money;

    public int getId() {
        return id;
    }

    public void setId(int 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;
    }

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

5.在java目录下创建com.cong.dao包,并且创建Account持久层相关的接口和类

package com.cong.dao;
import com.cong.pojo.Account;
import java.util.List;
public interface AccountDao {
    List<Account> findAllAccount();//find all
    Account findAccountById(int id);//find one
    void saveAccount(Account account);//save
    void updateAccount(Account account);//update
    void deleteAccount(int id);//delete
}



package com.cong.dao;
import com.cong.pojo.Account;
import com.cong.utils.ConnectionUtils;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import java.util.List;
/**
 * 从bean.xml中可以发现
 * <bean id="accountDao" class="com.cong.dao.AccountDaoImpl">
 <property name="runner" ref="runner"></property>
 * </bean>
 * <bean id="runner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype">
 *      <constructor-arg name="ds" ref="dataSource"></constructor-arg>
 * </bean>
 * accountDao注入了一个QueryRunner对象,QueryRunner就会从连接池里面自动获取一个连接
 *但是我们现在不希望它从连接池里面自动获取,所以需要在AccountDaoImpl里面有一个ConnectionUtils对象,来获取连接
 * 然后需要对runner的query方法添加一个连接的参数
 */
public class AccountDaoImpl implements AccountDao {
    private QueryRunner runner;
    public void setRunner(QueryRunner runner) {
        this.runner = runner;
    }
    private ConnectionUtils connectionUtils;
    public void setConnectionUtils(ConnectionUtils connectionUtils) {
        this.connectionUtils = connectionUtils;
    }

    @Override
    public List<Account> findAllAccount() {
        try {
            return runner.query(connectionUtils.getThreadConnection(),"select * from account", new BeanListHandler<Account>(Account.class));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public Account findAccountById(int id) {
        try{
            return runner.query(connectionUtils.getThreadConnection(),"select * from account where id = ? ",new BeanHandler<Account>(Account.class),id);
        }catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void saveAccount(Account account) {
        try {
            runner.update(connectionUtils.getThreadConnection(),"insert into account(name,money) values(?,?)",account.getName(),account.getMoney());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void updateAccount(Account account) {
        try {
            runner.update(connectionUtils.getThreadConnection(),"update account set name = ?,money = ? where id =?",account.getName(),account.getMoney(),account.getId());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void deleteAccount(int id) {
        try {
            runner.update(connectionUtils.getThreadConnection(),"delete from account where id = ?",id);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

6.在java目录下创建com.cong.service,并且创建服务层相关的接口和类

package com.cong.service;

import com.cong.pojo.Account;

import java.util.List;

public interface AccountService {
    List<Account> findAllAccount();//find all
    Account findAccountById(int id);//find one
    void saveAccount(Account account);//save
    void updateAccount(Account account);//update
    void deleteAccount(int id);//delete
    /**
     * 转账
     * @param sourceId  转出账户
     * @param targetId  转入账户
     * @param money     转账金额
     */
    void transfer(int sourceId, int targetId, float money);
}



package com.cong.service;

import com.cong.dao.AccountDao;
import com.cong.pojo.Account;
import com.cong.utils.TransactionManager;

import java.util.List;

/**
 * 账户的业务层实现类
 * 事务控制应该都在业务层的
 */
public class AccountServiceImpl implements AccountService {
    //业务层都是调用持久层的,所以需要有一个持久层的对象
    private AccountDao accountDao;
    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }
    //添加事务支持需要用到TransactionManager的对象,这个对象不能自己创建,只能有spring注入,所以有setter方法
    private TransactionManager tm;
    public void setTm(TransactionManager tm) {
        this.tm = tm;
    }

    /**
     * 以下所有操作都可以加上事务控制
     * 开启事务,执行操作,提交事务(返回结果),回滚操作,关闭事务
     */
    @Override
    public List<Account> findAllAccount() {
        try {
            //1.开启事务
            tm.beginTransaction();
            //2.执行操作
            List<Account> accounts = accountDao.findAllAccount();
            //3.提交事务
            tm.commit();
            //4.返回结果
            return accounts;
        }catch (Exception e){
            //5.回滚操作
            tm.rollback();
            throw new RuntimeException(e);
        }finally {
            //6.关闭事务
            tm.release();
        }
    }

    @Override
    public Account findAccountById(int id) {
        try {
            //1.开启事务
            tm.beginTransaction();
            //2.执行操作
            Account account = accountDao.findAccountById(id);
            //3.提交事务
            tm.commit();
            //4.返回结果
            return account;
        }catch (Exception e){
            //5.回滚操作
            tm.rollback();
            throw new RuntimeException(e);
        }finally {
            //6.关闭事务
            tm.release();
        }
    }

    @Override
    public void saveAccount(Account account) {
        try {
            //1.开启事务
            tm.beginTransaction();
            //2.执行操作
            accountDao.saveAccount(account);
            //3.提交事务
            tm.commit();
        }catch (Exception e){
            //5.回滚操作
            tm.rollback();
            throw new RuntimeException(e);
        }finally {
            //6.关闭事务
            tm.release();
        }

    }

    @Override
    public void updateAccount(Account account) {
        try {
            //1.开启事务
            tm.beginTransaction();
            //2.执行操作
            accountDao.updateAccount(account);
            //3.提交事务
            tm.commit();
        }catch (Exception e){
            //5.回滚操作
            tm.rollback();
            throw new RuntimeException(e);
        }finally {
            //6.关闭事务
            tm.release();
        }

    }

    @Override
    public void deleteAccount(int id) {
        try {
            //1.开启事务
            tm.beginTransaction();
            //2.执行操作
            accountDao.deleteAccount(id);
            //3.提交事务
            tm.commit();
        }catch (Exception e){
            //5.回滚操作
            tm.rollback();
            throw new RuntimeException(e);
        }finally {
            //6.关闭事务
            tm.release();
        }
    }

    @Override
    public void transfer(int sourceId, int targetId, float money) {
        try {
            //1.开启事务
            tm.beginTransaction();
            //2.执行操作
            //2.1.查找转账账户
            Account sourceAccount = accountDao.findAccountById(sourceId);
            //2.2.查找转入账户
            Account targetAccount = accountDao.findAccountById(targetId);
            //2.3.转出账户减少金额
            sourceAccount.setMoney(sourceAccount.getMoney() - money);
            //2.4.转入账户增减金额
            targetAccount.setMoney(targetAccount.getMoney() + money);
            //2.5.更新转出账户
            accountDao.updateAccount(sourceAccount);
            //2.6.更新转入账户
            accountDao.updateAccount(targetAccount);
            //3.提交事务
            tm.commit();
        }catch (Exception e){
            //5.回滚操作
            tm.rollback();
            throw new RuntimeException(e);
        }finally {
            //6.关闭事务
            tm.release();
        }
    }
}

7.在resources下创建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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!-- 配置Service -->
    <bean id="accountService" class="com.cong.service.AccountServiceImpl">
        <!-- 注入dao -->
        <property name="accountDao" ref="accountDao"></property>
        <property name="tm" ref="tm"></property>
    </bean>

    <!--配置Dao对象-->
    <bean id="accountDao" class="com.cong.dao.AccountDaoImpl">
        <!-- 注入QueryRunner -->
        <property name="runner" ref="runner"></property>
        <property name="connectionUtils" ref="connectionUtils"></property>
    </bean>

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

    <!-- 配置数据源 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <!--连接数据库的必备信息-->
        <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/cong"></property>
        <property name="user" value="root"></property>
        <property name="password" value="123456"></property>
    </bean>
    <bean id="connectionUtils" class="com.cong.utils.ConnectionUtils">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <bean id="tm" class="com.cong.utils.TransactionManager">
        <property name="connectionUtils" ref="connectionUtils"></property>
    </bean>
</beans>

8.在test,java下创建测试类

import com.cong.pojo.Account;
import com.cong.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;

import java.util.List;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:bean.xml")
public class AccountServiceTest {
    @Autowired
    AccountService as;
    @Test
    public void transfer(){
        as.transfer(1,2,100);
    }
   @Test
    public void testFindAll(){
       List<Account> list = as.findAllAccount();
       for (Account account : list) {
           System.out.println(account.toString());
       }
   }
    @Test
    public void testFindOne(){
        Account account = as.findAccountById(1);
        System.out.println(account.toString());
    }
    @Test
    public void testSave(){
       Account account = new Account();
       account.setName("cong");
       account.setMoney(5);
       as.saveAccount(account);
    }
    @Test
    public void testUpdate(){
        Account account = new Account();
        account.setName("rainbow");
        account.setMoney(50000);
        account.setId(3);
        as.updateAccount(account);
    }
    @Test
    public void testDelete(){
        as.deleteAccount(4);
    }
}

9.单元测试transfer,前后结果

10.一样,在转账的过程中添加一个会出错的语句

            //2.5.更新转出账户
            int i = 1/0;
            accountDao.updateAccount(sourceAccount);
            //2.6.更新转入账户
            accountDao.updateAccount(targetAccount);

11.运行结果,程序报错,数据库中的内容没有改变

 

 11.以上案例虽然添加了事务管理,但是有一些弊端‘

  *配置文件非常麻烦,很多依赖注入乱七八糟

  *service用到事务管理器TransactionManager,dao用到connectionUtils

  * 事务管理器也用到connectionUtils,它们之间都有一些互相依赖

  *更重要的是AccountServiceImpl中代码有很多的重复

所以就需要用到aop

Guess you like

Origin www.cnblogs.com/ccoonngg/p/11229393.html