MyBatis——在WEB中使用MyBatis(MVC架构模式)

news2024/10/4 14:29:32

一、在 Web 应用中使用 MyBatis

项目目录结构

pojo  

package org.qiu.bank.pojo;

/**
 * 账户类,封装账户数据
 * @author 秋玄
 * @version 1.0
 * @package org.qiu.bank.pojo
 * @date 2022-09-27-20:31
 * @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(Long id, String actno, Double balance) {
        this.id = id;
        this.actno = actno;
        this.balance = balance;
    }

    public Account() {
    }

    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;
    }
}

dao

package org.qiu.bank.dao;

import org.qiu.bank.pojo.Account;

/**
 * @author 秋玄
 * @version 1.0
 * @package org.qiu.bank.dao
 * @date 2022-09-28-10:11
 * @since 1.0
 */
public interface AccountDao {
    Account select(String actno);
    int update(Account account);
}
package org.qiu.bank.dao.impl;

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

/**
 * @author 秋玄
 * @version 1.0
 * @package org.qiu.bank.dao.impl
 * @date 2022-09-28-10:13
 * @since 1.0
 */
public class AccountDaoImpl implements AccountDao {
    @Override
    public Account select(String actno) {
        SqlSession sqlSession = SqlSessionUtil.openSession();
        Account account = sqlSession.selectOne("account.selectById", actno);
        sqlSession.close();
        return account;
    }

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

mybatis 的 SQL 映射文件  

<?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="selectById" resultType="org.qiu.bank.pojo.Account">
        select * from t_act where actno = #{actno};
    </select>

    <update id="updateByActno">
        update t_act set balance = #{balance} where actno = #{actno}
    </update>
</mapper>

service

package org.qiu.bank.service;

import org.qiu.bank.exceptions.MoneyNotEnoughException;
import org.qiu.bank.exceptions.TransferException;

/**
 * @author 秋玄
 * @version 1.0
 * @package org.qiu.bank.service
 * @date 2022-09-28-10:06
 * @since 1.0
 */
public interface AccountService {
    void transfer(String fromActno,String toActno,Double money) throws MoneyNotEnoughException, TransferException;
}
package org.qiu.bank.service.impl;

import org.qiu.bank.dao.AccountDao;
import org.qiu.bank.dao.impl.AccountDaoImpl;
import org.qiu.bank.exceptions.MoneyNotEnoughException;
import org.qiu.bank.exceptions.TransferException;
import org.qiu.bank.pojo.Account;
import org.qiu.bank.service.AccountService;

/**
 * @author 秋玄
 * @version 1.0
 * @package org.qiu.bank.service.impl
 * @date 2022-09-28-10:08
 * @since 1.0
 */
public class AccountServiceImpl implements AccountService {

    AccountDao accountDao = new AccountDaoImpl();

    @Override
    public void transfer(String fromActno, String toActno, Double money) throws MoneyNotEnoughException, TransferException {
        Account fromAct = accountDao.select(fromActno);
        if (fromAct.getBalance() < money) {
            // 余额不足
            throw new MoneyNotEnoughException("对不起,余额不足");
        }
        Account toAct = accountDao.select(toActno);
        fromAct.setBalance(fromAct.getBalance() - money);
        toAct.setBalance(toAct.getBalance() + money);
        int count = accountDao.update(fromAct);
        count += accountDao.update(toAct);
        if (count != 2) {
            throw new TransferException("转账异常");
        }
    }
}

异常处理类  

package org.qiu.bank.exceptions;

/**
 * @author 秋玄
 * @version 1.0
 * @package org.qiu.bank.exceptions
 * @date 2022-09-28-10:22
 * @since 1.0
 */
public class MoneyNotEnoughException extends Exception{
    public MoneyNotEnoughException(){}

    public MoneyNotEnoughException(String message) {
        super(message);
    }
}
package org.qiu.bank.exceptions;

/**
 * @author 秋玄
 * @version 1.0
 * @package org.qiu.bank.exceptions
 * @date 2022-09-28-10:35
 * @since 1.0
 */
public class TransferException extends Exception{
    public TransferException() {
    }

