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

news2024/11/18 8:17:31

2023.10.30

        本章将在web应用中使用MyBatis,实现一个银行转账的功能。整体架构采用MVC架构模式。


数据库表的初始化

环境的初始化配置

web.xml文件的配置:

<?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>bank.web.AccountServlet</servlet-class>-->
<!--    </servlet>-->
<!--    <servlet-mapping>-->
<!--        <servlet-name>test</servlet-name>-->
<!--        <url-pattern>/transfer</url-pattern>-->
<!--    </servlet-mapping>-->

</web-app>

ps:metadata-complete如果填true,会导致注解使用不了,路径就必须在xml文件里配置了,所以这里填false,就可以使用注解了。

pom.xml文件的配置:

<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>jay</groupId>
  <artifactId>mybatis-004-web</artifactId>
  <packaging>war</packaging>
  <version>1.0</version>
  <name>mybatis-004-web Maven Webapp</name>
  <url>http://maven.apache.org</url>
  <dependencies>
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.5.13</version>
    </dependency>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>8.0.30</version>
    </dependency>
    <dependency>
      <groupId>ch.qos.logback</groupId>
      <artifactId>logback-classic</artifactId>
      <version>1.2.11</version>
    </dependency>
    <dependency>
      <groupId>org.apache.tomcat</groupId>
      <artifactId>tomcat-servlet-api</artifactId>
      <version>10.0.12</version>
    </dependency>
  </dependencies>
  <build>
    <finalName>mybatis-004-web</finalName>
  </build>
</project>

 引入相关配置文件,放到resources目录下:

AccountMapper.xml文件:配置相关sql语句。

logback-test.xml文件

mybatis-config.xml文件:

 前端页面的准备

index.html : 启动服务器会自动跳转到这个页面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>银行账户转账</title>
</head>
<body>
<!--/bank是应用的根,部署web应用到tomcat的时候一定要注意这个名字-->
<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>

error1.html:当因“余额不足”导致转账失败,会跳到这个页面上。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>转账报告</title>
</head>
<body>
<h1>余额不足!!!!</h1>
</body>
</html>

error.html:当未知原因导致转账失败,会跳到这个页面上。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>转账报告</title>
</head>
<body>
<h1>转账失败,未知原因!!!</h1>
</body>
</html>

success.html:转账成功跳到这个页面上

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>转账报告</title>
</head>
<body>
<h1>转账成功!!!</h1>
</body>
</html>

在bank包下创建pojo包、service包、dao包、web包、utils、exceptions包:

dao为数据访问层,也称为持久层,位于三层中的最下层,用于对数据进行处理。

service为业务逻辑层,用于对业务逻辑的封装。

web为表示层,用于显示数据和接收用户输入的数据,为用户提供一种交互式操作的界面。

pojo为实体类,用于对数据的封装。

utils为工具类。

exceptions为异常处理。

定义pojo类:Account

package bank.pojo;

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

编写AccountDao接口,以及AccountDaoImpl实现类

package bank.dao;

import bank.pojo.Account;

public interface AccountDao {

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

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


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

import javax.swing.plaf.IconUIResource;

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

        return act;
    }

    public int update(Account act) {
        SqlSession sqlSession = SqlSessionUtil.openSession();
        int count = sqlSession.update("update",act);

        return count;
    }
}

编写AccountService接口以及AccountServiceImpl实现类

package bank.service;

import bank.exceptions.MoneyNotEnoughException;
import bank.exceptions.TransferException;

public interface AccountService {
    void transfer(String fromActno,String toActno,double money) throws MoneyNotEnoughException, TransferException;
}
package bank.service.impl;

import bank.dao.AccountDao;
import bank.dao.impl.AccountDaoImpl;
import bank.exceptions.MoneyNotEnoughException;
import bank.exceptions.TransferException;
import bank.utils.SqlSessionUtil;
import org.apache.ibatis.session.SqlSession;
import bank.pojo.Account;
import bank.service.AccountService;



