MyBatis学习笔记(四)
MyBatis在Web当中的应用(模拟转账)
新建工程
首先新建一个JAVA web工程。在这里我们选择Jakarta EE进行快速创建。
Template模板我们选择Web application
选择Tomcat服务器
Build System选择Maven进行构建
导入依赖
在pom.xml中我们导入mybatis,mysql依赖,如果有需要可以导入日志框架
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.11</version>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.0.31</version>
</dependency>
配置环境
我们把根路径设置成/bank
构建HTML页面
首先我们删除最初的jsp文件,我们采用静态页面的方式
首页代码和视图:
我们采用post提交方式 提交到transfer这个路径下
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>银行账户转账</title>
</head>
<body>
<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>
错误页面代码和视图:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>转账失败</title>
</head>
<body>
<h1>转账失败</h1>
</body>
</html>
成功页面代码和视图
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>转账成功</title>
</head>
<body>
<h1>转账成功</h1>
</body>
</html>
数据库建立
建立一个t_act表,字段如下:
默认插入两条记录
一个act001 余额50000
一个act002 余额0
配置xml文件并写sql语句
mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value=""/>
<property name="username" value=""/>
<property name="password" value=""/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="AccountMapper.xml"/> <!-- -->
</mappers>
</configuration>
AccountMapper.xml
因为我们要在转账之前确认余额还够不够所以我们需要一个select方法以及转账的update方法
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="hsk123456">
<select id="selectByActno" resultType="com.example.mybatisweb.pojo.Account">
select * from t_act where actno = #{actno}
</select>
<update id="updateByActno">
update t_act set balance = #{balance} where actno = #{actno}
</update>
</mapper>
编写Java代码
编写Dao接口
public interface AccountDao {
Account selectByActno(String id);
int updateByActno(Account account);
}
编写Dao接口实现类
这边的Sqlsession还是由之前编写的 SqlSessionUtils类来获取
public class AccountDaoImpl implements AccountDao {
@Override
public Account selectByActno(String id) {
SqlSession session = SqlSessionUtils.getSession();
Account account = session.selectOne("hsk123456.selectByActno",id);
session.close();
return account;
}
@Override
public int updateByActno(Account account) {
SqlSession session = SqlSessionUtils.getSession();
int count = session.update("hsk123456.updateByActno",account);
session.commit();
session.close();
return count;
}
}
编写POJO类
public class Account {
private Long id;
private String actno;
private Double balance;
public Account() {
}
@Override
public String toString() {
return "Account{" +
"id=" + id +
", actno='" + actno + '\'' +
", balance=" + balance +
'}';
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getActno() {
return actno;
}
public void setActno(String actno) {
this.actno = actno;
}
public Double getBalance() {
return balance;
}
public void setBalance(Double balance) {
this.balance = balance;
}
}
编写Service类
首先我们定义一个转账方法 参数1:转出方 参数2:收入方 参数3:金额数量。
我们采用先把数据从数据库拿到之后加载到类中也就是内存中
所以我们定义了两个account类对象
下一步:做判断如果余额不足则抛出异常;
接着:开始转账操作
最后把数据更新到数据库中,因为操作了两个记录,所以最后的i必定是2,,如果不是2则抛出异常
public class AccountService {
private AccountDao accountDao = new AccountDaoImpl();
public void transfer(String from, String to, double money) throws Exception {
Account account = accountDao.selectByActno(from);
Account account1 = accountDao.selectByActno(to);
if (account.getBalance() < money)
{
throw new Exception("no enough");
}
account.setBalance(account.getBalance() - money);
account1.setBalance(account1.getBalance() + money);
int i = accountDao.updateByActno(account);
i+= accountDao.updateByActno(account1);
if (i!=2)
{
throw new RuntimeException("未知");
}
}
}
编写Servlet类
使用注解进行开发
首先调用req.getParameter()方法获取前端发送过来的数据
之后进行转账,如果成功就是没有抛出异常,那么久跳转到成功的页面,反之,则跳转到失败的页面。
@WebServlet("/transfer")
public class AccountServlet extends HttpServlet {
private AccountService accountService = new AccountServiceimpl();
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String fromActno = req.getParameter("fromActno");
String toActno = req.getParameter("toActno");
double money = Double.parseDouble(req.getParameter("money"));
//执行service
try {
accountService.transfer(fromActno,toActno,money);
resp.sendRedirect("/bank/success.html");
} catch (Exception e) {
resp.sendRedirect("/bank/error.html");
}
}
}
至此我们的最初版就结束了。
但是现在存在一个问题 我们这边没有启用事务,我们可不可以在service类中,再获取一个sqlsession对象呢,然后再提交呢?
答案是不行的。
因为每次获取的sqlsession对象都是一个新的对象,和其他的sqlsession的任何操作不相互关联。
那么我们可以使用Threadlocal类,让sqlsession对象在同一个线程中进行共享
2.0版本如下(通过ThreadLocal实现对象的共享):
更改我们的SqlsessionUtil类
public class SqlSessionUtils {
private static SqlSessionFactory sqlSessionFactory;
static{
try {
sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static ThreadLocal<SqlSession> local = new ThreadLocal<>();
//从ThreadLocal加入对象并获取一个实例
public static SqlSession getSession()
{
SqlSession session = local.get();
if (session == null){
session = sqlSessionFactory.openSession();
}
return session;
}
//从ThreadLocal取出对象并关闭
public static void close(SqlSession session){
if (session!=null)
{
session.close();
local.remove();
}
}
}
更改Service类
public class AccountServiceimpl implements AccountService {
private SqlSession session = SqlSessionUtils.getSession();
private AccountDao accountDao = new AccountDaoImpl();
@Override
public void transfer(String from, String to, double money) throws Exception {
Account account = accountDao.selectByActno(from);
Account account1 = accountDao.selectByActno(to);
if (account.getBalance() < money)
{
throw new Exception("no enough");
}
account.setBalance(account.getBalance() - money);
account1.setBalance(account1.getBalance() + money);
int i = accountDao.updateByActno(account);
i+= accountDao.updateByActno(account1);
if (i!=2)
{
throw new RuntimeException("未知");
}
//提交
session.commit();
//调用工具类的关闭方法
SqlSessionUtils.close(session);
}
}
3.0版本(动态生成Dao接口实现类)
至此我们解决了事务的问题,当然还有一个点可以被解决,就是在Mybatis当中可以帮我们动态生成Dao接口的实现类执行CRUD,那么这个就取代了Dao接口的实现类。
在这之前我们需要更改我们的mapper文件
我们要把命名空间改为Dao类的全类名,因为在底层它是通过找这个路径来进行接口的实现
<mapper namespace="com.example.mybatisweb.dao.AccountDao">
我们的CRUD方法的id名需要与Dao类中的方法名一致,同样他在底层会通过这个生成方法名,并把返回值类型生成
<select id="selectByActno" resultType="com.example.mybatisweb.pojo.Account">
select * from t_act where actno = #{actno}
</select>
接下来我们再次改写我们的Service类
调用session.getMapper()方法,参数需要接口的class文件。
private AccountDao accountDao = session.getMapper(AccountDao.class);
这样我们的3.0版本就出炉了