[Java]Mybatis学习笔记(动力节点老杜)(四)

简介: [Java]Mybatis学习笔记(动力节点老杜)(四)

MyBatis 三大核心对象的作用域

SqlSessionFactoryBuilder

  • 这个类可以被实例化、使用和丢弃,一旦创建了 SqlSessionFactory,就不再需要它了。
  • SqlSessionFactoryBuilder 对象的作用就是创建 SqlSessionFactory 对象,只要有了 SqlSessionFactory 对象就不在需要 SqlSessionFactoryBuilder 对象,其就可以被垃圾回收器回收了
  • 因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。
static {
    try {
        // 获取默认数据库环境对应的SqlSessionFactory对象
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
    } catch (IOException e) {
        e.printStackTrace();
    }
}
  • 你可以重用 SqlSessionFactoryBuilder 来创建多个 SqlSessionFactory 实例,但最好还是不要一直保留着它,以保证所有的 XML 解析资源可以被释放给更重要的事情。

SqlSessionFactory

  • SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。
  • 使用 SqlSessionFactory 的最佳实践是在应用运行期间不要重复创建多次,多次重建 SqlSessionFactory 被视为一种代码“坏习惯”。
  • 因此 SqlSessionFactory 的最佳作用域是应用作用域。 有很多方法可以做到,最简单的就是使用单例模式或者静态单例模式。
// 默认数据库环境对应的SqlSessionFactory
private static SqlSessionFactory sqlSessionFactory = null;
static {
    try {
        // 获取默认数据库环境对应的SqlSessionFactory对象
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
    } catch (IOException e) {
        e.printStackTrace();
    }
}

SqlSession

  • 每个线程都应该有它自己的 SqlSession 实例。
  • SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。
  • 绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。 也绝不能将 SqlSession 实例的引用放在任何类型的托管作用域中,比如 Servlet 框架中的 HttpSession。
  • 如果你现在正在使用一种 Web 框架,考虑将 SqlSession 放在一个和 HTTP 请求相似的作用域中。 换句话说,每次收到 HTTP 请求,就可以打开一个 SqlSession,返回一个响应后,就关闭它。 这个关闭操作很重要,为了确保每次都能执行关闭操作,你应该把这个关闭操作放到 finally 块中。
  • 下面的示例就是一个确保 SqlSession 关闭的标准模式:
try (SqlSession session = sqlSessionFactory.openSession()) {
  // 你的应用逻辑代码
}

使用 javassist 生成类

依赖

<dependencies>
  <!-- javassist -->
  <dependency>
    <groupId>org.javassist</groupId>
    <artifactId>javassist</artifactId>
    <version>3.29.1-GA</version>
  </dependency>
  <!-- junit -->
  <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.13.2</version>
    <scope>test</scope>
  </dependency>
</dependencies>

运行参数配置

  • 出现下列报错,需要进行运行参数的配置,JDK高版本的问题,低版本没有java.base这个模块,但是高版本添加了这个模块
java.lang.reflect.InaccessibleObjectException: 
Unable to make protected final java.lang.Class 
java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain) 
throws java.lang.ClassFormatError accessible: 
module java.base does not "opens java.lang" to unnamed module @28c97a5
  • –add-opens java.base/java.lang=ALL-UNNAMED
  • –add-opens java.base/sun.net.util=ALL-UNNAMED

Javassist的使用

import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import java.lang.reflect.Method;
/**
 * ClassName: Test
 * Package: PACKAGE_NAME
 * Description:
 *
 * @Author tcw
 * @Create 2023-05-31 15:54
 * @Version 1.0
 */
