Suivez Lao Du pour apprendre MyBatis + Jour 5 + appliquer MyBatis en WEB (en utilisant le mode architecture MVC)

6. Appliquer MyBatis en WEB (en utilisant le mode architecture MVC)

Cible:

  • Maîtriser l'utilisation de mybatis dans les applications Web
  • La portée et le cycle de vie des trois objets mybatis
  • Principe et utilisation de ThreadLocal
  • Consolider le modèle architectural MVC
  • Préparez-vous à apprendre le mécanisme de proxy d'interface de MyBatis

Réalisez la fonction :

  • virement bancaire

Technologie utilisée :

  • HTML + Servlet + MyBatis

Le nom de l'application Web :

  • banque

6.1 Description des exigences

img

6.2 Conception des tables de base de données et préparation des données

img

img

/*
 Navicat Premium Data Transfer

 Source Server         : localhost
 Source Server Type    : MySQL
 Source Server Version : 80016
 Source Host           : localhost:3306
 Source Schema         : powernode

 Target Server Type    : MySQL
 Target Server Version : 80016
 File Encoding         : 65001

 Date: 29/10/2022 17:06:33
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for t_act
-- ----------------------------
DROP TABLE IF EXISTS `t_act`;
CREATE TABLE `t_act`  (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `actno` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,
  `balance` decimal(10, 2) NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of t_act
-- ----------------------------
INSERT INTO `t_act` VALUES (1, 'act001', 50000.00);

SET FOREIGN_KEY_CHECKS = 1;

6.3 Étapes de mise en œuvre

La première étape : la construction de l’environnement

  • Créer une application WEB Maven dans IDEA ( mybatis-004-web )

img

  • IDEA configure Tomcat, où Tomcat utilise la version 10+. Et déployez l'application sur Tomcat.

img

img

  • L'application Web Maven créée par défaut n'a pas de répertoires Java et de ressources, dont deux solutions

    • Le premier : ajoutez-le manuellement.

img

    • La seconde : modifier le fichier de configuration dans maven-archetype-webapp-1.4.jar

img

img

img

  • La version du fichier web.xml est relativement basse, vous pouvez la copier à partir du fichier exemple de tomcat10, puis la modifier
<?xml version="1.0" encoding="UTF-8"?>
<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_4_0.xsd"
         version="4.0"
         metadata-complete="false">
    <!-- <servlet> -->
    <!--     <servlet-name>test</servlet-name> -->
    <!--     <servlet-class>cn.yangbocsu.bank.web.AccountServlet</servlet-class> -->
    <!-- </servlet> -->

    <!-- <servlet-mapping> -->
    <!--     <servlet-name>test</servlet-name> -->
    <!--     <url-pattern>/transfer</url-pattern> -->
    <!-- </servlet-mapping> -->
</web-app>
  • Supprimez le fichier index.jsp, car nous n'utilisons pas JSP pour ce projet. Utilisez simplement du HTML.

  • Assurez-vous que la méthode d'empaquetage dans le fichier pom.xml est un package war.

  • Introduire les dépendances associées

    • La version du compilateur est passée à 17
    • Les dépendances introduites incluent : mybatis, pilote mysql, junit, logback, servlet.
<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/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>cn.yangbocsu</groupId>
  <artifactId>mybatis_004_web</artifactId>
  <packaging>war</packaging>
  <version>1.0</version>

  <!-- <name>mybatis_004_web maven</name> -->
  <name>mybatis_004_web</name>
  <url>http://maven.apache.org</url>
  <dependencies>
    <!--mybatis依赖-->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.5.10</version>
    </dependency>
    <!--mysql驱动依赖-->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>8.0.30</version>
    </dependency>

    <!--junit依赖-->
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.13.2</version>
      <scope>test</scope>
    </dependency>

    <!--logback依赖-->
    <dependency>
      <groupId>ch.qos.logback</groupId>
      <artifactId>logback-classic</artifactId>
      <version>1.2.11</version>
    </dependency>

    <!--servlet依赖-->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>4.0.1</version>
    </dependency>

  </dependencies>

  <build>
    <finalName>mybatis_004_web</finalName>
    <pluginManagement>
      <plugins>
        <plugin>
          <artifactId>maven-clean-plugin</artifactId>
          <version>3.1.0</version>
        </plugin>
        <plugin>
          <artifactId>maven-resources-plugin</artifactId>
          <version>3.0.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-compiler-plugin</artifactId>
          <version>3.8.0</version>
        </plugin>
        <plugin>
          <artifactId>maven-surefire-plugin</artifactId>
          <version>2.22.1</version>
        </plugin>
        <plugin>
          <artifactId>maven-war-plugin</artifactId>
          <version>3.2.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-install-plugin</artifactId>
          <version>2.5.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-deploy-plugin</artifactId>
          <version>2.8.2</version>
        </plugin>
      </plugins>
    </pluginManagement>
  </build>
</project>

  • Introduisez les fichiers de configuration pertinents et placez-les dans le répertoire des ressources (le tout dans le chemin racine de la classe)

    • monbatis-config.xml
    • AccountMapper.xml
    • connexion.xml
    • jdbc.propriétés
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/powernode
jdbc.username=root
jdbc.password=root
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>

    <properties resource="jdbc.properties"/>

    <environments default="dev">
        <environment id="dev">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <!--一定要注意这里的路径哦!!!-->
        <mapper resource="AccountMapper.xml"/>
    </mappers>
</configuration>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="account">

</mapper>

Étape 2 : page d'accueil index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>银行转账</title>
</head>
<body>
    <form action="/bank/transfer" method="post">
        转出账号:<input type="text" name="fromActno"><br>
        转入账号:<input type="text" name="toActno"><br>
        转账金额:<input type="text" name="money"><br>
        <input type="submit" value="转账">
    </form>
</body>
</html>

Étape 3 : Créer un package pojo, un package de services, un package dao, un package Web, un package utils

  • com.powernode.bank.pojo
  • com.powernode.bank.service
  • com.powernode.bank.service.impl
  • com.powernode.bank.dao
  • com.powernode.bank.dao.impl
  • com.powernode.bank.web.controller
  • com.powernode.bank.exception
  • com.powernode.bank.utils : copiez la classe d'outils SqlSessionUtil précédemment écrite dans ce package.

Étape 4 : Définir la classe pojo : Compte

package com.powernode.bank.pojo;

/**
 * 银行账户类
 * @author 老杜
 * @version 1.0
 * @since 1.0
 */
