MyBatis

mybatis在webapp中的应用

1.项目的目录结构

MyBatis_第1张图片

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包


    mysql
    mysql-connector-java
    5.1.47 




    org.mybatis.spring.boot
    mybatis-spring-boot-starter
    2.3.1

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

MyBatis_第2张图片其中entity为模型层,webapp中的页面为视图层 controller,service为-控制层,dao,util为对模型层的辅助

5.编码

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




       
           update account set balance=#{balance}
           where account_number=#{accountNumber}
       
       



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)

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

视图页面




    
    转账
    


转账







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

初始数据
MyBatis_第3张图片
MyBatis_第4张图片

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 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)

重启服务器,测试
MyBatis_第5张图片
整个线程或者会话过程中,只要业务出现错误,便不会错误的写入数据库。

你可能感兴趣的:(tomcat,java,maven,mybatis,servlet)