public class Test {
    @org.junit.Test
    public void tset01() throws Exception {
        // 获取类池,用于生成class
        ClassPool pool = ClassPool.getDefault();
        // 使用类池制造类
        // 方法参数为类的全类名
        CtClass ctClass = pool.makeClass("cw.mybatis.bank.dao.impl.AccountDaoImpl");
        // 类中方法的代码
        String methodCode = "public void insert() { System.out.println(\"数据库正在新增信息...\");}";
        // 制造类中的方法
        // 参数一:类中方法的代码;参数二:为哪个类创建方法
        CtMethod ctMethod = CtMethod.make(methodCode, ctClass);
        // 将创建的方法添加到类中
        ctClass.addMethod(ctMethod);
        // 在内存中动态生成class,将类加载到JVM中
        // 得到类对应的Class对象
        // Class<?> aClass = ctClass.toClass();
        ctClass.toClass();
        // 加载类到JVM中
        Class<?> aClass = Class.forName("cw.mybatis.bank.dao.impl.AccountDaoImpl");
        // 利用动态生成的类进行对象的创建(使用无参构造方法)
        Object o = aClass.getDeclaredConstructor().newInstance();
        // 获取类中的方法
        Method insertMethod = aClass.getDeclaredMethod("insert");
        // 调用新创建的对象的insert方法
        insertMethod.invoke(o);
    }
}

使用 javassist 动态生成类并实现接口

public interface AccountDao {
    void delete();
}
@org.junit.Test
public void testGenerateImpl() throws Exception {
    // 获取类型用于类的制造
    ClassPool pool = ClassPool.getDefault();
    // 制造类
    CtClass AccountDaoImpl = pool.makeClass("cw.study.mybatis.dao.impl.AccountDaoImpl");
    // 制造接口
    CtClass AccountDao = pool.makeInterface("cw.study.mybatis.dao.AccountDao");
    // 添加接口到类中
    AccountDaoImpl.addInterface(AccountDao);
    // 实现接口中的方法
    // 方法的代码
    String methodCode = "public void delete() {System.out.println(\"数据库正在删除信息...\");}";
    // 制造AccountDaoImpl类的方法
    CtMethod method = CtMethod.make(methodCode, AccountDaoImpl);
    // 将方法添加到类中
    AccountDaoImpl.addMethod(method);
    // 在内存中生成类,并将类加载到JVM中
    Class<?> AccountDaoImplClass = AccountDaoImpl.toClass();
    // 创建对象
    AccountDao accountDao = (AccountDao) AccountDaoImplClass.getDeclaredConstructor().newInstance();
    // 调用方法
    accountDao.delete();
}

使用 javassist 动态生成类并实现接口中所有方法

public interface AccountDao {
    void delete();
    int insert(String actno);
    int update(String actno, Double balance);
    String selectByActno(String actno);
}
@org.junit.Test
public void testGenerateAccountDaoImpl() throws Exception {
    // 获取类池
    ClassPool pool = ClassPool.getDefault();
    // 制造类
    CtClass ctClass = pool.makeClass("cw.study.mybatis.dao.impl.AccountDaoImpl");
    // 制造接口
    CtClass ctInterface = pool.makeInterface("cw.study.mybatis.dao.AccountDao");
    // 类实现接口
    ctClass.addInterface(ctInterface);
    // 获取接口中的所有方法
    Method[] methods = AccountDao.class.getDeclaredMethods();
    // 遍历接口中的每个方法实现接口方法
    Arrays.stream(methods).forEach(method -> {
        // 实现接口方法
        // public void update(String actno, Double balance) {}
        StringBuilder builder = new StringBuilder();
        // 添加访问权限修饰符
        builder.append("public ");
        // 添加返回值类型
        builder.append(method.getReturnType().getName());
        builder.append(" ");
        // 追加方法名
        builder.append(method.getName());
        builder.append("(");
        // 获取方法的形参列表
        Parameter[] parameters = method.getParameters();
        // 遍历追加形参列表
        for (int i = 0; i < parameters.length; i++) {
            // 追加形参列表中形参的类型
            builder.append(parameters[i].getType().getName());
            builder.append(" ");
            // 追加形参名 parameters[i].getName() 获取的形参名为 arg0 arg1 ...
            builder.append(parameters[i].getName());
            if (i != parameters.length - 1) {
                builder.append(", ");
            }
        }
        builder.append(") {");
        // 添加方法体的方法
        builder.append("System.out.println(\"" + method.getName() + "\");");
        // 动态添加return语句
        // 获取返回类型的简类名
        String returnTypeSimpleName = method.getReturnType().getSimpleName();
        if ("void".equals(returnTypeSimpleName)) {
        } else if ("int".equals(returnTypeSimpleName)) {
            builder.append("return 111;");
        } else if ("String".equals(returnTypeSimpleName)) {
            builder.append("return \"method return string\";");
        }
        builder.append("}");
        // System.out.println(builder);
        try {
            // 制造ctClass的方法
            CtMethod method1 = CtMethod.make(builder.toString(), ctClass);
            // 将方法添加到类中
            ctClass.addMethod(method1);
        } catch (CannotCompileException e) {
            e.printStackTrace();
        }
    });
    // 在内存中生成类并将类加载到JVM
    Class<?> aClass = ctClass.toClass();
    // 创建对象(接口的实现类可以进行强转)
    AccountDao accountDao = (AccountDao) aClass.getDeclaredConstructor().newInstance();
    // 调用方法
    accountDao.delete();
    accountDao.insert("");
    accountDao.selectByActno("");
    accountDao.update("", 12.0);
}