public class Account {
    
    
    private Long id;
    private String actno;
    private Double balance;

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

    public Account() {
    
    
    }

    public Account(Long id, String actno, Double balance) {
    
    
        this.id = id;
        this.actno = actno;
        this.balance = balance;
    }

    public Long getId() {
    
    
        return id;
    }

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

    public String getActno() {
    
    
        return actno;
    }

    public void setActno(String actno) {
    
    
        this.actno = actno;
    }

    public Double getBalance() {
    
    
        return balance;
    }

    public void setBalance(Double balance) {
    
    
        this.balance = balance;
    }
}

Étape 5 : Écrire l'interface AccountDao et la classe d'implémentation AccountDaoImpl

Au moins plusieurs méthodes doivent être fournies dans la table d'analyse pour réaliser le transfert :

  • Besoin de vérifier si le solde est suffisant avant le transfert : selectByActno
  • Pour mettre à jour le compte lors d'un transfert d'argent : mettre à jour
package com.powernode.bank.dao;

import com.powernode.bank.pojo.Account;

/**
 * 账户数据访问对象
 * @author 老杜
 * @version 1.0
 * @since 1.0
 */
public interface AccountDao {
    
    

    /**
     * 根据账号获取账户信息
     * @param actno 账号
     * @return 账户信息
     */
    Account selectByActno(String actno);

    /**
     * 更新账户信息
     * @param act 账户信息
     * @return 1表示更新成功,其他值表示失败
     */
    int update(Account act);
}
package com.powernode.bank.dao.impl;

