MyBatis

发布时间:2024年01月16日

mybatis在webapp中的应用

1.项目的目录结构

image.png

2.使用maven骨架构建项目

  1. src/main/java:src/main/java目录是用于存放项目的Java源代码文件的目录。
  2. src/main/resources:src/main/resources目录是用于存放项目的资源文件的目录,如配置文件、属性文件等。
  3. src/main/webapp:src/main/webapp目录是用于存放Web应用程序的Web资源文件的目录,如HTML、JSP、CSS、JavaScript等。
  4. pom.xml:pom.xml是Maven项目的核心配置文件,定义了项目的依赖、构建配置、插件等信息。

3.添加mybatis,mysql依赖,将项目打包方式设置为war包

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.47</version> <!--注意此处的大版本要和 MySQL 的大版本一致-->
</dependency>

<!--Mybatis-->
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.3.1</version>
</dependency>

4.创建mvc结构,m-模型,v-视图,c-控制层

image.png其中entity为模型层,webapp中的页面为视图层 controller,service为-控制层,dao,util为对模型层的辅助

5.编码

编写xml文件,mybatis核心配置,映射文件

<?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="com.greate.community.dao.UserMapper">
       <update id="updataBalance">
           update account set balance=#{balance}
           where account_number=#{accountNumber}
       </update>
       <select id="selectBalance" resultType="duhong.entity.Account">
           select balance from account where account_number=#{accountNumber};
       </select>


</mapper>

dao层对数据库操作
这里有两个接口,一个是根据用户名查询用户余额,一个是根据更新用户余额

public class AccountDaoImpl implements AccountDao {

    @Override
    public Double select(String account_number) {
        SqlSession sqlSession=SqlSessionUtil.openSession();
        Account account = sqlSession.selectOne("selectBalance", account_number);
        sqlSession.commit();
        sqlSession.close();
        return account.getBalance();
    }

    @Override
    public int updata(String account_number, Double account) {
        SqlSession sqlSession=SqlSessionUtil.openSession();
        Account accountone=new Account();
        accountone.setBalance(account);
        accountone.setAccountNumber(account_number);
        System.out.println(account.toString());
        int sign= sqlSession.update("updataBalance", accountone);
        sqlSession.commit();
        sqlSession.close();
        return sign;
    }
}

service层对业务处理,如果余额不够则不会进行转账,如果余额够则更新余额

package duhong.service;

import duhong.dao.AccountDao;
import duhong.dao.impl.AccountDaoImpl;

public class AccountService {
    AccountDao accountDao=new AccountDaoImpl();

    public void changeAccount(String from_number,String to_number,Double balance){
        //判断余额是否充足
        if(accountDao.select(from_number)<balance){
            System.out.println("余额不足");
        }
        else{
            accountDao.updata(from_number,accountDao.select(from_number)-balance);
            accountDao.updata(to_number,accountDao.select(to_number)+balance);
        }
    }
}

controller层控制视图,当访问localhost:8080/webapp名/account时执行响应的控制层处理

package duhong.controller;

import duhong.service.AccountService;

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;

@WebServlet("/account")
public class AccountController extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String fromAccount = (String) request.getParameter("fromAccount");
        String toAccount = (String) request.getParameter("toAccount");
        double amount = Double.parseDouble(request.getParameter("amount"));
        System.out.println(fromAccount);
        System.out.println(toAccount);
        System.out.println(amount);
        AccountService accountService=new AccountService();
        accountService.changeAccount(fromAccount, toAccount,amount);
    }
}