public class AccountServiceImpl implements AccountService {
    private AccountDao accountDao = new AccountDaoImpl();
    public void transfer(String fromActno, String toActno, double money) throws MoneyNotEnoughException, TransferException {
        //添加事务控制代码
        SqlSession sqlSession = SqlSessionUtil.openSession();


        //查询账户余额,判断转出账户的余额是否充足
        Account fromAct = accountDao.selectByActno(fromActno);
        if(fromAct.getBalance() < money){
            throw new MoneyNotEnoughException("对不起,余额不足!");
        }

        //执行到这说明余额充足了
        Account toAct = accountDao.selectByActno(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("转账异常,未知原因");
        }

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

 异常类

package bank.exceptions;

public class MoneyNotEnoughException extends Exception{
    public MoneyNotEnoughException(){}
    public MoneyNotEnoughException(String msg){
        super(msg);
    }
}
package bank.exceptions;

public class TransferException extends Exception{
    public TransferException(){}
    public TransferException(String msg){

    }
}

工具类SqlSessionUtil:

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

import java.io.IOException;

public class SqlSessionUtil {
    private SqlSessionUtil(){};

    private static SqlSessionFactory sqlSessionFactory;

    //静态代码块:类加载时运行
    static {
        try {
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    //全局的,服务器级别的,一个服务器当中定义一个即可。
    private static ThreadLocal<SqlSession> local = new ThreadLocal<SqlSession>();

    //获取会话对象
    public static SqlSession openSession(){
        SqlSession sqlSession = local.get();
        if(sqlSession == null){
            sqlSession = sqlSessionFactory.openSession();
            //将sqlSession对象绑定到当前线程上
            local.set(sqlSession);
        }
        return sqlSession;
    }

    //从当前线程中移除SqlSession对象
    public static void close(SqlSession sqlSession){
        if(sqlSession != null){
            sqlSession.close();
            //注意移除SqlSession对象和当前线程的绑定关系
            //因为Tomcat服务器支持线程池,也就是说用过的线程对象,可能下一次还会再用。
            local.remove();
        }
    }

}

表示层AccountServlet类:

package bank.web;

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

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;

@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);
            response.sendRedirect(request.getContextPath() + "/success.html");
        } catch (MoneyNotEnoughException e) {
            response.sendRedirect(request.getContextPath() + "/error1.html");
        } catch (TransferException e) {
            response.sendRedirect(request.getContextPath() + "/error2.html");
        }
    }
}

实验结果:

启动服务器,出现页面:

填入金额: 

跳转页面:

数据库中的数据:

事务问题 

        在AccountServiceImpl实现类中,收尾分别添加了事务语句,以确保事务安全。为了保证service和dao中使用的SqlSession对象是同一个,可以将SqlSession对象存放到ThreadLocal当中。 所以在工具类SqlSessionUtil中,使用了一个全局的ThreadLocal,它的使用场合主要是为了解决多线程中因为数据并发产生不一致的问题。  获取会话对象的代码为:

    public static SqlSession openSession(){
        SqlSession sqlSession = local.get();
        if(sqlSession == null){
            sqlSession = sqlSessionFactory.openSession();
            //将sqlSession对象绑定到当前线程上
            local.set(sqlSession);
        }
        return sqlSession;
    }

即一个线程对应一个sqlSession ,这样子做 dao 和 service中的sqlsession就都是同一个的了。

这里注意 sqlsession的close方法中需要对local进行移除:

    public static void close(SqlSession sqlSession){
        if(sqlSession != null){
            sqlSession.close();
            //注意移除SqlSession对象和当前线程的绑定关系
            //因为Tomcat服务器支持线程池,也就是说用过的线程对象,可能下一次还会再用。
            local.remove();
        }
    }

MyBatis的三个对象作用域

SqlSessionFactoryBuilder:

        这个类可以被实例化、使用和丢弃,一旦创建了 SqlSessionFactory,就不再需要它了。 因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。 你可以重用 SqlSessionFactoryBuilder 来创建多个 SqlSessionFactory 实例,但最好还是不要一直保留着它,以保证所有的 XML 解析资源可以被释放给更重要的事情。

SqlSessionFactory:

        SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。 使用 SqlSessionFactory 的最佳实践是在应用运行期间不要重复创建多次,多次重建 SqlSessionFactory 被视为一种代码“坏习惯”。因此 SqlSessionFactory 的最佳作用域是在WEB应用使用MyBatis应用作用域。 有很多方法可以做到,最简单的就是使用单例模式或者静态单例模式。