import com.powernode.bank.dao.AccountDao;
import com.powernode.bank.pojo.Account;
import com.powernode.bank.utils.SqlSessionUtil;
import org.apache.ibatis.session.SqlSession;

public class AccountDaoImpl implements AccountDao {
    
    
    @Override
    public Account selectByActno(String actno) {
    
    
        SqlSession sqlSession = SqlSessionUtil.openSession();
        Account act = (Account)sqlSession.selectOne("selectByActno", actno);
        sqlSession.close();
        return act;
    }

    @Override
    public int update(Account act) {
    
    
        SqlSession sqlSession = SqlSessionUtil.openSession();
        int count = sqlSession.update("update", act);
        sqlSession.commit();
        sqlSession.close();
        return count;
    }
}

Étape 6 : Le code mybatis est écrit dans AccountDaoImpl et le fichier de mappage SQL doit être écrit

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="account">
    <select id="selectByActno" resultType="com.powernode.bank.pojo.Account">
        select * from t_act where actno = #{actno}
    </select>
    <update id="update">
        update t_act set balance = #{balance} where actno = #{actno}
    </update>
</mapper>

Étape 7 : Écrivez l’interface AccountService et AccountServiceImpl

package com.powernode.bank.exception;

/**
 * 余额不足异常
 * @author 老杜
 * @version 1.0
 * @since 1.0
 */
public class MoneyNotEnoughException extends Exception{
    
    
    public MoneyNotEnoughException(){
    
    }
    public MoneyNotEnoughException(String msg){
    
     super(msg); }
}
package com.powernode.bank.exception;

/**
 * 应用异常
 * @author 老杜
 * @version 1.0
 * @since 1.0
 */
public class AppException extends Exception{
    
    
    public AppException(){
    
    }
    public AppException(String msg){
    
     super(msg); }
}
package com.powernode.bank.service;

import com.powernode.bank.exception.AppException;
import com.powernode.bank.exception.MoneyNotEnoughException;

/**
 * 账户业务类。
 * @author 老杜
 * @version 1.0
 * @since 1.0
 */
public interface AccountService {
    
    

    /**
     * 银行账户转正
     * @param fromActno 转出账户
     * @param toActno 转入账户
     * @param money 转账金额
     * @throws MoneyNotEnoughException 余额不足异常
     * @throws AppException App发生异常
     */
    void transfer(String fromActno, String toActno, double money) throws MoneyNotEnoughException, AppException;
}
package com.powernode.bank.service.impl;

import com.powernode.bank.dao.AccountDao;
import com.powernode.bank.dao.impl.AccountDaoImpl;
import com.powernode.bank.exception.AppException;
import com.powernode.bank.exception.MoneyNotEnoughException;
import com.powernode.bank.pojo.Account;
import com.powernode.bank.service.AccountService;

public class AccountServiceImpl implements AccountService {
    
    

    private AccountDao accountDao = new AccountDaoImpl();

    @Override
    public void transfer(String fromActno, String toActno, double money) throws MoneyNotEnoughException, AppException {
    
    
        // 查询转出账户的余额
        Account fromAct = accountDao.selectByActno(fromActno);
        if (fromAct.getBalance() < money) {
    
    
            throw new MoneyNotEnoughException("对不起,您的余额不足。");
        }
        try {
    
    
            // 程序如果执行到这里说明余额充足
            // 修改账户余额
            Account toAct = accountDao.selectByActno(toActno);
            fromAct.setBalance(fromAct.getBalance() - money);
            toAct.setBalance(toAct.getBalance() + money);
            // 更新数据库
            accountDao.update(fromAct);
            accountDao.update(toAct);
        } catch (Exception e) {
    
    
            throw new AppException("转账失败,未知原因!");
        }
    }
}

Étape 8 : Écrire AccountController

package com.powernode.bank.web.controller;

import com.powernode.bank.exception.AppException;
import com.powernode.bank.exception.MoneyNotEnoughException;
import com.powernode.bank.service.AccountService;
import com.powernode.bank.service.impl.AccountServiceImpl;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * 账户控制器
 * @author 老杜
 * @version 1.0
 * @since 1.0
 */