视图页面

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>转账</title>
    <style>
        body {
            background-color: #f1f1f1;
            font-family: Arial, sans-serif;
        }
        .container {
            max-width: 400px;
            margin: 0 auto;
            padding: 20px;
            background-color: #fff;
            border-radius: 5px;
            box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
        }
        h1 {
            color: #333;
            text-align: center;
        }
        label {
            display: block;
            margin-bottom: 10px;
            font-weight: bold;
        }
        input[type="text"] {
            width: 100%;
            padding: 10px;
            border: 1px solid #ccc;
            border-radius: 3px;
        }
        input[type="submit"] {
            width: 100%;
            padding: 10px;
            font-weight: bold;
            background-color: #4CAF50;
            color: #fff;
            border: none;
            border-radius: 5px;
            cursor: pointer;
        }
    </style>
</head>
<body>
<div class="container">
    <h1>转账</h1>
    <form action="/mvcweb_war_exploded/account" method="post">
        <label for="fromAccount">转出账户:</label>
        <input type="text" id="fromAccount" name="fromAccount" required><br><br>
        <label for="toAccount">转入账户:</label>
        <input type="text" id="toAccount" name="toAccount" required><br><br>
        <label for="amount">金额:</label>
        <input type="text" id="amount" name="amount" required><br><br>
        <input type="submit" value="确认转账">
    </form>
</div>
</body>
</html>

6.配置tomcat服务器,将项目添加到tomcat服务器中,启动服务,测试

初始数据
image.png
image.png

7.结果:

image.png

8.改进:

问题

实验测试成功,但是这种方式并不安全,业务逻辑是如果余额充足便会先后更新两个账号余额,一旦在更新完第一个账户之后报错,那么第二个账户更新并不会执行,就像如下:
在前一个更新执行完成后让程序出错
image.png

测试image.png

这种情况的出现是因为,在操作数据库时,每次都获取一个新的会话对象,在执行两次更新时互不影响。

解决方案

通过TreadLocal来保持同一个会话对象
ThreadLocal 是 Java 中的一个类,它提供了线程局部变量的功能。
每一次http请求都是一个独立的线程,因此可以使用这个类来使保存会话对象,使这个会话对象在整个http请求中都始终都是一个。
修改SqlSessionUtil工具类

import java.io.IOException;
import java.io.InputStream;

public class SqlSessionUtil {
    static SqlSessionFactory sqlSessionFactory;
    static {
        try {
            InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);//加载输入流创建会话工厂
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public  static SqlSession openSession(){
        return sqlSessionFactory.openSession();
    }

}
public class SqlSessionUtil {
    static SqlSessionFactory sqlSessionFactory;
    private static ThreadLocal<SqlSession> local=new ThreadLocal<>();//全局共享同一个静态变量
    static {
        try {
            InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);//加载输入流创建会话工厂
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    //获取线程会话对象
    public  static SqlSession openSession(){
        SqlSession sqlSession=local.get();//先从LocalThread中获取sqlsession
        if(sqlSession==null){//如果不存在,则添加到当前线程中
            sqlSession=sqlSessionFactory.openSession();
            local.set(sqlSession);
        }
        return sqlSession;
    }
    //关闭会话-移除线程与会话对象的关系
    public  static void closeSession(SqlSession sqlSession){
        if(local.get()!=null){
            sqlSession.close();
            local.remove();
        }
        sqlSession.close();
    }


}

修改dao(删除dao层的对话提交,关闭),service层,将会话提交放到业务逻辑完成后

public class AccountService {
    AccountDao accountDao=new AccountDaoImpl();
    SqlSession sqlSession=SqlSessionUtil.openSession();

    public void changeAccount(String from_number,String to_number,Double balance){
        
        //判断余额是否充足
        if(accountDao.select(from_number)<balance){
            System.out.println("余额不足");
        }
        else{
            accountDao.updata(from_number,accountDao.select(from_number)-balance);
            String s=null;
            System.out.println(s.toString());
            accountDao.updata(to_number,accountDao.select(to_number)+balance);
        }
        sqlSession.commit();
        sqlSession.close();
    }
}

重启服务器,测试
image.png
整个线程或者会话过程中,只要业务出现错误,便不会错误的写入数据库。

文章来源:https://blog.csdn.net/weixin_53333436/article/details/135632441
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。