SqlSession:

        每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。 绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。 也绝不能将 SqlSession 实例的引用放在任何类型的托管作用域中,比如 Servlet 框架中的 HttpSession。 如果你现在正在使用一种 Web 框架,考虑将 SqlSession 放在一个和 HTTP 请求相似的作用域中。 换句话说,每次收到 HTTP 请求,就可以打开一个 SqlSession,返回一个响应后,就关闭它。 这个关闭操作很重要,为了确保每次都能执行关闭操作,你应该把这个关闭操作放到 finally 块中。

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

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

相关文章

Linux多线程服务端编程:使用muduo C++网络库 学习笔记 第六章 muduo网络库简介

2010年3月作者写了一篇《学之者生&#xff0c;用之者死——ACE历史与简评》&#xff08;http://blog.csdn.net/Solstice/archive/2010/03/10/5364096.aspx&#xff0c;ACE是&#xff08;Adaptive Communication Environment&#xff09;是一个C编写的开源框架&#xff0c;用于开…

如何使用IP归属地查询API加强网络安全

引言 在当今数字化时代&#xff0c;网络安全对于个人和组织来说至关重要。恶意网络活动的威胁不断增加&#xff0c;因此采取有效的措施来加强网络安全至关重要。其中之一是利用IP归属地查询API。这个工具可以为您的网络安全策略提供宝贵的信息&#xff0c;帮助您更好地保护自己…

微软bing大声朗读文档或网页卡顿老是中断,用离线的huihui就很流畅但没那么自然

默认的xiaoxiao_online好听&#xff0c;但卡顿&#xff0c;朗读功能确实受到了网络状态的影响。 大概率是网络问题。

概念解析 | 动态非线性系统 VS 非线性系统 VS 线性系统

KaTeX parse error: \newcommand{\blue} attempting to redefine \blue; use \renewcommand 注1:本文系“概念解析”系列之一,致力于简洁清晰地解释、辨析复杂而专业的概念。本次辨析的概念是:动态非线性系统 VS 非线性系统 VS 线性系统。 概念解析 | 动态非线性系统 VS 非线性…

国产射频功率放大器技术指标有哪些内容

射频功率放大器是一种广泛应用于通信、雷达、卫星等领域的高频电子设备&#xff0c;其作用是将微弱的电磁信号放大到足以传输或检测的强度。射频功率放大器技术指标是衡量其性能优劣的重要标准&#xff0c;其主要内容包括以下几个方面。 频率范围&#xff1a;射频功率放大器需要…

接雨水 DP 双指针