@WebServlet("/transfer")
public class AccountServlet extends HttpServlet {
    
    

    //为了让这个对象在其他方法中也可以用。声明为实例变量。
    private AccountService accountService = new AccountServiceImpl();

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    
    
        // 获取表单数据
        String fromActno = request.getParameter("fromActno");
        String toActno = request.getParameter("toActno");
        double money = Double.parseDouble(request.getParameter("money"));

        // 调用 service 的转账方法完成转账 (调业务层)
        // 层和层之间要用接口去衔接,目的是降低层与层之间的耦合度
        try {
    
    
            accountService.transfer(fromActno,toActno,money);
            //程序能够走到这里,表示转账-定成功了。
            //调用 View 完成展示结果。
            response.sendRedirect(request.getContextPath() + "/success.html");
        } catch (MoneyNotEnoughException e) {
    
    
            response.sendRedirect(request.getContextPath() + "/error1.html");
        } catch (TransferException e) {
    
    
            response.sendRedirect(request.getContextPath() + "/error2.html");
        }catch (Exception e) {
    
    
            response.sendRedirect(request.getContextPath() + "/error2.html");
        }
        // 调用视图层 完成展示结果
    }
}

Démarrez le serveur, ouvrez le navigateur, saisissez l'adresse : http://localhost:8080/bank, testez :

img

img

img

6.4 Portée de l'objet MyBatis et problèmes de transaction

La portée des objets principaux de MyBatis

SqlSessionFactoryBuilder

Cette classe peut être instanciée, utilisée et supprimée. Une fois la SqlSessionFactory créée, elle n'est plus nécessaire. Ainsi, la meilleure portée pour une instance de SqlSessionFactoryBuilder est la portée de la méthode (c'est-à-dire les variables de méthode locales). Vous pouvez réutiliser SqlSessionFactoryBuilder pour créer plusieurs instances de SqlSessionFactory, mais il est préférable de ne pas le conserver en permanence pour garantir que toutes les ressources d'analyse XML peuvent être libérées pour des tâches plus importantes.

SqlSessionFactory

Une fois créé, le SqlSessionFactory doit exister pendant toute la durée de l'application, il n'y a aucune raison de le supprimer ou de créer une autre instance. La meilleure pratique pour utiliser SqlSessionFactory est de ne pas créer plusieurs fois pendant l'exécution de l'application. Reconstruire SqlSessionFactory plusieurs fois est considéré comme une "mauvaise habitude" de code. La meilleure portée pour SqlSessionFactory est donc la portée de l’application. Il existe de nombreuses façons de le faire, la plus simple est d'utiliser le modèle singleton ou le modèle singleton statique.

Session SQL

Chaque thread doit avoir sa propre instance de SqlSession. Les instances de SqlSession ne sont pas thread-safe et ne peuvent donc pas être partagées. Sa meilleure portée est donc la portée de la demande ou de la méthode. Vous ne devez jamais placer une référence à une instance de SqlSession dans un champ statique d'une classe, pas même une variable d'instance d'une classe. Vous ne devez également jamais placer de références aux instances SqlSession dans tout type de portée gérée, telle que HttpSession dans le framework Servlet. Si vous utilisez aujourd'hui un framework Web, envisagez de placer SqlSession dans une portée similaire aux requêtes HTTP. En d’autres termes, chaque fois qu’une requête HTTP est reçue, une SqlSession peut être ouverte et après le retour d’une réponse, elle peut être fermée. Cette opération de fermeture est très importante. Afin de garantir que l'opération de fermeture puisse être effectuée à chaque fois, vous devez mettre cette opération de fermeture dans le bloc final. L'exemple suivant est un modèle standard permettant de garantir que la SqlSession est fermée :

try (SqlSession session = sqlSessionFactory.openSession()) {
    
    
  // 你的应用逻辑代码
}

problème commercial

