向表中插入数据两条数据
index.html
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>
第一步: 创建Module , 使用Maven Archetype原型创建WEB应用mybatis-crud , 因为我们这个项⽬不使⽤JSP, 可以删除index.jsp⽂件
web.xml
<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>
第二步: 配置 pom.xml 文件, 因为是web应用所以需要指定打包⽅式为war包 , 并导入相关依赖:mybatis依赖 , mysql驱动依赖 , logback依赖 ,servlet依赖
第三步: 配置Tomcat服务器,这⾥Tomcat使⽤10+版本 , 并部署应⽤到Tomcat
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w
3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://mave
n.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<groupId>com.powernodegroupId>
<artifactId>mybatis-004-webartifactId>
<version>1.0-SNAPSHOTversion>
<packaging>warpackaging>
<name>mybatis-004-web Maven Webappname>
<url>http://localhost:8080/bankurl>
<properties>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
<maven.compiler.source>17maven.compiler.source>
<maven.compiler.target>17maven.compiler.target>
properties>
<dependencies>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.5.10version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>8.0.30version>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.13.2version>
<scope>testscope>
dependency>
<dependency>
<groupId>ch.qos.logbackgroupId>
<artifactId>logback-classicartifactId>
<version>1.2.11version>
dependency>
<dependency>
<groupId>jakarta.servletgroupId>
<artifactId>jakarta.servlet-apiartifactId>
<version>5.0.0version>
<scope>providedscope>
dependency>
dependencies>
<build>
<finalName>mybatis-004-webfinalName>
<pluginManagement>
<plugins>
<plugin>
<artifactId>maven-clean-pluginartifactId>
<version>3.1.0version>
plugin>
<plugin>
<artifactId>maven-resources-pluginartifactId>
<version>3.0.2version>
plugin>
<plugin>
<artifactId>maven-compiler-pluginartifactId>
<version>3.8.0version>
plugin>
<plugin>
<artifactId>maven-surefire-pluginartifactId>
<version>2.22.1version>
plugin>
<plugin>
<artifactId>maven-war-pluginartifactId>
<version>3.2.2version>
plugin>
<plugin>
<artifactId>maven-install-pluginartifactId>
<version>2.5.2version>
plugin>
<plugin>
<artifactId>maven-deploy-pluginartifactId>
<version>2.8.2version>
plugin>
plugins>
pluginManagement>
build>
project>
第四步: 在类的根路径下(resources⽬录)创建配置文件: mybatis-config.xml , XxxMapper.xml , logback.xml
jdbc.properties
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/powernode
jdbc.username=root
jdbc.password=root
**mybatis-config.xml **
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="dev">
<environment id="dev">
<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>
**AccountMapper.xml **
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="account">
<select id="selectByActno" resultType="com.powernode.bank.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.lang.ThreadLocal类中里面有个Map集合 , 这个集合中的key是当前线程 , value是我们想要存储的对象
在执行transfe转账⽅法时开启事务时需要保证service层开启的sqlSession对象和在Dao层执行SQL语句的是同一个sqlSession对象,才能真正控制事务
第一步: 编写使用ThreadLocal的SqlSessionUtil工具类
public class SqlSessionUtil {
private SqlSessionUtil(){}
private static SqlSessionFactory sqlSessionFactory;
static {
try {
sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
// 全局的,服务器级别的,一个服务器当中定义一个即可
private static ThreadLocal<SqlSession> local = new ThreadLocal<>();
/**
* 获取会话对象。
* @return 会话对象
*/
public static SqlSession openSession(){
SqlSession sqlSession = local.get();
if (sqlSession == null) {
sqlSession = sqlSessionFactory.openSession();
// 将sqlSession对象绑定到当前线程上
local.set(sqlSession);
}
return sqlSession;
}
/**
* 关闭SqlSession对象(从当前线程中移除SqlSession对象)
* @param sqlSession
*/
public static void close(SqlSession sqlSession){
if (sqlSession != null) {
sqlSession.close();
// 注意移除SqlSession对象和当前线程的绑定关系
// 因为Tomcat服务器支持线程池,也就是说用过的线程对象t1,可能下一次还会使用这个t1线程
local.remove();
}
}
}
第二步: 在pojo包下定义Account实体类封装账户数据
public class Account {
private Long id;
private String actno;
private Double balance;
@Override
public String toString() {
return "Account{" +
"id=" + id +
", actno='" + actno + '\'' +
", balance=" + balance +
'}';
}
//set和get以及toString方法
public Account(Long id, String actno, Double balance) {
this.id = id;
this.actno = actno;
this.balance = balance;
}
public Account() {
}
}
第三步: 在dao包下编写AccountDao接口,dao的子包impl下编写它的实现类AccountDaoImpl(利用MyBatis执行SQL映射文件中的SQL语句)
public interface AccountDao {
/**
* 根据账号查询账户信息
* @param actno 账号
* @return 账户信息
*/
Account selectByActno(String actno);
/**
* 更新账户信息
* @param act 被更新的账户对象
* @return 1表示更新成功,其他值表示失败。
*/
int updateByActno(Account act);
//增删的方法...
}
public class AccountDaoImpl implements AccountDao {
@Override
public Account selectByActno(String arg0) {
SqlSession sqlSession = SqlSessionUtil.openSession();
Account account = (Account) sqlSession.selectOne("account.selectByActno", actno);
sqlSession.commit();
sqlSession.close();
return account;
}
@Override
public int updateByActno(Account arg0) {
SqlSession sqlSession = SqlSessionUtil.openSession();
int count = sqlSession.update("account.updateByActno", act);
sqlSession.commit();
sqlSession.close();
return count;
}
}
第四步: 将自定义异常放在exception包下
//余额不足异常
public class MoneyNotEnoughException extends Exception{
public MoneyNotEnoughException(){}
public MoneyNotEnoughException(String msg){
super(msg);
}
}
//App异常
public class AppException extends Exception{
public AppException(){}
public AppException(String msg){
super(msg);
}
}
第五步: 在service包下编写AccountService 接口提供处理业务的方法,在service的子包impl下编写它的实现类AccountServiceImpl
public interface AccountService {
/**
* @param fromActno 转出账号
* @param toActno 转入账号
* @param money 转账金额
*/
//子类不能比父类抛出更宽泛的异常,子类抛父类必须也要抛
void transfer(String fromActno, String toActno, double money) throws MoneyNotEnoughException, TransferException;
}
public class AccountServiceImpl implements AccountService {
//业务类可能有多个业务方法,面向接口编程
private AccountDao accountDao = new AccountDaoImpl();
@Override
public void transfer(String fromActno, String toActno, double money) throws MoneyNotEnoughException, TransferException {
// 添加事务控制代码
SqlSession sqlSession = SqlSessionUtil.openSession();
// 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);
int count = accountDao.updateByActno(fromAct);
// 模拟异常
/*String s = null;
s.toString();*/
// 4. 更新转入账户余额(update)
count += accountDao.updateByActno(toAct);
if (count != 2) {
throw new TransferException("转账异常,未知原因");
}
// 提交事务
sqlSession.commit();
// 关闭事务
SqlSessionUtil.close(sqlSession);
}
}
第六步: 在web包下定义AccountServlet类作为Controller,用来接收用户的请求,负责调度其他组件来完成任务,面向接口编程
@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"));
try {
// 调用service的转账方法完成转账(调业务层)
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");
}
}
}
dao实现类中的⽅法代码很固定,通过SqlSession对象调⽤增删改查等⽅法,这个类中的⽅法没有任何业务逻辑,我们可以动态的⽣成这个类
Javassist是由东京⼯业⼤学的数学和计算机科学系的千叶滋所创建的⼀个开源的分析、编辑和创建Java字节码的类库 ,它已加⼊了开放源代码JBoss 应⽤服务器项⽬,通过使⽤Javassist对字节码操作为JBoss实现动态"AOP"框架
第一步:⾸先要引⼊javassist的依赖
<dependency>
<groupId>org.javassistgroupId>
<artifactId>javassistartifactId>
<version>3.29.1-GAversion>
dependency>
解决运⾏时JDK版本过高的问题:加两个参数要不然会有异常, 第一次时点击Modify options->Add VM options
ClassPool类制造类和接口
方法名 | 功能 |
---|---|
public static ClassPool getDefault() | 获取类池对象,这个类池就是用来给我生成class的 |
public CtClass makeClass(“类名”) | 制造类 , 需要告诉javassist制造类的类名是啥 |
public CtClass makeInterface() | 制造接口 , 需要告诉javassist制造接口的全类名是啥 |
CtMethod制造方法
方法名 | 功能 |
---|---|
public static CtMethod make(“方法代码片段”,CtClass) | 制造方法对象 , 第一个参数是方法的代码片段 , 第二个参数指定要把方法添加到哪个制造类中 |
CtClass接口实现类,方法添加到类
方法名 | |
---|---|
public CtClass addMethod(CtMethod) | 将方法添加到类中 , 参数是要添加的方法对象 |
public CtClass toClass(); | 在内存先中生成类手动类加载到Jvm执行 , 然后到制造类的字节码对象 , 也可以在内存中生成类的同时将生成的类加载到JVM当中的得到制造类的字节码对象 , 一步生成 |
public CtClass addInterface(ctInterface) | 将接口添加到类中 , 参数是要添加的接口对象 |
在内存中制造类并创建方法
@Test
public void testGenerateFirstClass() throws Exception{
// 获取类池,这个类池就是用来给我生成class的
ClassPool pool = ClassPool.getDefault();
// 制造类(需要告诉javassist,类名是啥)
CtClass ctClass = pool.makeClass("com.powernode.bank.dao.impl.AccountDaoImpl");
// 制造方法
String methodCode = "public void insert(){System.out.println(123);}";
CtMethod ctMethod = CtMethod.make(methodCode, ctClass);
// 将方法添加到类中
ctClass.addMethod(ctMethod);
// 在内存中生成class
ctClass.toClass();
// 类加载到JVM当中,返回AccountDaoImpl类的字节码
Class<?> clazz = Class.forName("com.powernode.bank.dao.impl.AccountDaoImpl");
// 创建对象
Object obj = clazz.newInstance();
// 获取AccountDaoImpl中的insert方法
Method insertMethod = clazz.getDeclaredMethod("insert");
// 调用方法insert
insertMethod.invoke(obj);
}
创建方法的另一种形式
// 创建⽅法
// 1.返回值类型 2.⽅法名 3.形式参数列表 4.所属类
CtMethod ctMethod = new CtMethod(CtClass.voidType, "execute", new
CtClass[]{}, ctClass);
// 设置⽅法的修饰符列表
ctMethod.setModifiers(Modifier.PUBLIC);
// 设置⽅法体
ctMethod.setBody("{System.out.println(\"hello world\");}");
在内存中动态生成类并实现AccountDao接口的方法(知道接口中的方法)
public interface AccountDao {
void delete();
}
@Test
public void testGenerateImpl() throws Exception{
// 获取类池
ClassPool pool = ClassPool.getDefault();
// 制造类
CtClass ctClass = pool.makeClass("com.powernode.bank.dao.impl.AccountDaoImpl");
// 制造接口
CtClass ctInterface = pool.makeInterface("com.powernode.bank.dao.AccountDao");
// 添加接口到类中,相当于让AccountDaoImpl implements AccountDao
ctClass.addInterface(ctInterface);
// 实现接口中的方法,这里假设我们已经知道接口中的方法
// 制造方法
CtMethod ctMethod = CtMethod.make("public void delete(){System.out.println(\"hello delete!\");}", ctClass);
// 将方法添加到类中
ctClass.addMethod(ctMethod);
// 在内存中生成类,同时将生成的类加载到JVM当中
Class<?> clazz = ctClass.toClass();
//面向接口编程 , 强制类型转换
AccountDao accountDao = (AccountDao)clazz.newInstance();
accountDao.delete();
}
动态生成类并实现接口中的所有方法(不知道接口中的方法),但是方法中的代码片段固定
public interface AccountDao {
void delete();
int insert(String actno);
int update(String actno, Double balance);
String selectByActno(String actno)
}
@Test
public void testGenerateAccountDaoImpl() throws Exception{
// 获取类池
ClassPool pool = ClassPool.getDefault();
// 制造类
CtClass ctClass = pool.makeClass("com.powernode.bank.dao.impl.AccountDaoImpl");
// 制造接口
CtClass ctInterface = pool.makeInterface("com.powernode.bank.dao.AccountDao");
// 实现接口
ctClass.addInterface(ctInterface);
// 要实现接口中所有的方法 , 先获取接口中所有的抽象方法
Method[] methods = AccountDao.class.getDeclaredMethods();
Arrays.stream(methods).forEach(method -> {
// 实现接口中的抽象方法
try {
// public void delete(){}
// public int update(String actno, Double balance){}
StringBuilder methodCode = new StringBuilder();
// 追加修饰符列表
methodCode.append("public ");
// 追加返回值类型
methodCode.append(method.getReturnType().getName());
// 追加空格
methodCode.append(" ");
// 追加方法名
methodCode.append(method.getName());
// 拼接参数 String actno, Double balance
methodCode.append("(");
//parameterType是每一个形参的类型
Class<?>[] parameterTypes = method.getParameterTypes();
for (int i = 0; i < parameterTypes.length; i++) {
Class<?> parameterType = parameterTypes[i];
methodCode.append(parameterType.getName());
methodCode.append(" ");
methodCode.append("arg" + i);
//如果不是最后一个形参就要在它的后面加上一个逗号
if(i != parameterTypes.length - 1){
methodCode.append(",");
}
}
//往方法体中添加代码
methodCode.append("){System.out.println(11111); ");
// 动态的添加return语句
//获取返回值类型的简类名
String returnTypeSimpleName = method.getReturnType().getSimpleName();
//判断返回值类型
if ("void".equals(returnTypeSimpleName)) {
}else if("int".equals(returnTypeSimpleName)){
methodCode.append("return 1;");
}else if("String".equals(returnTypeSimpleName)){
methodCode.append("return \"hello\";");
}
methodCode.append("}");
System.out.println(methodCode);
//制造方法
CtMethod ctMethod = CtMethod.make(methodCode.toString(), ctClass);
ctClass.addMethod(ctMethod);
} catch (Exception e) {
e.printStackTrace();
}
});
// 在内存中生成class,并且加载到JVM当中
Class<?> clazz = ctClass.toClass();
// 创建对象
AccountDao accountDao = (AccountDao) clazz.newInstance();
// 调用动态生产类的方法
accountDao.insert("aaaaa");
accountDao.delete();
accountDao.update("aaaa", 1000.0);
accountDao.selectByActno("aaaa");
}
MyBatis号称轻量级 , 只需要一个jar包就行 , 所以它对javassist进行了二次包装
SqlSession类的方法
方法名 | 功能 |
---|---|
getConfiguration() | 获取MyBaits核心配置文件的configuration标签内的信息 |
getMappedStatement(sqlId) | 通过sql语句id获取mapper配置文件中的sql语句 |
getSqlCommandType() | 获取sql语句的枚举类型(增,删,改,查) |
GenerateDaoProxy工具类(MyBatis框架的开发者已写好),可以动态生成dao接口实现类并创建对象返回, 生成类主要有两部分: 方法本身和方法中的代码片段
遵守了MaBatis的规定创建XxxMapper.xml文件, 就可以使用它提供的GenerateDaoProxy机制,为你动态生成dao接口的实现类
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.powernode.bank.dao.AccountDao">
<select id="selectByActno" resultType="com.powernode.bank.pojo.Account">
select * from t_act where actno = #{actno}
select>
<update id="updateByActno">
update t_act set balance = #{balance} where actno = #{actno}
update>
mapper>
public class GenerateDaoProxy {
/**
* @param daoInterface dao接口
* @return dao接口实现类的实例化对象
*/
public static Object generate(SqlSession sqlSession, Class daoInterface){
// 类池
ClassPool pool = ClassPool.getDefault();
// 制造类(com.powernode.bank.dao.AccountDao --> com.powernode.bank.dao.AccountDaoProxy)
// 实际本质上就是在内存中动态生成一个代理类
CtClass ctClass = pool.makeClass(daoInterface.getName() + "Proxy");
// 制造接口
CtClass ctInterface = pool.makeInterface(daoInterface.getName());
// 实现接口
ctClass.addInterface(ctInterface);
// 实现接口中所有的方法
Method[] methods = daoInterface.getDeclaredMethods();
// method是接口中的抽象方法
Arrays.stream(methods).forEach(method -> {
// 将method这个抽象方法进行实现
try {
// Account selectByActno(String actno)--->public Account selectByActno(String arg0){ 代码; }
StringBuilder methodCode = new StringBuilder();
methodCode.append("public ");
methodCode.append(method.getReturnType().getName());
methodCode.append(" ");
methodCode.append(method.getName());
methodCode.append("(");
// 需要方法的形式参数列表
Class<?>[] parameterTypes = method.getParameterTypes();
for (int i = 0; i < parameterTypes.length; i++) {
Class<?> parameterType = parameterTypes[i];
methodCode.append(parameterType.getName());
methodCode.append(" ");
methodCode.append("arg" + i);
if(i != parameterTypes.length - 1){
methodCode.append(",");
}
}
methodCode.append(")");
methodCode.append("{");
//实现SqlSession sqlSession = SqlSessionUtil.openSession();对于javassist来说必须知道SqlSession是哪个包下的SqlSession类
methodCode.append("org.apache.ibatis.session.SqlSession sqlSession = com.powernode.bank.utils.SqlSessionUtil.openSession();");
// 实现return (接口的方法返回值类型) sqlSession.selectOne("sqlId", arg0),拼接方法时的形式参数用的是arg0
// 想要知道是什么类型的sql语句,方法需要接收一个sqlSession对象
// sql语句的id是框架使用者提供的,具有多变性,mybatis框架的开发者不知道sqlId
// MyBatis规定凡是使用GenerateDaoProxy机制的,sqlId必须是dao接口中方法名,namespace必须是dao接口的全限定名称
String sqlId = daoInterface.getName() + "." + method.getName();
SqlCommandType sqlCommandType = sqlSession.getConfiguration().getMappedStatement(sqlId).getSqlCommandType();
if (sqlCommandType == SqlCommandType.INSERT) {
}
if (sqlCommandType == SqlCommandType.DELETE) {
}
if (sqlCommandType == SqlCommandType.UPDATE)
// 拼接方法时的形式参数用的是arg0,就一个参数
// return sqlSession.update("account.updateByActno", arg0);
methodCode.append("return sqlSession.update(\""+sqlId+"\", arg0);");
}
if (sqlCommandType == SqlCommandType.SELECT) {
// return (Account) sqlSession.selectOne("account.selectByActno", agr0);
String returnType = method.getReturnType().getName();
//sqlSession.selectOne(\""+sqlId+"\", arg0);")方法的返回值类型默认是object类型,需要我们强转
methodCode.append("return ("+returnType+")sqlSession.selectOne(\""+sqlId+"\", arg0);");
}
methodCode.append("}");
CtMethod ctMethod = CtMethod.make(methodCode.toString(), ctClass);
ctClass.addMethod(ctMethod);
} catch (Exception e) {
e.printStackTrace();
}
});
// 创建对象
Object obj = null;
try {
Class<?> clazz = ctClass.toClass();
obj = clazz.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return obj;
}
}
**通过自己封装的GenerateDaoProxy工具类动态生成AccountDao的实现类AccountServiceImpl **
public class AccountServiceImpl implements AccountService {
//面向接口编程,自己手动编写AccountDao的实现类
//private AccountDao accountDao = new AccountDaoImpl();
// 通过自己封装的GenerateDaoProxy工具类动态生成AccountDao的实现类
private AccountDao accountDao = (AccountDao) GenerateDaoProxy.generate(SqlSessionUtil.openSession(), AccountDao.class);
@Override
public void transfer(String fromActno, String toActno, double money) throws MoneyNotEnoughException, TransferException {
// 添加事务控制代码
SqlSession sqlSession = SqlSessionUtil.openSession();
// 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);
int count = accountDao.updateByActno(fromAct);
// 模拟异常
/*String s = null;
s.toString();*/
// 4. 更新转入账户余额(update)
count += accountDao.updateByActno(toAct);
if (count != 2) {
throw new TransferException("转账异常,未知原因");
}
// 提交事务
sqlSession.commit();
// 关闭事务
SqlSessionUtil.close(sqlSession);
}
}
在MyBatis当中提供了相关的机制, 可以动态为我们生成mapper接口的实现类(代理类实现了mapper接口中的所有方法)
SqlSession类的方法
方法名 | 功能 |
---|---|
getMapper() | 在内存中生成dao接口的代理类,然后创建代理类的实例 , 参数是dao接口的字节码对象 |
封装汽车相关信息的pojo类
public class Car {
// 数据库表当中的字段应该和pojo类的属性一一对应,建议使用包装类,这样可以防止null的问题
private Long id;
private String carNum;
private String brand;
private Double guidePrice;
private String produceTime;
private String carType;
//set和get方法以及toString方法
public Car(Long id, String carNum, String brand, Double guidePrice, String produceTime, String carType) {
this.id = id;
this.carNum = carNum;
this.brand = brand;
this.guidePrice = guidePrice;
this.produceTime = produceTime;
this.carType = carType;
}
public Car() {
}
}
mapper接口负责表的CRUD
public interface CarMapper {
/**
* 新增Car
* @param car
* @return
*/
int insert(Car car);
/**
* 根据id删除Car
* @param id
* @return
*/
int deleteById(Long id);
/**
* 修改汽车信息
* @param car
* @return
*/
int update(Car car);
/**
* 根据id查询汽车信息
* @param id
* @return
*/
Car selectById(Long id);
/**
* 获取所有的汽车信息。
* @return
*/
List<Car> selectAll();
}
SQL语句的映射文件XxxMapper.xml
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.powernode.mybatis.mapper.CarMapper">
<insert id="insert">
insert into t_car values(null, #{carNum},#{brand},#{guidePrice},#{produceTime},#{carType})
insert>
<delete id="deleteById">
delete from t_car where id = #{id}
delete>
<update id="update">
update t_car set
car_num=#{carNum},brand=#{brand},guide_price=#{guidePrice},produce_time=#{produceTime},car_type=#{carType}
where id = #{id}
update>
<select id="selectById" resultType="com.powernode.mybatis.pojo.Car">
select
id,
car_num as carNum,
brand,
guide_price as guidePrice,
produce_time as produceTime,
car_type as carType
from t_car where id = #{id}
select>
<select id="selectAll" resultType="com.powernode.mybatis.pojo.Car">
select
id,
car_num as carNum,
brand,
guide_price as guidePrice,
produce_time as produceTime,
car_type as carType
from t_car
select>
mapper>
面向接口编程完成的数据库表的CRUD
public class CarMapperTest {
@Test
public void testInsert(){
SqlSession sqlSession = SqlSessionUtil.openSession();
// 面向接口获取接口的代理对象 , 执行代理对象实现的方法
// mapper实际上就是daoImpl对象,底层不但为CarMapper接口生成了字节码,并且还new实现类对象了
CarMapper mapper = sqlSession.getMapper(CarMapper.class);
Car car = new Car(null, "4444", "奔驰C200", 32.0, "2000-10-10", "新能源");
int count = mapper.insert(car);
System.out.println(count);
sqlSession.commit();
}
@Test
public void testDeleteById(){
SqlSession sqlSession = SqlSessionUtil.openSession();
CarMapper mapper = sqlSession.getMapper(CarMapper.class);
int count = mapper.deleteById(154L);
System.out.println(count);
sqlSession.commit();
}
@Test
public void testUpdate(){
SqlSession sqlSession = SqlSessionUtil.openSession();
CarMapper mapper = sqlSession.getMapper(CarMapper.class);
Car car = new Car(155L, "2222", "凯美瑞222", 3.0, "2000-10-10", "新能源");
mapper.update(car);
sqlSession.commit();
}
@Test
public void testSelectById(){
SqlSession sqlSession = SqlSessionUtil.openSession();
CarMapper mapper = sqlSession.getMapper(CarMapper.class);
Car car = mapper.selectById(155L);
System.out.println(car);
}
@Test
public void testSelectAll(){
SqlSession sqlSession = SqlSessionUtil.openSession();
CarMapper mapper = sqlSession.getMapper(CarMapper.class);
List<Car> cars = mapper.selectAll();
cars.forEach(car -> System.out.println(car));
}
}