力扣 接雨水 public class 接雨水 {public static int trap(int[] height){int res 0;int len height.length;int[] maxLeft new int[len];//存 i 左边最高的高度int[] maxRight new int[len];//存 i 右边最高的高度maxLeft[0] 0;maxRight[len - 1] 0; // DPfor (int i…

leetcode每日一题复盘(10.30~11.5)

leetcode 93 复原ip地址 这一题和上一次的回文串那题差不多&#xff0c;都是给一串数据&#xff0c;在数据中挑出符合要求的放进结果集中 整个题目可以分成三部分&#xff0c;用来判断是否符合条件的函数&#xff0c;回溯函数&#xff0c;主函数 首先是判断函数&#xff0c;这…

ubuntu安装python以及conda

切换国内镜像源 备份下 sudo cp /etc/apt/sources.list /etc/apt/sources_init.list 更换源 sudo vi /etc/apt/sources.list 开头加 deb http://mirrors.aliyun.com/ubuntu/ trusty main restricted universe multiverse deb http://mirrors.aliyun.com/ubuntu/ trusty-sec…

项目解读_v2

1. 项目介绍 如果使用task2-1作为示例时&#xff0c; 运行process.py的过程中需要确认 process调用的是函数 preprocess_ast_wav2vec(wav, fr) 1.1 任务简介 首个开源的儿科呼吸音数据集&#xff0c; 通过邀请11位医师标注&#xff1b; 数字听诊器的采样频率和量化分辨率分…

单目标应用:进化场优化算法(Evolutionary Field Optimization,EFO)求解微电网优化MATLAB

一、微网系统运行优化模型 微电网优化模型介绍&#xff1a; 微电网多目标优化调度模型简介_IT猿手的博客-CSDN博客 二、进化场优化算法EFO 进化场优化算法&#xff08;Evolutionary Field Optimization&#xff0c;EFO&#xff09;由Baris Baykant Alagoz等人于2022年提出&…

HarmonyOS鸿蒙原生应用开发设计- 音效库

HarmonyOS设计文档中&#xff0c;为大家提供了独特的音效库&#xff0c;开发者可以根据需要直接引用。 音效库可以分为拟物音效、系统音效、特殊效果音。具体分为提示音、UI交互音、来电铃声、闹铃、拟物音效、乐器、科幻等。 整体分类 开发者直接使用官方提供的音效库内容&a…

S32DS踩坑日记二、调试和SPI

J-Link调试需要注意以下配置&#xff1a; 一&#xff1a;从下图中Debug Configurations进入配置J-Link 二&#xff1a; 三&#xff1a; 关于初始化&#xff1a; 还有一个宏&#xff1a; FEATURE_HAS_HIGH_SPEED_RUN_MODE需要改成0&#xff0c;因为高温型号不支持1。 如果不…

使用NVIDIA GPU FFmpeg转码 YUV to H264(成功)

0. 官方教程 NVIDIA官方教程&#xff1a;链接&#xff0c;本篇内容主要参考2.2 Software Setup。 1. 安装显卡驱动 确保nvidia-smi能够正常使用&#xff1a; 2. 安装CUDA toolkit 注意要与显卡驱动版本对应&#xff0c;验证toolkit是否正确安装&#xff1a; 3. 安装ffnvco…

好用的API调试工具推荐:Apipost

随着数字化转型的加速&#xff0c;API&#xff08;应用程序接口&#xff09;已经成为企业间沟通和数据交换的关键。而在API开发和管理过程中&#xff0c;API文档、调试、Mock和测试的协作显得尤为重要。Apipost正是这样一款一体化协作平台&#xff0c;旨在解决这些问题&#xf…

【LeetCode 算法专题突破】二叉树的深度优先遍历(⭐)

文章目录 前言1. 二叉树的前序遍历题目描述代码 2. 二叉树的中序遍历题目描述代码 3. 二叉树的后序遍历题目描述代码 4. 前序遍历的非递归实现代码与思路 5. 中序遍历的非递归实现代码与思路 6. 后序遍历的非递归实现代码与思路 总结 前言 接下来我要开始攻克二叉树这一个大难…

Mac 配置环境变量

Mac 配置环境变量 修改配置文件 vim ~/.bash_profile i进入编辑模式. Esc&#xff1a;wq 保存文件 esc:q 退出 如&#xff1a;jdk环境变量配置 JAVA_HOME/Library/Java/JavaVirtualMachines/jdk1.8.0_361.jdk/Contents/Home CLASSPATH J A V A H O M E / l i b / t o o l…

SAP SPAD新建打印纸张

SAP SPAD新建打印纸张 1.事务代码SPAD 2.完全管理&#xff0d;设备类型&#xff0d;页格式-显示(创建格式页) 3.按标准A4纸张为模板参考创建。同一个纸张纵向/横向各创建1次(创建格式页) 4.完全管理&#xff0d;设备类型&#xff0d;格式类型-显示(创建格式类型&#xff0…

PID控制示例

PID控制简单示例 import numpy as np import matplotlib.pyplot as plt import copy# 定义曲线函数 y sin(x) def target_curve(x):return np.sin(x)class PID:def __init__(self, kp, ki, kd):self.kp kpself.ki kiself.kd kdself.ep 0.0self.ei 0.0self.ed 0.0self.d…

个人服务器怎么搭建?个人服务器搭建方法

​  个人服务器是指一台由个人拥有和管理的服务器&#xff0c;用于存储和提供个人网站、应用程序或其他在线服务。搭建个人服务器可以让我们更好地掌控自己的数据和网络资源。下面介绍一种常见的个人服务器搭建方法。 第一步&#xff1a;选择合适的硬件 我们需要选择一台适合…

【自用】英语一新题型每年归类

图片出自马天艺老师视频课。