    public TransferException(String message) {
        super(message);
    }
}

controller  

package org.qiu.bank.web;

import org.qiu.bank.exceptions.MoneyNotEnoughException;
import org.qiu.bank.exceptions.TransferException;
import org.qiu.bank.service.AccountService;
import org.qiu.bank.service.impl.AccountServiceImpl;

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;

/**
 * @author 秋玄
 * @version 1.0
 * @package org.qiu.bank.web
 * @date 2022-09-28-09:59
 * @since 1.0
 */

@WebServlet("/transfer")
public class AccountServlet extends HttpServlet {
    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"));
        try {
            // 调用 service 的转账方法完成转账
            accountService.transfer(fromActno,toActno,money);
            // 调用 View 展示结果
            response.sendRedirect(request.getContextPath() + "/success.html");
        } catch (MoneyNotEnoughException e) {
            response.sendRedirect(request.getContextPath() + "/err1.html");
        } catch (TransferException e) {
            response.sendRedirect(request.getContextPath() + "/err2.html");
        }
    }
}

测试:浏览器访问 http://localhost:8080/bank/  

存在的问题:

当用户进行转账时,需要更新两个账号的余额信息,若两次更新操作之间,程序出现了异常,此时对于收款账号的更新操作不会执行,但是转账账号的余额更新操作已经完成,所以会造成数据丢失问题。

解决思路:

首先考虑的肯定是给更新操作添加事务,使得程序对两个账号余额的更新操作同时成功或者同时失败。在 transfer 方法开始执行时开启事务,直到两个更新都成功之后,再提交事务

 

存在的问题:

在给两次更新操作添加事务后发现,上述的问题并未得到解决。原因是 service 和 dao 里使用的 SqlSession 对象不是同一个。

解决思路:

为了保证 service 和 dao 中使用的 SqlSession 对象是同一个,可以将 SqlSession 对象存放到 ThreadLocal 当中

 

改造工具类  

package org.qiu.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.0
 * @since 1.0.0
 */
public class SqlSessionUtil {
    private static SqlSessionFactory sqlSessionFactory;

    private static ThreadLocal<SqlSession> local = new ThreadLocal<>();

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

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

    /**
     * 关闭 SqlSession 对象
     * @param sqlSession
     */
    public static void close(SqlSession sqlSession){
        if (sqlSession != null) {
            sqlSession.close();
            // tomcat 支持线程池,所以关闭 SqlSession 需要将其从当前线程中移除
            local.remove();
        }
    }
}

改造 transfer 方法  

@Override
public void transfer(String fromActno, String toActno, Double money) throws MoneyNotEnoughException, TransferException {

    SqlSession sqlSession = SqlSessionUtil.openSession();

    Account fromAct = accountDao.select(fromActno);
    if (fromAct.getBalance() < money) {
        // 余额不足
        throw new MoneyNotEnoughException("对不起,余额不足");
    }
    Account toAct = accountDao.select(toActno);
    fromAct.setBalance(fromAct.getBalance() - money);
    toAct.setBalance(toAct.getBalance() + money);

    int count = accountDao.update(fromAct);

    // 模拟异常
    String s = null;
    s.toString();

    count += accountDao.update(toAct);
    if (count != 2) {
        throw new TransferException("转账异常");
    }

    sqlSession.commit();
    SqlSessionUtil.close(sqlSession);
}

改造 DaoImpl  

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

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

 

二、MyBatis 对象作用域

SqlSessionFactoryBuilder

这个类可以被实例化、使用和丢弃,一旦创建了 SqlSessionFactory,就不再需要它了。

因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。

可以重用 SqlSessionFactoryBuilder 来创建多个 SqlSessionFactory 实例,但最好还是不要一直保留着它,以保证所有的 XML 解析资源可以被释放给更重要的事情。

SqlSessionFactory

SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。

使用 SqlSessionFactory 的最佳实践是在应用运行期间不要重复创建多次,多次重建 SqlSessionFactory 被视为一种代码“坏习惯”。

因此 SqlSessionFactory 的最佳作用域是应用作用域

有很多方法可以做到,最简单的就是使用单例模式或者静态单例模式。

SqlSession

每个线程都应该有它自己的 SqlSession 实例。

SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域

绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。

也绝不能将 SqlSession 实例的引用放在任何类型的托管作用域中,比如 Servlet 框架中的 HttpSession。

如果现在正在使用一种 Web 框架,考虑将 SqlSession 放在一个和 HTTP 请求相似的作用域中。

换句话说,每次收到 HTTP 请求,就可以打开一个 SqlSession,返回一个响应后,就关闭它。

