手動實現IOC與事務控制-基於JDBC-1

手動實現IOC與事務控制基於JDBC

sql

在這裏插入圖片描述

DROP TABLE IF EXISTS `account`;
CREATE TABLE `account` (
  `name` varchar(50) NOT NULL COMMENT ' 姓名',
  `cardNo` varchar(50) NOT NULL COMMENT '卡號',
  `money` int(11) NOT NULL COMMENT '金額'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of account
-- ----------------------------
INSERT INTO `account` VALUES ('張三', '123456', '10000');
INSERT INTO `account` VALUES ('李四', '456789', '10000');

新建項目

在這裏插入圖片描述
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">
    <parent>
        <artifactId>stage01-spring</artifactId>
        <groupId>com.liu</groupId>
        <version>1.0.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>spring-classroom</artifactId>
    <packaging>war</packaging>


    <dependencies>
        <dependency>
            <groupId>dom4j</groupId>
            <artifactId>dom4j</artifactId>
            <version>1.6.1</version>
        </dependency>
        <dependency>
            <groupId>jaxen</groupId>
            <artifactId>jaxen</artifactId>
            <version>1.2.0</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.22</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.49</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.71</version>
        </dependency>
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.3.0</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.10</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>8</source>
                    <target>8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat7-maven-plugin</artifactId>
                <version>2.2</version>
                <configuration>
                    <port>9099</port>
                    <path>/</path>
                    <server>tomcat7</server>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

web.xml

<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                   http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1"
         metadata-complete="false">


    <welcome-file-list>
        <welcome-file>index.html</welcome-file>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>
</web-app>

Servlet

package com.liu.spring.servlet;

import com.alibaba.fastjson.JSON;
import com.liu.spring.factory.BeanFactory;
import com.liu.spring.result.Result;
import com.liu.spring.service.TransferService;
import com.liu.spring.service.impl.TransferServcieImpl;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.sql.SQLException;

/**
 * @Description
 * @ClassName TransferServlet
 * @Author 劉楠
 * @date 2020.06.24
 */
@WebServlet(name = "transferServlet",urlPatterns = "/transferServlet")
public class TransferServlet extends HttpServlet {
    /**
     * http mine,json類型
     */
    public static final String MIME_TYPE_JSON = "application/json";
    /**
     * http mine,json類型指定utf-8編碼
     */
    public final static String MIME_TYPE_JSON_UTF8 = MIME_TYPE_JSON + ";charset=UTF-8";
    //1.實例化TransferService
    private TransferService transferService = new TransferServcieImpl();

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
       doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //設置請求的字體編碼
        req.setCharacterEncoding("UTF-8");

        String fromCardNo=req.getParameter("fromCardNo");
        String toCardNo=req.getParameter("toCardNo");
        String moneyValue = req.getParameter("money");
        Integer money = Integer.valueOf(moneyValue);

		 //默認就是200
        Result result = new Result();
        try {
           

            transferService.transfer(fromCardNo, toCardNo,money);
        } catch (Exception e) {
            e.printStackTrace();
            result.setResult(Result.ResultEnum.SERVER_ERROR);
        }

        //設置響應字符編碼
        resp.setContentType(MIME_TYPE_JSON_UTF8);

        resp.getWriter().println(JSON.toJSON(result));

    }
}
##### Result 
```java

/**
 * @Description
 * @ClassName Result
 * @Author 劉楠
 * @date 2020.06.24
 */
public class Result<T> implements Serializable {

    private static final long serialVersionUID = -645547982453859521L;
    private Integer status;

    private String message;
    private T data;


    public Result() {
        this(ResultEnum.SUCCESS);
    }
    public Result(ResultEnum result) {
        setResult(result.getStatus(), result.getMessage());
    }
    public void setResult(ResultEnum result) {
        status = result.getStatus();
        message = result.getMessage();
    }

    public void setResult(int status, String message) {
        this.status = status;
        this.message = message;
    }


    public Integer getStatus() {
        return status;
    }

    public void setStatus(Integer status) {
        this.status = status;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }

    @Override
    public String toString() {
        return new StringJoiner(", ", Result.class.getSimpleName() + "[", "]")
                .add("status=" + status)
                .add("message='" + message + "'")
                .add("data=" + data)
                .toString();
    }

    public enum ResultEnum {
        SUCCESS(200),
        PARAM_ERROR(400),
        NO_AUTH(401),
        FORBIDDEN(403),
        SERVER_ERROR(500),
        FAILED(-1);

        private int status;
        private final static String[] initMsgs = { "執行成功","提交的數據錯誤","身份驗證失敗","無權限訪問","執行錯誤","執行失敗" };

        private ResultEnum(int status) {
            this.status = status;
        }

        public int getStatus() {
            return status;
        }

        /**
         * 返回結果描述.
         *
         * @return 結果描述String
         */
        public String getMessage() {
            switch (status) {
                case 200:
                    return initMsgs[0];
                case 400:
                    return initMsgs[1];
                case 401:
                    return initMsgs[2];
                case 403:
                    return initMsgs[3];
                case 500:
                    return initMsgs[4];
                case -1:
                    return initMsgs[5];
                default:
                    break;
            }
            return null;
        }
    }
}

service

public interface TransferService {

    boolean transfer(String fromAccount, String toAccount, Integer money) throws SQLException, Exception;
}

service 實體類
package com.liu.spring.service.impl;

import com.liu.spring.dao.AccountDao;
import com.liu.spring.dao.impl.AccountDaoImpl;
import com.liu.spring.entity.Account;
import com.liu.spring.factory.BeanFactory;
import com.liu.spring.service.TransferService;
import com.liu.spring.transaction.TransactionManager;
import com.liu.spring.util.ConnectionUtils;

import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.SQLException;

/**
 * @Description
 * @ClassName TransferServcieImpl
 * @Author 劉楠
 * @date 2020.06.23
 */
public class TransferServcieImpl implements TransferService {

	private AccountDao accountDao = new AccountDaoImpl();

    @Override
    public boolean transfer(String fromAccount, String toAccount, Integer money) throws Exception {
        System.out.println("=====開始轉賬==== ");

      

            Account fAccount = accountDao.queryByCardNo(fromAccount);
            Account tAccount = accountDao.queryByCardNo(toAccount);

            fAccount.setMoney(fAccount.getMoney()-money);
            tAccount.setMoney(tAccount.getMoney()+money);
            accountDao.updateAccountByCardno(fAccount);
            //int i=1/0; 異常用於測試 事務
            accountDao.updateAccountByCardno(tAccount);

        return true;
    }

}

dao

public interface AccountDao {

    /**
     * 根據卡號查詢
     * @param cardNo
     * @return
     */
     Account queryByCardNo(String cardNo) throws SQLException;

    int  updateAccountByCardno(Account fAccount) throws SQLException;
}

dao實現類
package com.liu.spring.dao.impl;

import com.alibaba.druid.pool.DruidPooledConnection;
import com.liu.spring.dao.AccountDao;
import com.liu.spring.entity.Account;
import com.liu.spring.util.ConnectionUtils;
import com.liu.spring.util.DruidUtils;

import javax.swing.*;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * @Description
 * @ClassName AccountDaoImpl
 * @Author 劉楠
 * @date 2020.06.23
 */
public class AccountDaoImpl implements AccountDao {
    @Override
    public Account queryByCardNo(String cardNo) throws SQLException {
        //從連接池獲取連接
		Connection connection = DruidUtils.getInstance().getConnection();
        System.out.println("queryByCardNo connection "+connection.toString());
        String sql = "select name ,cardNo ,money from account where cardNo=?";
        //預編譯SQL
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        //填充參數
        preparedStatement.setString(1, cardNo);
        //執行查詢
        ResultSet resultSet = preparedStatement.executeQuery();
        Account account = null;
        while (resultSet.next()) {
            account = new Account();
            account.setMoney(resultSet.getInt("money"));
            account.setCardNo(resultSet.getString("cardNo"));
            account.setName(resultSet.getString("name"));
        }
        preparedStatement.close();
        connection.close(); 
        return account;
    }

    @Override
    public int updateAccountByCardno(Account fAccount) throws SQLException {
        Connection connection = DruidUtils.getInstance().getConnection();
     
        System.out.println("updateAccountByCardno connection "+connection.toString());
        String sql = "update account set money =? where cardNo=? ";
        //預編譯SQL
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        //設置參數
        preparedStatement.setInt(1, fAccount.getMoney());
        preparedStatement.setString(2, fAccount.getCardNo());
        //執行更新
        int update = preparedStatement.executeUpdate();
        preparedStatement.close();
        connection.close(); 
        return update;
    }
}

實體類

public class Account  implements Serializable {
    private static final long serialVersionUID = -6836411742270629093L;
    /**
     * 帳戶名稱
     */
    private String name;
    /**
     * 卡號
     */
    private String cardNo;
    /**
     * 金額,餘額
     */
    private Integer money;


    public String getName() {
        return name;
    }

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

    public String getCardNo() {
        return cardNo;
    }

    public void setCardNo(String cardNo) {
        this.cardNo = cardNo;
    }

    public Integer getMoney() {
        return money;
    }

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

工具類

DruidUtils

public class DruidUtils {

    private DruidUtils(){

    }
    private static DruidDataSource druidDataSource = new DruidDataSource();

    static {
        druidDataSource.setUrl("jdbc:mysql://127.0.0.1/bank?characterEncoding=utf8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&useSSL=false");
        druidDataSource.setDriverClassName("com.mysql.jdbc.Driver");
        druidDataSource.setUsername("root");
        druidDataSource.setPassword("root");
    }

    public static DruidDataSource getInstance(){
        return druidDataSource;
    }




    public static void main(String[] args) throws SQLException {
        System.out.println(DruidUtils.getInstance().getConnection().toString());
    }
}

使用tomcat7插件啓動

在這裏插入圖片描述

測試 postman

在這裏插入圖片描述
正常執行沒有問題
在這裏插入圖片描述
數據庫也正常,

打開異常代碼-重啓項目

在這裏插入圖片描述
再次發送同樣的請求
在這裏插入圖片描述
看數據庫
在這裏插入圖片描述
第一個記錄扣了錢,但第二記錄並沒有增加,錢不見了,因爲沒有事務控制

現在的問題
  1. Servlet中使用了new 關鍵字創建Service–代碼耦合
    在這裏插入圖片描述
  2. Service中也使用的new 創建dao—代碼耦合
    在這裏插入圖片描述
  3. 沒有事務事務
一共2個大問題
代碼代碼耦合
 *  使用工廠來創建對象,不使用new 
沒有事務
  • 在servcie中的select,update方法每次都會創建一個新的連接,這樣不是一個Connection,
  • 同時每次執行完都把連接關閉了,再次又要重新獲取新連接 肯定不是一個事務的見下方代碼
    在這裏插入圖片描述
    在這裏插入圖片描述

在這裏插入圖片描述
* 事務控制JDBC默認是在dao層Connection對象的,控制在Service層中,解決就是把事務控制放在Service中
在這裏插入圖片描述

解決第一個問題-代碼耦合

使用工廠來解決
使用配置文件的方式來聲明bean
在這裏插入圖片描述
beans.xml

<beans>

    <!--id標識 class 類的全限定類名-->
    <bean id="transferService" class="com.liu.spring.service.impl.TransferServcieImpl">
        <property name="AccountDao" ref="accountDao"/>
    </bean>

    <bean id="accountDao" class="com.liu.spring.dao.impl.AccountDaoImpl">
    </bean>
</beans>

只是名稱和spring一樣,沒有任何關係
1.創建工廠來解析XML,
2.把對象實例保存
3. 提供接口獲取對象

package com.liu.spring.factory;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;


import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Description 使用反射創建對象
 * @ClassName BeanFactory
 * @Author 劉楠
 * @date 2020.06.24
 */
public class BeanFactory {
    /**
     * 1.解析xml,將bean保存
     * 2.提供獲取對象的接口
     */

    /**
     * 保存對象
     */
    private static    Map<String, Object> beansMap = new ConcurrentHashMap<>();

    private static  String beansXml="beans.xml";
    static {
        //解析xml 保存在map中
        //1.加載xml
        InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream(beansXml);
        try {
            Document document = new SAXReader().read(in);
            //獲取根據節點
            Element rootElement = document.getRootElement();
            //獲取所有bean的標籤
            List<Element> beanList = rootElement.selectNodes("//bean");

            for (Element element : beanList) {
                /**
                 * 獲取每個元素的id、class屬性
                 */
                String id = element.attributeValue("id");
                String clazz = element.attributeValue("class");
                /**
                 * 通過反射創建對象
                 */
                Class<?> cla = Class.forName(clazz);
                //實例化之後的對象
                Object instance = cla.newInstance();
                /**
                 * 放入map中
                 */
                beansMap.put(id, instance);
            }
            //實例化後,維護對象的依賴關係
            List<Element> propertyList = rootElement.selectNodes("//property");

            //解析property獲取父元素
            for (Element element : propertyList) {
                String name = element.attributeValue("name");
                String ref = element.attributeValue("ref");
                //找到當前需要被處理依賴關係的Bean
                Element parent = element.getParent();
                //調用父元素的反射功能
                String parantId = parent.attributeValue("id");
                Object parantObject = beansMap.get(parantId);
                Method[] methods = parantObject.getClass().getMethods();
                for (Method method : methods) {
                    String methodName = method.getName();
                    //這個方法是要要賦值的方法
                    if(methodName.equalsIgnoreCase("set"+name)){
                        method.invoke(parantObject, beansMap.get(ref));
                    }
                }
                //重新放入容器
                beansMap.put(parantId, parantObject);
            }

        } catch (DocumentException | ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }


    /**
     * 根據id獲取對象
     * @param id
     * @return
     */
    public static Object getBean(String id){
        Object o = beansMap.get(id);
        return  o;
    }
}


修改servlet 與service

在這裏插入圖片描述
在這裏插入圖片描述
TransferServlet

@WebServlet(name = "transferServlet",urlPatterns = "/transferServlet")
public class TransferServlet extends HttpServlet {
    /**
     * http mine,json類型
     */
    public static final String MIME_TYPE_JSON = "application/json";
    /**
     * http mine,json類型指定utf-8編碼
     */
    public final static String MIME_TYPE_JSON_UTF8 = MIME_TYPE_JSON + ";charset=UTF-8";
    //1.實例化TransferService
//    private TransferService transferService = new TransferServcieImpl();
    private TransferService transferService =(TransferService) BeanFactory.getBean("transferService");
    ...
    }


service

public class TransferServcieImpl implements TransferService {

//   private AccountDao accountDao = new AccountDaoImpl();

    private AccountDao accountDao ;

    /**
     * 提供get,set方法
     * @return
     */
    public AccountDao getAccountDao() {
        return accountDao;
    }

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

啓動並把異常代碼註釋

在這裏插入圖片描述

在這裏插入圖片描述
在這裏插入圖片描述

執行成功,但打開異常代碼後,還是不行的,因爲沒有事務控制在Service中

解決第二個大問題
  • 在servcie中的select,update方法每次都會創建一個新的連接,這樣不是一個Connection,
  • 同時每次執行完都把連接關閉了,再次又要重新獲取新連接
  • 事務控制JDBC默認是在dao層Connection對象的,控制在Service層中,解決就是把事務控制放在Service中
    ####### 1.每次都創建新連接
    一個請求到Server後就一個線程在處理,可不可把當前線程與Connection綁定在一起這樣就解決了呢?

新建ConnectionUtils工具類來實現


/**
 * @Description
 * @ClassName ConnectionUtils
 * @Author 劉楠
 * @date 2020.06.24
 */
public class ConnectionUtils {

    /**
     * 單例
     */
    private ConnectionUtils(){


    }
    private static  ConnectionUtils connectionUtils = new ConnectionUtils();
    /**
     * 使用ThreadLocal與Connection綁定
     */
    private static ThreadLocal<Connection> threadLocal = new ThreadLocal<>();


    public static ConnectionUtils getInstance(){

        return connectionUtils;
    }

    public   Connection getCurrentConnection() throws SQLException {
        //先直接從當前線程取
        Connection connection  =threadLocal.get();
        //判斷是否爲空
        if(connection==null){
            //空就從工具類中創建一個連接Connection
            connection = DruidUtils.getInstance().getConnection();
            //Connection與當前線程綁定 
            threadLocal.set(connection);
        }
        //返回連接
        return connection;
    }
}

  1. Connection每次使用完都關閉了?我們不關閉就好了嗎
改造Dao層
public class AccountDaoImpl implements AccountDao {

    @Override
    public Account queryByCardNo(String cardNo) throws SQLException {
        //從連接池獲取連接
//        Connection connection = DruidUtils.getInstance().getConnection();
        /**
         * 獲取當前線程的Connection
         */
        Connection connection = ConnectionUtils.getInstance().getCurrentConnection();

        System.out.println("queryByCardNo connection " + connection.toString());
        String sql = "select name ,cardNo ,money from account where cardNo=?";
        //預編譯SQL
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        //填充參數
        preparedStatement.setString(1, cardNo);
        //執行查詢
        ResultSet resultSet = preparedStatement.executeQuery();
        Account account = null;
        while (resultSet.next()) {
            account = new Account();
            account.setMoney(resultSet.getInt("money"));
            account.setCardNo(resultSet.getString("cardNo"));
            account.setName(resultSet.getString("name"));
        }
        preparedStatement.close();
        //關閉後就不是當前線程了
//        connection.close();
        return account;
    }

   
    @Override
    public int updateAccountByCardno(Account fAccount) throws SQLException {
//        Connection connection = DruidUtils.getInstance().getConnection();
        /**
         * 獲取當前線程的Connection
         */
        Connection connection = ConnectionUtils.getInstance().getCurrentConnection();
        System.out.println("updateAccountByCardno connection " + connection.toString());
        String sql = "update account set money =? where cardNo=? ";
        //預編譯SQL
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        //設置參數
        preparedStatement.setInt(1, fAccount.getMoney());
        preparedStatement.setString(2, fAccount.getCardNo());
        //執行更新
        int update = preparedStatement.executeUpdate();
        preparedStatement.close();
        //關閉後就不是當前線程了
//        connection.close();
        return update;
    }
}
測試下Connection是不是同一個

發一個請求
在這裏插入圖片描述

在這裏插入圖片描述

改造Servcie層-使用手動事務-異常未開啓

package com.liu.spring.service.impl;

import com.liu.spring.dao.AccountDao;
import com.liu.spring.dao.impl.AccountDaoImpl;
import com.liu.spring.entity.Account;
import com.liu.spring.factory.BeanFactory;
import com.liu.spring.service.TransferService;
import com.liu.spring.transaction.TransactionManager;
import com.liu.spring.util.ConnectionUtils;

import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.SQLException;

/**
 * @Description
 * @ClassName TransferServcieImpl
 * @Author 劉楠
 * @date 2020.06.23
 */
public class TransferServcieImpl implements TransferService {

//   private AccountDao accountDao = new AccountDaoImpl();

    private AccountDao accountDao ;




    @Override
    public boolean transfer(String fromAccount, String toAccount, Integer money) throws Exception {
        System.out.println("=====開始轉賬==== ");



        try {
            /**
             * 開啓事務關閉事務的自動提交
             */
            ConnectionUtils.getInstance().getCurrentConnection().setAutoCommit(false);

            Account fAccount = accountDao.queryByCardNo(fromAccount);
            Account tAccount = accountDao.queryByCardNo(toAccount);

            fAccount.setMoney(fAccount.getMoney()-money);
            tAccount.setMoney(tAccount.getMoney()+money);
            accountDao.updateAccountByCardno(fAccount);
            //int i=1/0;
            accountDao.updateAccountByCardno(tAccount);
            /**
             * 正常執行完成 提交事務
             */
            ConnectionUtils.getInstance().getCurrentConnection().commit();
        }catch (Exception e){
            e.printStackTrace();
            /**
             * 有異常回滾事務
             */
            ConnectionUtils.getInstance().getCurrentConnection().rollback();
            //拋出異常 便於上層 捕獲
            throw  new RuntimeException(e);
        }

        return true;
    }

    /**
     * 提供get,set方法
     * @return
     */
    public AccountDao getAccountDao() {
        return accountDao;
    }

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


測試下

在這裏插入圖片描述
在這裏插入圖片描述

打開異常代碼

在這裏插入圖片描述
測試
在這裏插入圖片描述

重點看數據庫
在這裏插入圖片描述
並沒有出現最開始的現象

優化,將事務的開啓,提交,回滾提取到一個公共類中
package com.liu.spring.transaction;

import com.liu.spring.util.ConnectionUtils;

import java.sql.SQLException;

/**
 * @Description 負責手動事務的開啓,提交 ,回滾
 * @ClassName TransactionManager 事務管理器
 * @Author 劉楠
 * @date 2020.06.24
 */
public class TransactionManager {


    private static  TransactionManager INSTANCE = new TransactionManager();

    private TransactionManager(){

    }
    public static TransactionManager getInstance(){
        return INSTANCE;
    }

    /**
     * 開啓事務
     */
    public void beginTransaction() throws SQLException {
        ConnectionUtils.getInstance().getCurrentConnection().setAutoCommit(false);
    }

    /**
     * 提交事務
     */
    public void commitTransaction() throws SQLException {
        ConnectionUtils.getInstance().getCurrentConnection().commit();
    }

    /**
     * 回滾事務
     */
    public void rollbackTransaction() throws SQLException {
        ConnectionUtils.getInstance().getCurrentConnection().rollback();
    }
}



改造Service

在這裏插入圖片描述

在這裏插入圖片描述

package com.liu.spring.service.impl;

import com.liu.spring.dao.AccountDao;
import com.liu.spring.dao.impl.AccountDaoImpl;
import com.liu.spring.entity.Account;
import com.liu.spring.factory.BeanFactory;
import com.liu.spring.service.TransferService;
import com.liu.spring.transaction.TransactionManager;
import com.liu.spring.util.ConnectionUtils;

import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.SQLException;

/**
 * @Description
 * @ClassName TransferServcieImpl
 * @Author 劉楠
 * @date 2020.06.23
 */
public class TransferServcieImpl implements TransferService {

//   private AccountDao accountDao = new AccountDaoImpl();

    private AccountDao accountDao ;




    @Override
    public boolean transfer(String fromAccount, String toAccount, Integer money) throws Exception {
        System.out.println("=====開始轉賬==== ");



        try {
            /**
             * 開啓事務關閉事務的自動提交
             */
//            ConnectionUtils.getInstance().getCurrentConnection().setAutoCommit(false);
            //使用事務管理器-開啓使用
            TransactionManager.getInstance().beginTransaction();
            Account fAccount = accountDao.queryByCardNo(fromAccount);
            Account tAccount = accountDao.queryByCardNo(toAccount);

            fAccount.setMoney(fAccount.getMoney()-money);
            tAccount.setMoney(tAccount.getMoney()+money);
            accountDao.updateAccountByCardno(fAccount);
            int i=1/0;
            accountDao.updateAccountByCardno(tAccount);
            /**
             * 正常執行完成 提交事務
             */
//            ConnectionUtils.getInstance().getCurrentConnection().commit();
            //使用事務管理器-提交事務
            TransactionManager.getInstance().commitTransaction();
        }catch (Exception e){
            e.printStackTrace();
            /**
             * 有異常回滾事務
             */
//            ConnectionUtils.getInstance().getCurrentConnection().rollback();
            //使用事務管理器回滾事務
            TransactionManager.getInstance().rollbackTransaction();
            //拋出異常 便於上層 捕獲
            throw  new RuntimeException(e);
        }

        return true;
    }

    /**
     * 提供get,set方法
     * @return
     */
    public AccountDao getAccountDao() {
        return accountDao;
    }

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

再次測試異常情況

在這裏插入圖片描述

在這裏插入圖片描述

並沒有改變,

測試下正常情況

註釋異常代碼
在這裏插入圖片描述
在這裏插入圖片描述

在這裏插入圖片描述

提交成功
這樣事務就改選到Service中了,改造完成
代碼:https://gitee.com/null_631_9084/myhomework/tree/master/stage01-spring/spring-classroom

還存在一些問題,後期優化
每個方法方法都有一大段try-catch代碼不太友好

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章