<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>
其中entity为模型层,webapp中的页面为视图层 controller,service为-控制层,dao,util为对模型层的辅助
编写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>
初始数据
实验测试成功,但是这种方式并不安全,业务逻辑是如果余额充足便会先后更新两个账号余额,一旦在更新完第一个账户之后报错,那么第二个账户更新并不会执行,就像如下:
在前一个更新执行完成后让程序出错
这种情况的出现是因为,在操作数据库时,每次都获取一个新的会话对象,在执行两次更新时互不影响。
通过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();
}
}
重启服务器,测试
整个线程或者会话过程中,只要业务出现错误,便不会错误的写入数据库。