工具类 GenerateDaoProxy 的实现

  • 工具类 GenerateDaoProxy 可以动态生成接口的实现类
  • 在MyBatis中对javassist进行了二次包装,所以可以在MyBatis中直接使用javassist
  • 由于SQL语句的id是框架的使用者提供的,所以框架的开发者不可能知道SQL语句的id,因此框架的开发者就规定SQL语句的id必须时dao接口中的方法名,namespace必须是dao接口的全限定类名,否则不能使用框架提供的动态代理生成接口的实现类的功能
package cw.study.mybatis.utils;
import org.apache.ibatis.javassist.CannotCompileException;
import org.apache.ibatis.javassist.ClassPool;
import org.apache.ibatis.javassist.CtClass;
import org.apache.ibatis.javassist.CtMethod;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.session.SqlSession;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Arrays;
/**
 * ClassName: GenerateDaoProxy
 * Package: cw.study.mybatis.utils
 * Description:
 * 该工具类用于动态生成实现Dao接口的代理类
 * GenerateDaoProxy 这个工具类是MyBatis框架提供的
 *
 * @Author tcw
 * @Create 2023-05-31 17:42
 * @Version 1.0
 */
public class GenerateDaoProxy {
    /**
     * 工具类的构造方法私有化,防止创建对象
     */
    private GenerateDaoProxy() {}
    /**
     * 动态生成Dao接口的代理类对象
     *
     * @param sqlSession 与数据库的会话对象,由工具类的使用这传递
     * @param daoInterfaceClass Dao接口
     * @return Dao接口的代理类对象
     */
    public static Object generate(SqlSession sqlSession, Class daoInterfaceClass) {
        // 获取类池
        ClassPool pool = ClassPool.getDefault();
        // 制造接口的代理类
        CtClass daoInterfaceImpl = pool.makeClass(daoInterfaceClass.getName() + "Impl");
        // 制造接口
        CtClass daoInterface = pool.makeInterface(daoInterfaceClass.getName());
        // 接口的代理类实现接口
        daoInterfaceImpl.addInterface(daoInterface);
        // 接口的代理类实现接口中的方法
        // 获取接口中所有的方法
        Method[] daoInterfaceClassDeclaredMethods = daoInterfaceClass.getDeclaredMethods();
        // 遍历接口中的所有的方法,逐个进行实现,并添加到接口的代理类中
        Arrays.stream(daoInterfaceClassDeclaredMethods).forEach(method -> {
            // 实现接口中的方法
            try {
                // 接口方法的实现方法的代码
                StringBuilder methodCode = new StringBuilder();
                // public int update(String actno, Double balance) {代码;}
                methodCode.append("public ");
                // 获取接口方法的返回值类型,添加到实现方法中
                methodCode.append(method.getReturnType().getName());
                methodCode.append(" ");
                // 获取接口方法的方法名,添加到实现方法中
                methodCode.append(method.getName());
                methodCode.append("(");
                // 获取接口方法的形参列表,添加到实现方法中
                Parameter[] methodParameters = method.getParameters();
                for (int i = 0; i < methodParameters.length; i++) {
                    // 获取形参的类型
                    methodCode.append(methodParameters[i].getType().getName());
                    methodCode.append(" ");
                    // 获取形参的名 arg0 arg1 arg2 ...
                    methodCode.append(methodParameters[i].getName());
                    // 如果不是最后一个形参需要在后面添加逗号
                    if (i != methodParameters.length - 1) {
                        methodCode.append(", ");
                    }
                }
                methodCode.append(")");
                methodCode.append("{\n");
                // 接口方法的具体实现
                // 获取与数据库的会话对象
                // javassist 需要知道类是哪个包下的所以要使用全限定包名
                methodCode.append("\torg.apache.ibatis.session.SqlSession sqlSession = cw.study.mybatis.utils" +
                                ".SqlSessionUtil.openSession();\n");
                // 执行SQL语句,我们需要知道SQL语句的类型才可以调用相应的方法执行SQL
                // getConfiguration() 获取MyBatis核心配置文件中根标签configuration中的内容
                // getMappedStatement(SQL语句的Id) 根据SQL语句的id获取相应的SQL语句
                // getSqlCommandType() 获取SQL语句的类型,获取到SQL语句的类型是一个枚举值
                // 由于SQL语句的id是框架的使用者提供的,所以框架的开发者不可能知道SQL语句的id
                // 因此框架的开发者就规定SQL语句的id必须时dao接口中的方法名,namespace必须是dao接口的全限定类名
                // 否则不能使用框架提供的动态代理生成接口的实现类的功能
                // SQL id = namespace + . +SQL id
                String SqlId = daoInterfaceClass.getName() + "." + method.getName();
                SqlCommandType sqlCommandType = sqlSession.getConfiguration().getMappedStatement(SqlId).getSqlCommandType();
                // 根据SQL语句的类型的不同调用相应的方法
                if (sqlCommandType == SqlCommandType.INSERT) {
                } else if (sqlCommandType == SqlCommandType.DELETE) {
                } else if (sqlCommandType == SqlCommandType.UPDATE) {
                    methodCode.append("\treturn sqlSession.update(\"" + SqlId + "\", arg0);\n");
                } else if (sqlCommandType == SqlCommandType.SELECT) {
                    // by java.lang.reflect.InvocationTargetException
                    methodCode.append("\treturn (" + method.getReturnType().getName() + ") sqlSession.selectOne(\"" + SqlId +
                            "\", arg0);\n");
                }
                methodCode.append("}");
                // System.out.println(methodCode);
                // 制造接口方法对应的实现方法
                CtMethod ctMethod = CtMethod.make(methodCode.toString(), daoInterfaceImpl);
                // 将制造的实现方法添加到动态生成的代理类中
                daoInterfaceImpl.addMethod(ctMethod);
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
        // 接口动态代理类的对象
        Object daoInterfaceProxyObj = null;
        try {
            // 在内存中动态生成类,并且将动态生成的类添加到JVM中
            Class<?> daoInterfaceImplClass = daoInterfaceImpl.toClass();
            // 创建接口代理类的对象
            daoInterfaceProxyObj = daoInterfaceImplClass.getDeclaredConstructor().newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        // 返回接口动态代理类的对象
        return daoInterfaceProxyObj;
    }
}

工具类 GenerateDaoProxy 测试

  • 要想使用工具类GenerateDaoProxy这种自动生成Dao接口实现类的机制: namespace必须是dao接口的全限定名称,id必须是dao接口的方法名。
<!-- mybatis -->
<dependency>
  <groupId>org.mybatis</groupId>
  <artifactId>mybatis</artifactId>
  <version>3.5.10</version>
</dependency>
<!-- mysql -->
<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
  <version>8.0.30</version>
</dependency>
<!-- junit -->
<dependency>
  <groupId>junit</groupId>
  <artifactId>junit</artifactId>
  <version>4.13.2</version>
  <scope>test</scope>
</dependency>
package cw.study.mybatis.pojo;
/**
 * ClassName: Account
 * Package: cw.study.mybatis.pojo
 * Description:
 *
 * @Author tcw
 * @Create 2023-05-28 14:33
 * @Version 1.0
 */
public class Account {
    private Long id;
    private String actno;
    private Double balance;
    @Override
    public String toString() {
        return "Account{" +
                "id=" + id +
                ", actno='" + actno + '\'' +
                ", balance=" + balance +
                '}';
    }
    public Account() {
    }
    public Account(Long id, String actno, Double balance) {
        this.id = id;
        this.actno = actno;
        this.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;
    }
}
package cw.study.mybatis.dao;
import cw.study.mybatis.pojo.Account;
/**
 * ClassName: AccountDao
 * Package: cw.study.mybatis.dao
 * Description:
 *
 * @Author tcw
 * @Create 2023-05-31 16:44
 * @Version 1.0
 */
public interface AccountDao {
    Account selectByActno(String actno);
}
<?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="cw.study.mybatis.dao.AccountDao">
  <select id="selectByActno" resultType="cw.study.mybatis.pojo.Account">
    select * from t_act where actno = #{actno}
  </select>
</mapper>
@org.junit.Test
public void testGenerateDaoProxy() {
    SqlSession sqlSession = SqlSessionUtil.openSession();
    AccountDao accountDao = (AccountDao) GenerateDaoProxy.generate(sqlSession, AccountDao.class);
    Account act001 = accountDao.selectByActno("act001");
    System.out.println(act001);
}

MyBatis 的 getMapper 方法(MyBatis 的接口代理机制)

  • 在MyBatis中提供了相关机制,可以为我们动态生成dao接口的代理实现类,即工具类 GenerateDaoProxy 不用我们编写
  • MyBatis中实际上采用了代理机制,在内存中生成dao接口的代理实现类,并创建代理类的实例对象
  • 如果要使用MyBatis提供的dao接口代理机制,SQL语句的id必须时dao接口中的方法名,namespace必须是dao接口的全限定类名
  • MyBatis提供的dao接口代理机制的使用如下:
@org.junit.Test
public void testGetMapper() {
    SqlSession sqlSession = SqlSessionUtil.openSession();
    // GenerateDaoProxy.generate(sqlSession, AccountDao.class);
    // 需要一个sqlSession用于获取SQL id,AccountDao.class 需要代理实现的接口
    // sqlSession.getMapper(AccountDao.class)
    // 调用该方法的就是用于获取SQL id的sqlSession,AccountDao.class 需要代理实现的接口
    // 该方法返回dao接口的代理实现类对象实例
    // 要使用该方法,SQL语句的id必须时dao接口中的方法名,namespace必须是dao接口的全限定类名
    AccountDao accountDaoProxy = sqlSession.getMapper(AccountDao.class);
    Account act001 = accountDaoProxy.selectByActno("act001");
    System.out.println(act001);
}

面向接口进行CRUD

工具类 SqlSessionUtil

package cw.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;
/**
 * ClassName: SqlSessionUtil
 * Package: cw.mybatis.utils
 * Description:
 *
 * @Author tcw
 * @Create 2023-05-23 21:40
 * @Version 1.0
 */
public class SqlSessionUtil {
    private static SqlSessionFactory sqlSessionFactory = null; // 默认使用数据库环境对应的SqlSessionFactory
    private static ThreadLocal<SqlSession> local = new ThreadLocal<>(); // 保证一个线程对应一个SqlSession
    static {
        try {
            // 获取默认使用数据库环境对应的SqlSessionFactory对象
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config" +
                    ".xml"));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    /**
     * 私有化构造器,防止创建工具类实例对象
     */
    private SqlSessionUtil() {}
    /**
     * 使用工厂对象开启本线程和数据库的会话
     *
     * @return 与数据库的会话对象
     */
    public static SqlSession openSession() {
        SqlSession sqlSession = local.get(); // 获取当前线程对应的数据库会话对象
        // 如果当前线程没有对应的数据库会话对象,则创建一个会话对象
        if (sqlSession == null) {
            sqlSession = sqlSessionFactory.openSession();
            // 保存本线程对应的数据库会话对象
            // 将sqlSession对象绑定到当前线程上。
            local.set(sqlSession);
        }
        return sqlSession;
    }
    /**
     * 关闭会话对象
     * 关闭SqlSession对象并从当前线程中移除SqlSession对象
     */
    public static void close() {
        SqlSession sqlSession = local.get(); // 获取当前线程对应的数据库会话对象
        // 如果当前线程有相应的数据库会话对象就进行关闭
        if (sqlSession != null) {
            sqlSession.close();
            // 注意移除SqlSession对象和当前线程的绑定关系。
            // 因为Tomcat服务器支持线程池。也就是说:用过的线程对象t1,可能下一次还会使用这个t1线程。
            local.remove();
        }
    }
}

Car(pojo)

package cw.mybatis.pojo;
/**
 * ClassName: Car
 * Package: cw.mybatis.pojo
 * Description:
 * 封装汽车相关信息的pojo类,普通的java类。
 *
 * @Author tcw
 * @Create 2023-05-23 21:59
 * @Version 1.0
 */
public class Car {
    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;
    }
    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;
    }
    @Override
    public String toString() {
        return "Car{" + "id=" + id + ", carNum='" + carNum + '\'' + ", brand='" + brand + '\'' + ", guidePrice=" + guidePrice + ", produceTime='" + produceTime + '\'' + ", carType='" + carType + '\'' + '}';
    }
}

CarMapper

  • 操作数据库的接口,在MyBatis中一般包名命名为mapper,就是MVC架构模式中的dao
  • 在MyBatis中操作数据库的接口名,一般叫XxxxMapper,而不为XxxxDao
package cw.mybatis.mapper;
import cw.mybatis.pojo.Car;
import java.util.List;
/**
 * ClassName: CarMapper
 * Package: cw.mybatis.mapper
 * Description:
 *
 * @Author tcw
 * @Create 2023-05-23 21:57
 * @Version 1.0
 */
public interface CarMapper {
    /**
     * 新增 car
     * 
     * @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();
}

CarMapper.xml

<?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">
<!-- 如果要使用面向接口的方式进行CRUD,则namespace的属性值必须为相应接口的全类名 -->
<!-- SQL语句的id必须为方法名 -->
<mapper namespace="cw.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="cw.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="cw.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>

测试

package cw.mybatis.mapper;
import cw.mybatis.pojo.Car;
import cw.mybatis.utils.SqlSessionUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.List;
import static org.junit.Assert.*;
/**
 * ClassName: CarMapperTest
 * Package: cw.mybatis.mapper
 * Description:
 *
 * @Author tcw
 * @Create 2023-05-23 22:15
 * @Version 1.0
 */
public class CarMapperTest {
    @Test
    public void insert() {
        SqlSession sqlSession = SqlSessionUtil.openSession();
        // 面向接口,获取接口的代理对象
        CarMapper mapper = sqlSession.getMapper(CarMapper.class);
        // 数据对应的对象
        Car car = new Car(null, "4444", "奔驰C200", 32.0, "2000-10-10", "新能源");
        // 调用定义的新增car的方法
        int insert = mapper.insert(car);
        System.out.println(insert);
        sqlSession.commit();
        SqlSessionUtil.close();
    }
    @Test
    public void deleteById() {
        SqlSession sqlSession = SqlSessionUtil.openSession();
        CarMapper mapper = sqlSession.getMapper(CarMapper.class);
        int i = mapper.deleteById(1L);
        System.out.println(i);
        sqlSession.commit();
        SqlSessionUtil.close();
    }
    @Test
    public void update() {
        SqlSession sqlSession = SqlSessionUtil.openSession();
        CarMapper mapper = sqlSession.getMapper(CarMapper.class);
        Car car = new Car(20L, "4444", "奔驰C200&&&", 32.0, "2000-10-10", "新能源");
        int update = mapper.update(car);
        System.out.println(update);
        sqlSession.commit();
        SqlSessionUtil.close();
    }
    @Test
    public void selectById() {
        SqlSession sqlSession = SqlSessionUtil.openSession();
        CarMapper mapper = sqlSession.getMapper(CarMapper.class);
        Car car = mapper.selectById(20L);
        System.out.println(car);
        SqlSessionUtil.close();
    }
    @Test
    public void selectAll() {
        SqlSession sqlSession = SqlSessionUtil.openSession();
        List<Car> cars = sqlSession.getMapper(CarMapper.class).selectAll();
        cars.forEach(System.out::println);
        SqlSessionUtil.close();
    }
}


相关文章
|
1月前
|
存储 Java
Java学习笔记 List集合的定义、集合的遍历、迭代器的使用
Java学习笔记 List集合的定义、集合的遍历、迭代器的使用
|
3天前
|
存储 安全 Java
Java修仙之路,十万字吐血整理全网最完整Java学习笔记(基础篇)
从Java环境的搭建到实际代码的编写,从基本用法的讲解到底层原理的剖析,深度解析Java基础知识。本文是《Java学习路线》专栏的起始文章,旨在提供一套完整的Java学习路线,覆盖Java基础知识、数据库、SSM/SpringBoot等框架、Redis/MQ等中间件、设计模式、架构设计、性能调优、源码解读、核心面试题等全面的知识点,并在未来不断更新和完善,帮助Java从业者在更短的时间内成长为高级开发。
Java修仙之路,十万字吐血整理全网最完整Java学习笔记(基础篇)
|
3天前
|
存储 安全 Java
Java修仙之路,十万字吐血整理全网最完整Java学习笔记(进阶篇)
本文是Java基础的进阶篇,对异常、集合、泛型、Java8新特性、I/O流等知识进行深入浅出的介绍,并附有对应的代码示例,重要的地方带有对性能、底层原理、源码的剖析。适合Java初学者。
Java修仙之路,十万字吐血整理全网最完整Java学习笔记(进阶篇)
|
3天前
|
存储 安全 Java
Java修仙之路,十万字吐血整理全网最完整Java学习笔记(高级篇)
本文是“Java学习路线”中Java基础知识的高级篇,主要对多线程和反射进行了深入浅出的介绍,在多线程部分,详细介绍了线程的概念、生命周期、多线程的线程安全、线程通信、线程同步,并对synchronized和Lock锁;反射部分对反射的特性、功能、优缺点、适用场景等进行了介绍。
Java修仙之路,十万字吐血整理全网最完整Java学习笔记(高级篇)
|
1月前
|
SQL druid Java
Java数据库部分(MySQL+JDBC)(二、JDBC超详细学习笔记)(下)
Java数据库部分(MySQL+JDBC)(二、JDBC超详细学习笔记)
45 3
Java数据库部分(MySQL+JDBC)(二、JDBC超详细学习笔记)(下)
|
1月前
|
SQL Java 关系型数据库
Java数据库部分(MySQL+JDBC)(二、JDBC超详细学习笔记)(上)
Java数据库部分(MySQL+JDBC)(二、JDBC超详细学习笔记)
54 3
Java数据库部分(MySQL+JDBC)(二、JDBC超详细学习笔记)(上)
|
18天前
|
jenkins Java Shell
jenkins学习笔记之十三:配置SonarScanner扫描Java项目
jenkins学习笔记之十三:配置SonarScanner扫描Java项目
|
2天前
|
存储 缓存 安全
【Java面试题汇总】多线程、JUC、锁篇(2023版)
线程和进程的区别、CAS的ABA问题、AQS、哪些地方使用了CAS、怎么保证线程安全、线程同步方式、synchronized的用法及原理、Lock、volatile、线程的六个状态、ThreadLocal、线程通信方式、创建方式、两种创建线程池的方法、线程池设置合适的线程数、线程安全的集合?ConcurrentHashMap、JUC
【Java面试题汇总】多线程、JUC、锁篇(2023版)
|
13天前
|
监控 Java 调度
【Java学习】多线程&JUC万字超详解
本文详细介绍了多线程的概念和三种实现方式,还有一些常见的成员方法,CPU的调动方式,多线程的生命周期,还有线程安全问题,锁和死锁的概念,以及等待唤醒机制,阻塞队列,多线程的六种状态,线程池等
74 6
【Java学习】多线程&JUC万字超详解
|
6天前
|
Java 调度 开发者
Java并发编程:深入理解线程池
在Java的世界中,线程池是提升应用性能、实现高效并发处理的关键工具。本文将深入浅出地介绍线程池的核心概念、工作原理以及如何在实际应用中有效利用线程池来优化资源管理和任务调度。通过本文的学习,读者能够掌握线程池的基本使用技巧,并理解其背后的设计哲学。