Dans l'activité de transfert précédente, deux comptes ont été mis à jour. Nous devons nous assurer qu'ils réussissent ou échouent en même temps. À ce stade, nous devons utiliser le mécanisme de transaction. Lorsque la méthode de transfert commence à s'exécuter, la transaction est lancée jusqu'à ce que les deux mises à jour réussissent, puis pour valider la transaction, nous essayons de modifier la méthode de transfert comme suit :

package com.powernode.bank.service.impl;

import com.powernode.bank.dao.AccountDao;
import com.powernode.bank.dao.impl.AccountDaoImpl;
import com.powernode.bank.exception.AppException;
import com.powernode.bank.exception.MoneyNotEnoughException;
import com.powernode.bank.pojo.Account;
import com.powernode.bank.service.AccountService;
import com.powernode.bank.utils.SqlSessionUtil;
import org.apache.ibatis.session.SqlSession;

public class AccountServiceImpl implements AccountService {
    
    

    private AccountDao accountDao = new AccountDaoImpl();

    @Override
    public void transfer(String fromActno, String toActno, double money) throws MoneyNotEnoughException, AppException {
    
    
        // 查询转出账户的余额
        Account fromAct = accountDao.selectByActno(fromActno);
        if (fromAct.getBalance() < money) {
    
    
            throw new MoneyNotEnoughException("对不起,您的余额不足。");
        }
        try {
    
    
            // 程序如果执行到这里说明余额充足
            // 修改账户余额
            Account toAct = accountDao.selectByActno(toActno);
            fromAct.setBalance(fromAct.getBalance() - money);
            toAct.setBalance(toAct.getBalance() + money);
            // 更新数据库(添加事务)
            SqlSession sqlSession = SqlSessionUtil.openSession();
            accountDao.update(fromAct);
            // 模拟异常
            String s = null;
            s.toString();
            
            accountDao.update(toAct);
            sqlSession.commit();
            sqlSession.close();
        } catch (Exception e) {
    
    
            throw new AppException("转账失败,未知原因!");
        }
    }
}

Faites attention aux données actuelles dans la table de la base de données avant d'exécuter :

img

exécuter le programme :

img

img

Affichez à nouveau les données dans la table de base de données :

img

Abasourdi maintenant ! ! ! Il y a eu un problème avec la transaction, le transfert a échoué et il manquait toujours 10 000 fonds. Quelle est la raison? La raison principale est que les objets SqlSession utilisés dans service et dao ne sont pas les mêmes.

ce qu'il faut faire? Afin de garantir que l'objet SqlSession utilisé dans service et dao est le même, vous pouvez stocker l'objet SqlSession dans ThreadLocal. Modifiez la classe d'outils SqlSessionUtil :

package com.powernode.bank.utils;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

/**
 * MyBatis工具类
 *
 * @author 老杜
 * @version 1.0
 * @since 1.0
 */
public class SqlSessionUtil {
    
    
    private static SqlSessionFactory sqlSessionFactory;

    /**
     * 类加载时初始化sqlSessionFactory对象
     */
    static {
    
    
        try {
    
    
            SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
            sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"));
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
    }

    private static ThreadLocal<SqlSession> local = new ThreadLocal<>();   // 新增加的

    /**
     * 每调用一次openSession()可获取一个新的会话,该会话支持自动提交。
     *
     * @return 新的会话对象
     */
    public static SqlSession openSession() {
    
    
        SqlSession sqlSession = local.get();
        if (sqlSession == null) {
    
    
            sqlSession = sqlSessionFactory.openSession();
            local.set(sqlSession);
        }
        return sqlSession;
    }

    /**
     * 关闭SqlSession对象
     * @param sqlSession
     */
    public static void close(SqlSession sqlSession){
    
    
        if (sqlSession != null) {
    
    
            sqlSession.close();
        }
        local.remove();
    }
}

Modifier la méthode dans dao : tous les codes de validation et de fermeture de toutes les méthodes de AccountDaoImpl sont supprimés.

package com.powernode.bank.dao.impl;

import com.powernode.bank.dao.AccountDao;
import com.powernode.bank.pojo.Account;
import com.powernode.bank.utils.SqlSessionUtil;
import org.apache.ibatis.session.SqlSession;

public class AccountDaoImpl implements AccountDao {
    
    
    @Override
    public Account selectByActno(String actno) {
    
    
        SqlSession sqlSession = SqlSessionUtil.openSession();
        Account act = (Account)sqlSession.selectOne("account.selectByActno", actno);
        return act;
    }

