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