这个关闭操作很重要,为了确保每次都能执行关闭操作,应该把这个关闭操作放到 finally 块中。

下面的示例就是一个确保 SqlSession 关闭的标准模式:

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

 

一  叶  知  秋,奥  妙  玄  心

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1669855.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

邮件代发邮箱API发送邮件时如何正确使用?

邮件代发API发送邮件如何使用&#xff1f;邮件代发的注意事项&#xff1f; 邮件代发邮箱API作为邮件发送的自动化工具&#xff0c;其正确使用对于提高工作效率、保障信息安全具有重要意义。下面&#xff0c;AokSend就来探讨一下在使用邮件代发邮箱API发送邮件时&#xff0c;应…

[数据集][图像分类]抽烟打电话分类数据集6150张3类别

数据集类型&#xff1a;图像分类用&#xff0c;不可用于目标检测无标注文件 数据集格式&#xff1a;仅仅包含jpg图片&#xff0c;每个类别文件夹下面存放着对应图片 图片数量(jpg文件个数)&#xff1a;6150 分类类别数&#xff1a;3 类别名称:["normal","phone&…

【JVM基础篇】双亲委派机制介绍

文章目录 双亲委派机制简介案例&#xff1a;自底向上查找案例&#xff1a;自顶向下加载案例&#xff1a;C类在当前程序的classpath中 双亲委派机制的作用如何指定加载类的类加载器&#xff1f;面试题如果一个类重复出现在三个类加载器的加载位置&#xff0c;应该由谁来加载&…

css backdrop-filter 实现背景滤镜

官方给出的定义是&#xff1a;backdrop-filter属性允许您将图形效果&#xff08;如模糊或颜色偏移&#xff09;应用于元素后面的区域。因为它适用于元素后面的所有内容&#xff0c;所以要查看元素或其背景的效果&#xff0c;需要透明或部分透明。 大致分为以下10种&#xff1a…

【基于 PyTorch 的 Python 深度学习】5 机器学习基础(2)

前言 文章性质&#xff1a;学习笔记 &#x1f4d6; 学习资料&#xff1a;吴茂贵《 Python 深度学习基于 PyTorch ( 第 2 版 ) 》【ISBN】978-7-111-71880-2 主要内容&#xff1a;根据学习资料撰写的学习笔记&#xff0c;该篇主要介绍了如何选择合适的激活函数、损失函数和优化器…

【系统架构师】-案例篇(三)NoSQL与分布式对象调用

1、NoSQL 一个基于Web 2.0的大型社交网络系统。就该系统的数据架构而言&#xff0c;李工决定采用公司熟悉的数据架构&#xff0c;使用通用的商用关系型数据库&#xff0c;系统内部数据采用中央集中方式存储。该系统投入使用后&#xff0c;初期用户数量少&#xff0c;系统运行平…

第二证券|北交所股票散户可以买吗?门槛多少?

北交所股票散户能够买&#xff0c;不过一般来说&#xff0c;北交所股票出资风险比较大&#xff0c;不适合资金实力不雄厚的散户。 北交所买卖权限注册条件&#xff1a;请求注册权限前20个买卖日的证券账户和资金账户内的财物日均不低于50万元&#xff0c;其间不包括经过融资融…

【资源分享】完胜谷歌翻译的Deepl翻译

::: block-1 “时问桫椤”是一个致力于为本科生到研究生教育阶段提供帮助的不太正式的公众号。我们旨在在大家感到困惑、痛苦或面临困难时伸出援手。通过总结广大研究生的经验&#xff0c;帮助大家尽早适应研究生生活&#xff0c;尽快了解科研的本质。祝一切顺利&#xff01;—…

【算法】动态规划之背包DP与树形DP

前言&#xff1a; 本系列是学习了董晓老师所讲的知识点做的笔记 董晓算法的个人空间-董晓算法个人主页-哔哩哔哩视频 (bilibili.com) 动态规划系列 【算法】动态规划之线性DP问题-CSDN博客 【算法】动态规划之背包DP问题&#xff08;2024.5.11&#xff09;-CSDN博客 背包…

银河麒麟服务器操作系统ssh服务无法启动报exit-code

尝试重装ssh服务后依然无法解决&#xff0c;查看日志journalctl -xe&#xff0c;发现可能是ssh配置文件权限问题导致的。 journalctl -xe AWARNING: UNPROTECTED PRIVATE KEY FILE! AA Permissions 0755 for /etc/ssh/ssh_host_rsa_key are too open. A It is required that …

