一、概述
1、 什么是Javassist?;
是一个开源的分析、编辑和创建Java字节码的类库。已加入了开放源代码JBoss 应用服务器项目,通过使用Javassist对字节码操作为JBoss实现动态"AOP"框架。
说的明白一点就是:为我们生成类的。
2、 我们为什么要使用它呢?我们自己手动创建一个类不是很方便吗?;
- 适用于代码量不是很多的类,并且和业务没有什么关系
- 例如对数据库操作的接口实现类:【此处不提交和关闭连接是因为在工具类做了特殊处理】
package com.powernode.bank.dao.impl;
import com.powernode.bank.dao.AccountDao;
import com.powernode.bank.pojo.Account;
import com.powernode.bank.utils.SqlSessionUtil;
import org.apache.ibatis.session.SqlSession;
/**
* @author Bonbons
* @version 1.0
*/
public class AccountDaoImpl implements AccountDao {
@Override
public Account selectByActno(String actno) {
//开启会话,根据actno查询账户
SqlSession sqlSession = SqlSessionUtil.openSession();
Account account = (Account) sqlSession.selectOne("com.powernode.bank.dao.AccountDao.selectByActno",actno);
// 关闭会话,返回账户信息
// sqlSession.close();
return account;
}
@Override
public int updateActno(Account act) {
//开启会话
SqlSession sqlSession = SqlSessionUtil.openSession();
//修改余额
int count = sqlSession.update("com.powernode.bank.dao.AccountDao.updateActno", act);
// //提交事务,关闭会话
// sqlSession.commit();
// sqlSession.close();
//返回影响数据库表中记录的条数
return count;
}
}
这部分代码简单,而且没有多少内容,所以我们就像不去写这个类,而是通过Javassist去实现直接在内存中生成类。
二、使用Javassist
- 先附上这部分模块的文件的层次结构,再依次展开论述
1、 我们需要在模块中导入javassist的依赖【mybatis、junit依赖也需要】;
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.29.1-GA</version>
</dependency>
2、 我们这部分的目的就是>>为我们数据库操作接口生成类;
(1)写一下 dao 层的接口 AccoutDao 【增删改查四个操作】
package com.powernode.bank.dao;
/**
* @author Bonbons
* @version 1.0
*/
public interface AccountDao {
void delete();
int update(String actno, double balance);
int insert(String actno);
String selectByActno(String actno);
}
(2)接下来就可以写我们的 javassist 类了 JavassistTest【由浅入深该类中有三个 @Test】
- 先给出完整代码:
package com.powernode.javassist;
import com.powernode.bank.dao.AccountDao;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import org.junit.Test;
import java.lang.reflect.Method;
import java.util.Arrays;
/**
* @author Bonbons
* @version 1.0
*/
public class JavassistTest {
@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");
// 将接口添加到类中[类似声明类实现哪个接口]
ctClass.addInterface(ctInterface);
// 实现接口中的方法
// (1)制造方法
CtMethod ctMethod = CtMethod.make("public void delete(){System.out.println(\"hello delete!\");}", ctClass);
// (2)将方法添加到类中
ctClass.addMethod(ctMethod);
// 在内存中生成类,并将类加载到JVM中
Class<?> clazz = ctClass.toClass();
AccountDao accountDao = (AccountDao)clazz.newInstance();
accountDao.delete();
}
@Test
public void testGenerateFirstClass() throws Exception{
// 获取类池,通过类池的实例创建Class
ClassPool pool = ClassPool.getDefault();
// 根据指定的类名创建类
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();
// 至此,javassist的任务已经完成了 >> 在内存中生成类
// 为了使用这个类和方法。我们添加一些JDK中的方法
// 类加载到JVM中,返回类的字节码
Class<?> clazz = Class.forName("com.powernode.bank.dao.impl.AccountDaoImpl");
// 创建类的实例
Object obj = clazz.newInstance();
//获取它的insert方法
Method insertMethod = clazz.getDeclaredMethod("insert");
//调用这个方法
insertMethod.invoke(obj);
}
// 动态的实现接口中的方法
@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");
// 实现接口[只有类实现了接口,最后才能强转Wie接口的类型]
ctClass.addInterface(ctInterface);
// 获取接口中所有的方法
Method[] methods = AccountDao.class.getDeclaredMethods();
// 流式遍历
Arrays.stream(methods).forEach(method -> {
// method 是接口中的抽象方法,我们现在要实现这些方法
try {
// 创建我们要拼接的方法
StringBuilder methodCode = new StringBuilder();
// 追加修饰符列表
methodCode.append("public ");
// 追加返回值类型
methodCode.append(method.getReturnType().getName());
// 追加空格
methodCode.append(" ");
// 追加方法名
methodCode.append(method.getName());
// 追加左括号
methodCode.append("(");
// 拼接参数列表:(1)先获得所有返回值类型
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(1111);");
// 判断当前方法的返回值类型
String returnTypeSimpleName = method.getReturnType().getSimpleName();
// 分情讨论 [我们接口这块只有 void、String、double]
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("bala");
accountDao.delete();
accountDao.update("bala", 1000.0);
accountDao.selectByActno("bala");
}
}
3、 第一个@Test的内容;
@Test
public void testGenerateFirstClass() throws Exception{
// 获取类池,通过类池的实例创建Class
ClassPool pool = ClassPool.getDefault();
// 根据指定的类名创建类
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();
// 至此,javassist的任务已经完成了 >> 在内存中生成类
// 为了使用这个类和方法。我们添加一些JDK中的方法
// 类加载到JVM中,返回类的字节码
Class<?> clazz = Class.forName("com.powernode.bank.dao.impl.AccountDaoImpl");
// 创建类的实例
Object obj = clazz.newInstance();
//获取它的insert方法
Method insertMethod = clazz.getDeclaredMethod("insert");
//调用这个方法
insertMethod.invoke(obj);
}
- 简单的引入 javassist,只是通过类池去创建一个类和方法,然后将方法添加到类中
- 创建类的对象,最后调用这个方法
4、 第二个@Test的内容;
@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");
// 将接口添加到类中[类似声明类实现哪个接口]
ctClass.addInterface(ctInterface);
// 实现接口中的方法
// (1)制造方法
CtMethod ctMethod = CtMethod.make("public void delete(){System.out.println(\"hello delete!\");}", ctClass);
// (2)将方法添加到类中
ctClass.addMethod(ctMethod);
// 在内存中生成类,并将类加载到JVM中
Class<?> clazz = ctClass.toClass();
AccountDao accountDao = (AccountDao)clazz.newInstance();
accountDao.delete();
}
这是一个最简单的 javassist 应用案例,它的使用流程与注释步骤基本相同
获取类池 >> 制造类 >> 制造接口 >> 用类去实现接口 >> 创建方法 >> 将方法添加到类中
- 以上这部分为 javassist 的工作内容,剩余在内存中生成类和将类加载到JVM中以及创建类的实例、调用类的方法都是JDK中提供的一些方法
- 对于实现接口的那条语句类似于声明该类实现这个接口,但我们要具体实现这个接口中的方法
- 所以我们创建一个接口方法的实现方法,最后再添加到类中【此处是随便写了一个方法】
5、 第三个@Test的内容【动态的获取接口中的方法,但是随便实现了方法的内容】;
// 动态的实现接口中的方法
@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");
// 实现接口[只有类实现了接口,最后才能强转Wie接口的类型]
ctClass.addInterface(ctInterface);
// 获取接口中所有的方法
Method[] methods = AccountDao.class.getDeclaredMethods();
// 流式遍历
Arrays.stream(methods).forEach(method -> {
// method 是接口中的抽象方法,我们现在要实现这些方法
try {
// 创建我们要拼接的方法
StringBuilder methodCode = new StringBuilder();
// 追加修饰符列表
methodCode.append("public ");
// 追加返回值类型
methodCode.append(method.getReturnType().getName());
// 追加空格
methodCode.append(" ");
// 追加方法名
methodCode.append(method.getName());
// 追加左括号
methodCode.append("(");
// 拼接参数列表:(1)先获得所有返回值类型
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(1111);");
// 判断当前方法的返回值类型
String returnTypeSimpleName = method.getReturnType().getSimpleName();
// 分情讨论 [我们接口这块只有 void、String、double]
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("bala");
accountDao.delete();
accountDao.update("bala", 1000.0);
accountDao.selectByActno("bala");
}
- 在创建方法之前,先获取了接口中的所有方法,然后通过 Arrays 将数组转为流,再利用 forEach 遍历
- 在内部创建一个 StringBuilder 的实例用于拼接方法串,拼接流程如下:
修饰符 >> 返回值类型 >> 方法名 >> 参数列表 【此处省略了空格、逗号、参数名】
- 因为参数可能有多个,所以我们通过method.getParameterTypes()获取所有的参数类型,通过遍历将参数类型和参数拼接进去
- 然后就是方法体,此处我们简化了,就拼接了一条输出语句
- 最后就是返回值类型,通过method.getReturnType().getSimpleName()获取返回值类型,然后分情况讨论,我们返回的内容也简化了,因为此处只是想说明是一个什么样的原理
- 然后根据我们拼接的字符串创建方法,再将方法添加到类中,最后就是创建类…【与上面剩余步骤大同小异】
6、 如果运行的时候出现以下的报错:;
这是因为高版本的 JDK 使用了base类,我们要通过以下操作来解决这个问题
7、 利用Javassist实现在dao层的AccountDao接口;
(1)接口如下:【包含参数和返回值类型】
package com.powernode.bank.dao;
import com.powernode.bank.pojo.Account;
/**
* 账户的Dao对象,负责对 t_act 表进行增删改查
* Dao 中的方法与业务逻辑不存在联系
* @author Bonbons
* @version 1.0
*/
public interface AccountDao {
/**
* 根据账号查询账户信息
* @param actno 账户id
* @return 返回账户的信息
*/
Account selectByActno(String actno);
/**
* 更新账户的信息
* @param act 被更新的账户
* @return 返回更新结果(成功/失败)
*/
int updateActno(Account act);
}
(2)我们通过 GenerateDaoProxy 的 generate 方法实现在内存中根据接口创建实现类
package com.powernode.bank.utils;
import org.apache.ibatis.javassist.ClassPool;
import org.apache.ibatis.javassist.CtClass;
import org.apache.ibatis.javassist.CtMethod;
import org.apache.ibatis.session.SqlSession;
import java.lang.reflect.Method;
import java.util.Arrays;
/**
* 可以动态的生成DAO的实现类
* @author Bonbons
* @version 1.0
*/
public class GenerateDaoProxy {
/**
* 生成dao接口的实现类,返回实现类的对象
* @param daoInterface 接口
* @return 返回的对象
*/
public static Object generate(SqlSession sqlSession, Class daoInterface){
// 获取类池
ClassPool pool = ClassPool.getDefault();
// 制造类[类名采用接口名 + Proxy(只要类名和接口名不冲突就行)]
CtClass ctClass = pool.makeClass(daoInterface.getName() + "Proxy");
// 制造接口
CtClass ctInterface = pool.makeInterface(daoInterface.getName());
// 实现接口
ctClass.addInterface(ctInterface);
// 获得接口中的所有方法
Method[] methods = daoInterface.getDeclaredMethods();
// 采用流式遍历
Arrays.stream(methods).forEach(method -> {
try {
// 拼接方法
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);
methodCode.append(" ");
methodCode.append("arg");
methodCode.append(i);
if(i != parameterTypes.length - 1){
methodCode.append(",");
}
}
methodCode.append("){");
// 内部代码
// 拼接创建会话的语句,但注意要使用全限定类名
methodCode.append("org.apache.ibatis.session.SqlSession sqlSession = com.powernode.bank.utils.SqlSessionUtil.openSession()");
// mybatis 规定了namespace必须为Dao中接口的全限定类名,id必须为接口中的方法名 >> 此处我们就可以获得 sql的Id
String sqlId = daoInterface.getName() + "." + method.getName();
// 获取当前方法的SQL类型[CRUD]
// SqlCommandType sqlCommandType = sqlSession.getConfiguration().getMappedStatement(sqlId).getSqlCommandType();
String sqlCommandType = sqlSession.getConfiguration().getMappedStatement(sqlId).getSqlCommandType().name();
// 分情况讨论
if("UPDATE".equals(sqlCommandType)){
methodCode.append("return sqlSession.update(\""+sqlId+"\", arg0);");
}else if("SELECT".equals(sqlCommandType)){
String returnType = method.getReturnType().getName();
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;
}
}
三、接口代理机制
🌔1、对于我们用 Javassist 方式实现的 GenerateDaoProxy 类感觉比直接写dao中接口的实现类费事,那我们为什么还去写这个方法呢?
实际上确实没啥必要去自己写generate方法,因为 mybatis 为我们封装了这个接口,我们直接调用即可。
之所以我们自己去实现是为了容易理解javassist的底层是如何实现的。
🌔2、使用 mybatis 的接口有什么要求吗?
当然,在Mapper 映射文件中 namespace 只能为 接口的全限定类名,而且其中SQL语句的 id 要与接口中的方法名对应上。
🌔3、比较一下我们自己写的静态类方法和 mybatis 提供的接口代理是如何使用的?
//数据库操作的对象[第一种采用接口的实现类]
private AccountDao accountDao= new AccountDaoImpl();
// 使用我们的工具类动态生成
private AccountDao accountDao = (AccountDao) GenerateDaoProxy1.generate(SqlSessionUtil.openSession(), AccountDao.class);
// 使用mybatis提供的接口代理机制
private AccountDao accountDao = SqlSessionUtil.openSession().getMapper(AccountDao.class);
🌔4、为了更好的说明如何使用这个接口代理,我们写一个增删改查的 java 案例
(1)准备数据库的一张表 t_car 【其实之前用过这个表】
(2)在 idea 中创建一个新的普通Maven Java模块 mybatis-005-crud2
- 老样子,第一步修改 pom.xml 文件 >> 确定打包方式和导入依赖
- 创建我们需要的 xml 文件 >> mybatis的核心配置文件和SQL的映射文件【此处我就不用日志文件了】
- 贴一张目录结构
(3)两个 xml 文件如下
- 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>
<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="jdbc:mysql://localhost:3306/powernode"/>
<property name="username" value="root"/>
<property name="password" value="111111"/>
</dataSource>
</environment>
</environments>
<mappers>
<!--sql映射文件创建好之后,需要将该文件路径配置到这里-->
<mapper resource="CarMapper.xml"/>
</mappers>
</configuration>
- CarMapper.xml 映射文件 【当表中字段名与我们封装的pojo类的属性名对应不上时记得起别名】
<?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.powernode.mybatis.mapper.CarMapper">
<insert id="insert">
-- 因为id在设计表的时候我们采用了自增的方式,所以插入数据的时候不用指定id
insert into t_car values (null, #{
carNum},#{
brand},#{
guidePrice},#{
produceTime},#{
carType})
</insert>
<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>
<delete id="deleteById">
delete from t_car where id = #{
id}
</delete>
<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>
(4)贴一下我们封装的汽车的普通 Java 类 【之所以说普通因为只有属性、构造方法、get和set、toString方法】
package com.powernode.mybatis.pojo;
/**
* @author Bonbons
* @version 1.0
*/
public class Car {
// id,car_num,brand,guide_price,produce_time,car_type
// 使用包装类,出现null时不会报错
// 对于这些私有属性要与表中数据对应上
private Long id;
private String carNum;
private String brand;
private Double guidePrice;
private String produceTime;
private String carType;
//提供构造方法
public Car(){
}
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;
}
//提供get和set方法
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getCarNum() {
return carNum;
}
public void setCarNum(String carNum) {
this.carNum = carNum;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public Double getGuidePrice() {
return guidePrice;
}
public void setGuidePrice(Double guidePrice) {
this.guidePrice = guidePrice;
}
public String getProduceTime() {
return produceTime;
}
public void setProduceTime(String produceTime) {
this.produceTime = produceTime;
}
public String getCarType() {
return carType;
}
public void setCarType(String carType) {
this.carType = carType;
}
//重写toString方法
@Override
public String toString() {
return "Car{" +
"id=" + id +
", carNum='" + carNum + '\'' +
", brand='" + brand + '\'' +
", guidePrice=" + guidePrice +
", produceTime='" + produceTime + '\'' +
", carType='" + carType + '\'' +
'}';
}
}
(5)定义的工具类 SqlSessionUtil【用于创建连接对象,因为此处没啥业务,我们就不用ThreadLocal了】
package com.powernode.mybatis.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;
/**
* @author Bonbons
* @version 1.0
*/
public class SqlSessionUtil {
private SqlSessionUtil(){
}
//定义一个SqlSession
private static final SqlSessionFactory sqlSessionFactory;
//在类加载的时候初始化SqlSessionFactory
static {
try {
sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
//定义一个全局的ThreadLocal,可以保证一个SqlSession对应一个线程
private static ThreadLocal<SqlSession> local = new ThreadLocal<>();
//通过一个公有的方法为外部提供会话的对象 >> 确保同一个线程操作的是同一个连接对象
public static SqlSession openSession(){
//我们用local去获取会话
SqlSession sqlSession = local.get();
//如果当前没有开启的会话就去创建一个,如果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();
local.remove();
}
}
}
(6)我们定义对数据库中表操作的接口 【习惯问题,正常都叫XxxDao,使用mybatis的dao层都叫mapper层】
package com.powernode.mybatis.mapper;
import com.powernode.mybatis.pojo.Car;
import java.util.List;
/**
* @author Bonbons
* @version 1.0
*/
public interface CarMapper {
/**
* 新增车辆信息
* @param car Car对象
* @return 返回影响数据库表中记录的条数
*/
int insert(Car car);
/**
* 删除指定id的车辆信息
* @param id 车id
* @return 返回影响的数据条数
*/
int deleteById(Long id);
/**
* 修改已有的车辆信息
* @param car 汽车对象
* @return 返回影响的数据条数
*/
int update(Car car);
/**
* 根据id查询车辆信息
* @param id ID
* @return 查询到的信息
*/
Car selectById(Long id);
/**
* 获取所有的汽车信息
* @return 返回所有查询结果的集合
*/
List<Car> selectAll();
}
(7)最后就可以在test包下写我们的测试方法了
package com.powernode.mybatis.test;
import com.powernode.mybatis.mapper.CarMapper;
import com.powernode.mybatis.pojo.Car;
import com.powernode.mybatis.utils.SqlSessionUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.List;
/**
* @author Bonbons
* @version 1.0
*/
public class CarMapperTest {
@Test
public void testInsert(){
// 创建连接对象
SqlSession sqlSession = SqlSessionUtil.openSession();
CarMapper mapper = sqlSession.getMapper(CarMapper.class);
// 创建我们要添加的Car
Car car = new Car(null, "1","卡罗拉", 100.0, "2022-11-04", "燃油车");
// 调用我们的insert方法
int count = mapper.insert(car);
System.out.println(count);
sqlSession.commit();
sqlSession.close();
}
@Test
public void testDeleteById(){
SqlSession sqlSession = SqlSessionUtil.openSession();
CarMapper mapper = sqlSession.getMapper(CarMapper.class);
int count = mapper.deleteById(1L);
sqlSession.commit();
sqlSession.close();
System.out.println(count);
}
@Test
public void testUpdate(){
SqlSession sqlSession = SqlSessionUtil.openSession();
CarMapper mapper = sqlSession.getMapper(CarMapper.class);
Car car = new Car(21L, "21","卡罗拉新款", 50.0, "2022-11-04", "燃油车");
mapper.update(car);
sqlSession.commit();
sqlSession.close();
}
@Test
public void testSelectById(){
SqlSession sqlSession = SqlSessionUtil.openSession();
CarMapper mapper = sqlSession.getMapper(CarMapper.class);
Car car = mapper.selectById(2L);
System.out.println(car);
sqlSession.close();
}
@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));
}
}
(8)所有的方法测试都是通过的,最后再赘述一下流程
利用工具类创建会话 >> 调用getMapper方法(参数为 CarMapper.class) >> 利用mapper调用我们的方法