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

1.数据库表的设计和准备数据

在这里插入图片描述
在这里插入图片描述

2. 实现步骤

2.1环境搭建

1. idea中新建Maven WEB应用(mybatis-004-web),Create from archetype勾选上,选择如图所示webapp

在这里插入图片描述

2.配置依赖

在pom.xml文件中添加依赖

<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>
    <!--logback依赖-->
    <dependency>
      <groupId>ch.qos.logback</groupId>
      <artifactId>logback-classic</artifactId>
      <version>1.2.11</version>
    </dependency>
    <!--servlet依赖-->
    <dependency>
      <groupId>jakarta.servlet</groupId>
      <artifactId>jakarta.servlet-api</artifactId>
      <version>5.0.0</version>
      <scope>provided</scope>
    </dependency>
  </dependencies>
3.idea配置Tomcat,并部署应用到tomcat

在这里插入图片描述
在这里插入图片描述

4. 修改web.xml配置文件
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee
                      https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
         version="5.0"
         metadata-complete="false">
</web-app>
5.在resources目录下新建、编辑jdbc.properties文件
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/powernode
jdbc.username=root
jdbc.password=root
6.在resources目录下新建、编辑mybatis-config.xml配置文件
<?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="powernodeDB">
        <environment id="powernodeDB">
            <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>
7.在resources目录下新建、编写AccountMapper.xml文件
<?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>
8.在resources目录下新建、编写logback.xml配置文件
<?xml version="1.0" encoding="UTF-8"?>

<configuration debug="false">
    <!-- 控制台输出 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
            <pattern>[%thread] %-5level %logger{50} - %msg%n</pattern>
        </encoder>
    </appender>

    <!--mybatis log configure-->
    <logger name="com.apache.ibatis" level="TRACE"/>
    <logger name="java.sql.Connection" level="DEBUG"/>
    <logger name="java.sql.Statement" level="DEBUG"/>
    <logger name="java.sql.PreparedStatement" level="DEBUG"/>

    <!-- 日志输出级别,logback日志级别包括五个:TRACE < DEBUG < INFO < WARN < ERROR -->
    <root level="DEBUG">
        <appender-ref ref="STDOUT"/>
        <appender-ref ref="FILE"/>
    </root>

</configuration>

2.2 前端页面

在webapp中新建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>

在webapp中新建success.html(同理创建error1.html和error2.html)

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

2.3创建包

  • 普通的java类
    • com.powernode.bank.pojo
  • 工具包
    • com.powernode.bank.utils
  • 表示层
    • com.powernode.bank.web
  • 业务逻辑层
    • com.powernode.bank.service
    • com.powernode.bank.service.impl
  • 持久化层
    • com.powernode.bank.dao
    • com.powernode.bank.dao.impl
  • 异常包
    • com.powernode.bank.exception
1.新建pojo类,放到com.powernode.bank.pojo包中
package com.powernode.pojo;

/**
 * 账户类,封装账户数据
 */
public class Account {
    private Long id;
    private String actno;
    private Double balance;
......
//此处省略构造方法、getting setting toString方法
2.新建SqlSessionUtil工具类,放到com.powernode.bank.utils包中
package com.powernode.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;

/**
 * MyBatis工具类
 */
public class SqlSessionUtil {

    private SqlSessionUtil(){}

    private static SqlSessionFactory sqlSessionFactory;

    static {
        try {
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsReader("mybatis-config.xml"));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // ThreadLocal对象,一个服务器定义一个即可
    // 把SqlSession对象放到ThreadLocal当中,为了保证一个线程对应一个SqlSession
    private static ThreadLocal<SqlSession> local = new ThreadLocal<>();

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

    /**
     * 关闭sqlSession对象
     * @param sqlSession
     */
    public static void close(SqlSession sqlSession){
        if (sqlSession != null) {
            sqlSession.close();
            // 移除sqlSession对象和当前线程的绑定关系
            local.remove();
        }
    }
}

2.4 后端代码实现

1. 编写控制器AccountServlet
package com.powernode.web;

import com.powernode.exceptions.MoneyNotEnoughException;
import com.powernode.exceptions.TransferException;
import com.powernode.service.AccountService;
import com.powernode.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);
            // 走到这,转账成功
            // 调用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");
        }
    }
}
2. 编写业务层AccountService接口以及AccountServiceImpl
package com.powernode.service;