EPAI手绘建模APP工程图工具栏

(2) 工程图工具栏 ① 模板 1) 打开模板选择页面。 图 306 工程图模板列表 2) 模板选择页面列出了可以使用的工程图模板类型&#xff0c;每个模板规定了工程的大小、方向、规格、标准、常用字段等。也包括一些空白模板&#xff0c;此时可以通过添加表格等注释自定义工程图样式…

苹果15适合用哪些充电宝充电?苹果15可以用充电宝推荐

在如今移动设备如此普遍的时代&#xff0c;充电宝已成为我们日常生活中不可或缺的一部分。对于拥有苹果15的用户来说&#xff0c;选择一款适合的充电宝尤为重要。因为接口以及苹果手机配置上的一个升级&#xff0c;市面上很多普通充电宝已经不能兼容苹果15充电了。苹果15作为一…

5.10.8 Transformer in Transformer

Transformer iN Transformer (TNT)。具体来说&#xff0c;我们将局部补丁&#xff08;例如&#xff0c;1616&#xff09;视为“视觉句子”&#xff0c;并将它们进一步划分为更小的补丁&#xff08;例如&#xff0c;44&#xff09;作为“视觉单词”。每个单词的注意力将与给定视…

ChatGLM 本地部署指南(问题解决)

硬件要求&#xff08;模型推理&#xff09;&#xff1a; INT4 &#xff1a; RTX3090*1&#xff0c;显存24GB&#xff0c;内存32GB&#xff0c;系统盘200GB 如果你没有 GPU 硬件的话&#xff0c;也可以在 CPU 上进行推理&#xff0c;但是推理速度会更慢。 模型微调硬件要求更高。…

高效测评系统方案助力沃尔玛、亚马逊卖家提升产品销量

无论在哪个电商平台&#xff0c;测评确实是最有效的推广方式。测之前一定要选好产品&#xff0c;因为对于大部分卖家而言&#xff0c;不可能你店铺里所有的都是爆款&#xff0c;所以选择的是需要有潜力成为爆款的产品。测评是指通过搭建安全的环境模拟真实的买家购物行为&#…

AH1515-12v转3V20A电源芯片

AH1515-12v转3V20A电源芯片&#xff1a;一款强大的电源解决方案 在当今的电子设备领域&#xff0c;电源管理的重要性不言而喻。一款优秀的电源芯片能够为各种设备提供稳定、高效的电能转换。今天&#xff0c;我们将为大家介绍一款极具特色的电源芯片——AH1515。这款芯片将为您…

FMEA存在的五个主要不足及改进措施——FMEA软件

免费试用FMEA软件-免费版-SunFMEA 在制造业和产品设计领域&#xff0c;失效模式与影响分析&#xff08;Failure Modes and Effects Analysis&#xff0c;简称FMEA&#xff09;被广泛运用&#xff0c;用于预防潜在的设计或制造缺陷。然而&#xff0c;尽管FMEA在风险管理方面发挥…

【51】Camunda8-Zeebe核心引擎-Zeebe Gateway

概述 Zeebe网关是Zeebe集群的一个组件,它可以被视为Zeebe集群的联系点,它允许Zeebe客户端与Zeebe集群内的Zeebe代理进行通信。有关Zeebe broker的更多信息,请访问我们的附加文档。 总而言之,Zeebe broker是Zeebe集群的主要部分,它完成所有繁重的工作,如处理、复制、导出…

加州大学欧文分校英语高级语法专项课程01:Verb Tenses and Passives 学习笔记

Verb Tenses and Passives Course Certificate Course Intro 本文是学习 Verb Tenses and Passives 这门课的学习笔记。 文章目录 Verb Tenses and PassivesWeek 01: Simple, Progressive, and Perfect Verb Tenses ReviewLearning Objectives Present Perfect Tense Review L…

Spring Cloud Alibaba Sentinel 集成与限流实战(6)

项目的源码地址 Spring Cloud Alibaba 工程搭建&#xff08;1&#xff09; Spring Cloud Alibaba 工程搭建连接数据库&#xff08;2&#xff09; Spring Cloud Alibaba 集成 nacos 以及整合 Ribbon 与 Feign 实现负载调用&#xff08;3&#xff09; Spring Cloud Alibaba Ribbo…