    @Override
    public int update(Account act) {
    
    
        SqlSession sqlSession = SqlSessionUtil.openSession();
        int count = sqlSession.update("account.update", act);
        return count;
    }
}

Modifier la méthode en service :

package com.powernode.bank.service.impl;

import com.powernode.bank.dao.AccountDao;
import com.powernode.bank.dao.impl.AccountDaoImpl;
import com.powernode.bank.exception.AppException;
import com.powernode.bank.exception.MoneyNotEnoughException;
import com.powernode.bank.pojo.Account;
import com.powernode.bank.service.AccountService;
import com.powernode.bank.utils.SqlSessionUtil;
import org.apache.ibatis.session.SqlSession;

public class AccountServiceImpl implements AccountService {
    
    

    private AccountDao accountDao = new AccountDaoImpl();

    @Override
    public void transfer(String fromActno, String toActno, double money) throws MoneyNotEnoughException, AppException {
    
    
        // 查询转出账户的余额
        Account fromAct = accountDao.selectByActno(fromActno);
        if (fromAct.getBalance() < money) {
    
    
            throw new MoneyNotEnoughException("对不起,您的余额不足。");
        }
        try {
    
    
            // 程序如果执行到这里说明余额充足
            // 修改账户余额
            Account toAct = accountDao.selectByActno(toActno);
            fromAct.setBalance(fromAct.getBalance() - money);
            toAct.setBalance(toAct.getBalance() + money);
            // 更新数据库(添加事务)
            SqlSession sqlSession = SqlSessionUtil.openSession();
            accountDao.update(fromAct);
            // 模拟异常
            String s = null;
            s.toString();
            accountDao.update(toAct);
            sqlSession.commit();
            SqlSessionUtil.close(sqlSession);  // 只修改了这一行代码。
        } catch (Exception e) {
    
    
            throw new AppException("转账失败,未知原因!");
        }
    }
}

Données dans la table de base de données actuelle :

img

Exécutez à nouveau le programme :

img

img

En regardant les tables de la base de données : pas de problème.

img

Retestez que le transfert est réussi :

img

img

img

Si le solde est insuffisant :

img

img

Le solde du compte est toujours normal :

img

6.5 Analyser les problèmes existants dans le programme actuel

Jetons un coup d'œil au code de DaoImpl

package com.powernode.bank.dao.impl;

import com.powernode.bank.dao.AccountDao;
import com.powernode.bank.pojo.Account;
import com.powernode.bank.utils.SqlSessionUtil;
import org.apache.ibatis.session.SqlSession;

public class AccountDaoImpl implements AccountDao {
    
    
    @Override
    public Account selectByActno(String actno) {
    
    
        SqlSession sqlSession = SqlSessionUtil.openSession();
        Account act = (Account)sqlSession.selectOne("account.selectByActno", actno);
        return act;
    }

    @Override
    public int update(Account act) {
    
    
        SqlSession sqlSession = SqlSessionUtil.openSession();
        int count = sqlSession.update("account.update", act);
        return count;
    }
}

Il n'est pas difficile de constater que le code de la méthode dans cette classe d'implémentation dao est très fixe, essentiellement une ligne de code, appelant les méthodes insert, delete, update, select et autres via l'objet SqlSession, les méthodes de cette classe n'en ont pas Logique métier, puisque c'est le cas, Pouvons-nous générer dynamiquement cette classe et ne pouvons-nous pas écrire cette classe à l'avenir ? Réponse : Oui.

Je suppose que tu aimes

Origine blog.csdn.net/qq_38689263/article/details/127618955
conseillé
Classement