import com.powernode.exceptions.MoneyNotEnoughException;
import com.powernode.exceptions.TransferException;

/**
 * 账户业务类
 */
public interface AccountService {
    /**
     * 账户转账业务
     * @param fromActno 转出账号
     * @param toActno 转入账号
     * @param money 转账金额
     */
    void transfer(String fromActno, String toActno, double money) throws MoneyNotEnoughException, TransferException;
}
package com.powernode.service.impl;

import com.powernode.dao.AccountDao;
import com.powernode.dao.impl.AccountDaoImpl;
import com.powernode.exceptions.MoneyNotEnoughException;
import com.powernode.exceptions.TransferException;
import com.powernode.pojo.Account;
import com.powernode.service.AccountService;

public class AccountServiceImpl implements AccountService {

    private AccountDao accountDao = new AccountDaoImpl();

    @Override
    public void transfer(String fromActno, String toActno, double money) throws MoneyNotEnoughException, TransferException {
        // 1.判断转出账户余额是否充足(select)
        Account fromAct = accountDao.selectByActno(fromActno);
        if (fromAct.getBalance() < money) {
            // 2.余额不足,提示用户
            throw new MoneyNotEnoughException("余额不足");
        }
        // 3. 余额充足,更新转出账户余额(update)
        // 先更新内存中java对象account余额
        Account toAct = accountDao.selectByActno(toActno);
        fromAct.setBalance(fromAct.getBalance() - money);
        toAct.setBalance(toAct.getBalance() + money);

        // 添加事务控制代码
        SqlSession sqlSession = SqlSessionUtil.openSession();

        // 4.更新转入账户余额(update)
        int count = accountDao.updateByActno(fromAct);
        
        /*// 模拟异常
        String s = null;
        s.toString();*/

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

        // 提交事务
        sqlSession.commit();
        // 关闭事务
        SqlSessionUtil.close(sqlSession);
    }
}

3. 编写AccountMapper.xml中SQL语句
<select id="selectByActno" resultType="com.powernode.pojo.Account">
    select * from t_act where actno = #{actno}
</select>

<update id="updateByActno">
    update t_act set balance = #{balance} where actno = #{actno}
</update>
4. 编写DAO层AccountDao接口,以及AccountDaoImpl实现类
package com.powernode.dao;

import com.powernode.pojo.Account;

/**
 * 账户的DAO对象,负责t_act表中数据据的CRUD
 */
public interface AccountDao {
    /**
     * 根据账号查询账户信息
     * @param actno 账号
     * @return 账户信息
     */
    Account selectByActno(String actno);

    /**
     * 更新账户信息
     * @param act 被更新的账户信息
     * @return 1表示成功
     */
    int updateByActno(Account act);
}
package com.powernode.dao.impl;

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

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

    @Override
    public int updateByActno(Account act) {
        SqlSession sqlSession = SqlSessionUtil.openSession();
        int count = sqlSession.update("account.updateByActno", act);
        return count;
    }
}
5. 编写两个异常类
package com.powernode.exceptions;

/**
 * 余额不足异常
 */
public class MoneyNotEnoughException extends Exception{
    public MoneyNotEnoughException() {
    }

    public MoneyNotEnoughException(String message) {
        super(message);
    }
}
package com.powernode.exceptions;

/**
 * 转账异常
 */
public class TransferException extends Exception{
    public TransferException() {
    }

    public TransferException(String message) {
        super(message);
    }
}
6.启动服务器,打开浏览器,输入地址:http://localhost:8080/bank,

测试:
在这里插入图片描述
在这里插入图片描述

3.MyBatis核心对象的作用域

SqlSessionFactoryBuilder 只是用来buildFactory的,利用完即可丢弃
SqlSessionFactory 一个数据库对应一个Factory,最好不要丢弃
SqlSession 一个线程对应一个SqlSession

4. 分析当前程序存在的问题

dao实现类中的方法代码很固定,基本上就是一行代码,通过SqlSession对象调用insert、delete、update、select等方法,这个类中的方法没有任何业务逻辑,既然是这样,这个类能不能动态的生成,以后可以不写这个类吗?

使用javassist动态生成类

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值