• MyBatis记录(一)----与MyBatis初识


    目录

    学习MyBatis

    一、MyBatis概述

    1.1 框架

    1.2 三层架构

    1.3 JDBC不足

    1.4 了解MyBatis

    对ORM思想的深入理解:

    二、MyBatis入门程序

    2.1 版本

    软件版本:

    组件版本:

    2.2 MyBatis下载

    2.3 MyBatis入门程序开发步骤

    入门程序的小细节:

    2.4 关于MyBatis核心配置文件的名字和路径详解

    2.5 MyBatis第一个比较完整的代码写法

    2.6 引入JUnit

    2.7 引入日志框架logback

    2.8 MyBatis工具类SqlSessionUtil的封装

    三、使用MyBatis完成CRUD

    3.1 insert(Create)


    学习MyBatis

    一、MyBatis概述

    1.1 框架

    • 在文献中看到的framework被翻译为框架
    • Java常用框架:
      • SSM三大框架:Spring + SpringMVC + MyBatis
      • SpringBoot
      • SpringCloud
      • 等。。
    • 框架其实就是对通用代码的封装,提前写好了一堆接口和类,我们可以在做项目的时候直接引入这些接口和类(引入框架),基于这些现有的接口和类进行开发,可以大大提高开发效率。
    • 框架一般都以jar包的形式存在。(jar包中有class文件以及各种配置文件等。)
    • SSM三大框架的学习顺序:MyBatis、Spring、SpringMVC(仅仅是建议)

    1.2 三层架构

    • 表现层(UI):直接跟前端打交互(一是接收前端ajax请求,二是返回json数据给前端)
    • 业务逻辑层(BLL):一是处理表现层转发过来的前端请求(也就是具体业务),二是将从持久层获取的数据返回到表现层。
    • 数据访问层(DAL):直接操作数据库完成CRUD,并将获得的数据返回到上一层(也就是业务逻辑层)。
    • Java持久层框架:
      • MyBatis
      • Hibernate(实现了JPA规范)
      • jOOQ
      • Guzz
      • Spring Data(实现了JPA规范)
      • ActiveJDBC
      • ......

    1.3 JDBC不足

    • 示例代码1:
    1. // ......
    2. // sql语句写死在java程序中
    3. String sql = "insert into t_user(id,idCard,username,password,birth,gender,email,city,street,zipcode,phone,grade) values(?,?,?,?,?,?,?,?,?,?,?,?)";
    4. PreparedStatement ps = conn.prepareStatement(sql);
    5. // 繁琐的赋值:思考一下,这种有规律的代码能不能通过反射机制来做自动化。
    6. ps.setString(1, "1");
    7. ps.setString(2, "123456789");
    8. ps.setString(3, "zhangsan");
    9. ps.setString(4, "123456");
    10. ps.setString(5, "1980-10-11");
    11. ps.setString(6, "男");
    12. ps.setString(7, "zhangsan@126.com");
    13. ps.setString(8, "北京");
    14. ps.setString(9, "大兴区凉水河二街");
    15. ps.setString(10, "1000000");
    16. ps.setString(11, "16398574152");
    17. ps.setString(12, "A");
    18. // 执行SQL
    19. int count = ps.executeUpdate();
    20. // ......
    • 示例代码2:
    1. // ......
    2. // sql语句写死在java程序中
    3. String sql = "select id,idCard,username,password,birth,gender,email,city,street,zipcode,phone,grade from t_user";
    4. PreparedStatement ps = conn.prepareStatement(sql);
    5. ResultSet rs = ps.executeQuery();
    6. List userList = new ArrayList<>();
    7. // 思考以下循环中的所有代码是否可以使用反射进行自动化封装。
    8. while(rs.next()){
    9. // 获取数据
    10. String id = rs.getString("id");
    11. String idCard = rs.getString("idCard");
    12. String username = rs.getString("username");
    13. String password = rs.getString("password");
    14. String birth = rs.getString("birth");
    15. String gender = rs.getString("gender");
    16. String email = rs.getString("email");
    17. String city = rs.getString("city");
    18. String street = rs.getString("street");
    19. String zipcode = rs.getString("zipcode");
    20. String phone = rs.getString("phone");
    21. String grade = rs.getString("grade");
    22. // 创建对象
    23. User user = new User();
    24. // 给对象属性赋值
    25. user.setId(id);
    26. user.setIdCard(idCard);
    27. user.setUsername(username);
    28. user.setPassword(password);
    29. user.setBirth(birth);
    30. user.setGender(gender);
    31. user.setEmail(email);
    32. user.setCity(city);
    33. user.setStreet(street);
    34. user.setZipcode(zipcode);
    35. user.setPhone(phone);
    36. user.setGrade(grade);
    37. // 添加到集合
    38. userList.add(user);
    39. }
    40. // ......
    • JDBC不足:
        • SQL语句写死在Java程序中,不灵活。改SQL的话就要改Java代码。违背开闭原则OCP。
        • 给?传值是繁琐的。能不能自动化???
        • 将结果集封装成Java对象是繁琐的。能不能自动化???

    1.4 了解MyBatis

    • MyBatis本质上就是对JDBC的封装,通过MyBatis完成CRUD。
    • MyBatis在三层架构中负责持久层的,属于持久层框架。
    • MyBatis的发展历程:【引用百度百科】
      • MyBatis本是apache的一个开源项目iBatis,2010年这个项目由apache software foundation迁移到了google code,并且改名为MyBatis。2013年11月迁移到Github。
      • iBATIS一词来源于“internet”和“abatis”的组合,是一个基于Java的持久层框架。iBATIS提供的持久层框架包括SQL Maps和Data Access Objects(DAOs)。
    • 打开mybatis代码可以看到它的包结构中包含:ibatis
    • ORM:对象关系映射
      • O(Object):Java虚拟机中的Java对象
      • R(Relational):关系型数据库
      • M(Mapping):将Java虚拟机中的Java对象映射到数据库表中一行记录,或是将数据库表中一行记录映射成Java虚拟机中的一个Java对象。
      • ORM图示
      • MyBatis属于半自动化ORM框架。
      • Hibernate属于全自动化的ORM框架。
    • 对ORM思想的深入理解:

    • MyBatis框架特点:
      • 支持定制化 SQL、存储过程、基本映射以及高级映射
      • 避免了几乎所有的 JDBC 代码中手动设置参数以及获取结果集
      • 支持XML开发,也支持注解式开发。【为了保证sql语句的灵活,所以mybatis大部分是采用XML方式开发。】
      • 将接口和 Java 的 POJOs(Plain Ordinary Java Object,简单普通的Java对象)映射成数据库中的记录
      • 体积小好学:两个jar包,两个XML配置文件。
      • 完全做到sql解耦合。
      • 提供了基本映射标签。
      • 提供了高级映射标签。
      • 提供了XML标签,支持动态SQL的编写。
      • ......

    二、MyBatis入门程序

    只要你会JDBC,MyBatis就可以学。

    2.1 版本

    软件版本:
    • IntelliJ IDEA:2022.1.4
    • Navicat for MySQL:16.0.14
    • MySQL数据库:8.0.30
    组件版本:
    • MySQL驱动:8.0.30
    • MyBatis:3.5.10
    • JDK:Java17
    • JUnit:4.13.2
    • Logback:1.2.11

    2.2 MyBatis下载

    • 将框架以及框架的源码都下载下来,下载框架后解压,打开mybatis目录
      • 通过以上解压可以看到,框架一般都是以jar包的形式存在。我们的mybatis课程使用maven,所以这个jar我们不需要。
      • 官方手册需要。

    2.3 MyBatis入门程序开发步骤

     

    • 写代码前准备:
      • 准备数据库表:汽车表t_car,字段包括:
        • id:主键(自增)【bigint】
        • car_num:汽车编号【varchar】
        • brand:品牌【varchar】
        • guide_price:厂家指导价【decimal类型,专门为财务数据准备的类型】
        • produce_time:生产时间【char,年月日即可,10个长度,'2022-10-11'】
        • car_type:汽车类型(燃油车、电车、氢能源)【varchar】
      • 使用navicat for mysql工具建表
      • 使用navicat for mysql工具向t_car表中插入两条数据,如下:
      • 创建Project:建议创建Empty Project,设置Java版本以及编译版本等。
      • 设置IDEA的maven
      • 创建Module:普通的Maven Java模块
    • 步骤1:打包方式:jar(不需要war,因为mybatis封装的是jdbc。)
      1. <groupId>com.donggroupId>
      2. <artifactId>mybatis-001-introductionartifactId>
      3. <version>1.0-SNAPSHOTversion>
      4. <packaging>jarpackaging>
    • 步骤2:引入依赖(mybatis依赖 + mysql驱动依赖)
    1. <dependency>
    2. <groupId>org.mybatisgroupId>
    3. <artifactId>mybatisartifactId>
    4. <version>3.5.10version>
    5. dependency>
    6. <dependency>
    7. <groupId>mysqlgroupId>
    8. <artifactId>mysql-connector-javaartifactId>
    9. <version>8.0.30version>
    10. dependency>
    • 步骤3:在resources根目录下新建mybatis-config.xml配置文件(可以参考mybatis手册拷贝)
    1. "1.0" encoding="UTF-8" ?>
    2. configuration
    3. PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
    4. "http://mybatis.org/dtd/mybatis-3-config.dtd">
    5. <configuration>
    6. <environments default="development">
    7. <environment id="development">
    8. <transactionManager type="JDBC"/>
    9. <dataSource type="POOLED">
    10. <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
    11. <property name="url" value="jdbc:mysql://localhost:3306/dong"/>
    12. <property name="username" value="root"/>
    13. <property name="password" value="root"/>
    14. dataSource>
    15. environment>
    16. environments>
    17. <mappers>
    18. <mapper resource=""/>
    19. mappers>
    20. configuration>

    注意1:mybatis核心配置文件的文件名不一定是mybatis-config.xml,可以是其它名字。

    注意2:mybatis核心配置文件存放的位置也可以随意。这里选择放在resources根下,相当于放到了类的根路径下。

    • 步骤4:在resources根目录下新建CarMapper.xml配置文件(可以参考mybatis手册拷贝)
    1. "1.0" encoding="UTF-8" ?>
    2. mapper
    3. PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    4. "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    5. <mapper namespace="car">
    6. <insert id="insertCar">
    7. insert into t_car
    8. (id,car_num,brand,guide_price,produce_time,car_type)
    9. values
    10. (null,'102','丰田mirai',40.30,'2014-10-05','氢能源')
    11. insert>
    12. mapper>

    注意1:sql语句最后结尾可以不写“;”

    注意2:CarMapper.xml文件的名字不是固定的。可以使用其它名字。

    注意3:CarMapper.xml文件的位置也是随意的。这里选择放在resources根下,相当于放到了类的根路径下。

    注意4:将CarMapper.xml文件路径配置到mybatis-config.xml:

    <mapper resource="CarMapper.xml"/>
    • 步骤5:编写MyBatisIntroductionTest代码
    1. package com.dong.mybatis;
    2. import org.apache.ibatis.session.SqlSession;
    3. import org.apache.ibatis.session.SqlSessionFactory;
    4. import org.apache.ibatis.session.SqlSessionFactoryBuilder;
    5. import java.io.InputStream;
    6. /**
    7. * MyBatis入门程序
    8. * @author 老杜
    9. * @since 1.0
    10. * @version 1.0
    11. */
    12. public class MyBatisIntroductionTest {
    13. public static void main(String[] args) {
    14. // 1. 创建SqlSessionFactoryBuilder对象
    15. SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
    16. // 2. 创建SqlSessionFactory对象
    17. InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("mybatis-config.xml");
    18. SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(is);
    19. // 3. 创建SqlSession对象
    20. SqlSession sqlSession = sqlSessionFactory.openSession();
    21. // 4. 执行sql
    22. int count = sqlSession.insert("insertCar"); // 这个"insertCar"必须是sql的id
    23. System.out.println("插入几条数据:" + count);
    24. // 5. 提交(mybatis默认采用的事务管理器是JDBC,默认是不提交的,需要手动提交。)
    25. sqlSession.commit();
    26. // 6. 关闭资源(只关闭是不会提交的)
    27. sqlSession.close();
    28. }
    29. }

    注意1:默认采用的事务管理器是:JDBC。JDBC事务默认是不提交的,需要手动提交。

    • 步骤6:运行程序,查看运行结果,以及数据库表中的数据

    入门程序的小细节:

     

    2.4 关于MyBatis核心配置文件的名字和路径详解

    • 核心配置文件的名字是随意的,因为以下的代码:
    1. // 文件名是出现在程序中的,文件名如果修改了,对应这里的java程序也改一下就行了。
    2. InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("mybatis-config.xml");
    • 核心配置文件必须放到resources下吗?放到D盘根目录下,可以吗?测试一下:

    将mybatis-config.xml文件拷贝一份放到D盘根下,然后编写以下程序:

    1. package com.dong.mybatis;
    2. import org.apache.ibatis.session.SqlSession;
    3. import org.apache.ibatis.session.SqlSessionFactory;
    4. import org.apache.ibatis.session.SqlSessionFactoryBuilder;
    5. import java.io.FileInputStream;
    6. import java.io.InputStream;
    7. /**
    8. * 测试mybatis核心配置文件路径问题
    9. * @author captain_dong
    10. * @since 1.0
    11. * @version 1.0
    12. */
    13. public class MyBatisConfigFilePath {
    14. public static void main(String[] args) throws Exception{
    15. // 1. 创建SqlSessionFactoryBuilder对象
    16. SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
    17. // 2. 创建SqlSessionFactory对象
    18. // 这只是一个输入流,可以自己new。
    19. InputStream is = new FileInputStream("D:/mybatis-config.xml");
    20. SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(is);
    21. // 3. 创建SqlSession对象
    22. SqlSession sqlSession = sqlSessionFactory.openSession();
    23. // 4. 执行sql
    24. int count = sqlSession.insert("insertCar");
    25. System.out.println("插入几条数据:" + count);
    26. // 5. 提交(mybatis默认采用的事务管理器是JDBC,默认是不提交的,需要手动提交。)
    27. sqlSession.commit();
    28. // 6. 关闭资源(只关闭是不会提交的)
    29. sqlSession.close();
    30. }
    31. }

    以上程序运行后,看到数据库表t_car中又新增一条数据,如下(成功了):

    经过测试说明mybatis核心配置文件的名字是随意的,存放路径也是随意的。

    虽然mybatis核心配置文件的名字不是固定的,但通常该文件的名字叫做:mybatis-config.xml

    虽然mybatis核心配置文件的路径不是固定的,但通常该文件会存放到类路径当中,这样让项目的移植更加健壮。

    • 在mybatis中提供了一个类:Resources【org.apache.ibatis.io.Resources】,该类可以从类路径当中获取资源,我们通常使用它来获取输入流InputStream,代码如下
    1. // 这种方式只能从类路径当中获取资源,也就是说mybatis-config.xml文件需要在类路径下。
    2. InputStream is = Resources.getResourceAsStream("mybatis-config.xml");

    2.5 MyBatis第一个比较完整的代码写法

    MyBatisCompleteCodeTest.java

    1. package com.dong.mybatis;
    2. import org.apache.ibatis.io.Resources;
    3. import org.apache.ibatis.session.SqlSession;
    4. import org.apache.ibatis.session.SqlSessionFactory;
    5. import org.apache.ibatis.session.SqlSessionFactoryBuilder;
    6. import java.io.IOException;
    7. /**
    8. * 比较完整的第一个mybatis程序写法
    9. * @author captain_dong
    10. * @since 1.0
    11. * @version 1.0
    12. */
    13. public class MyBatisCompleteCodeTest {
    14. public static void main(String[] args) {
    15. SqlSession sqlSession = null;
    16. try {
    17. // 1.创建SqlSessionFactoryBuilder对象
    18. SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
    19. // 2.创建SqlSessionFactory对象
    20. SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"));
    21. // 3.创建SqlSession对象
    22. sqlSession = sqlSessionFactory.openSession();
    23. // 4.执行SQL
    24. int count = sqlSession.insert("insertCar");
    25. System.out.println("更新了几条记录:" + count);
    26. // 5.提交
    27. sqlSession.commit();
    28. } catch (Exception e) {
    29. // 回滚
    30. if (sqlSession != null) {
    31. sqlSession.rollback();
    32. }
    33. e.printStackTrace();
    34. } finally {
    35. // 6.关闭
    36. if (sqlSession != null) {
    37. sqlSession.close();
    38. }
    39. }
    40. }
    41. }

    运行后数据库表的变化:

    2.6 引入JUnit

    • JUnit是专门做单元测试的组件。
        • 在实际开发中,单元测试一般是由我们Java程序员来完成的。
        • 我们要对我们自己写的每一个业务方法负责任,要保证每个业务方法在进行测试的时候都能通过。
        • 测试的过程中涉及到两个概念:
          • 期望值
          • 实际值
        • 期望值和实际值相同表示测试通过,期望值和实际值不同则单元测试执行时会报错。
      • 这里引入JUnit是为了代替main方法。
      • 使用JUnit步骤:
        • 第一步:引入依赖
    1. <dependency>
    2. <groupId>junitgroupId>
    3. <artifactId>junitartifactId>
    4. <version>4.13.2version>
    5. <scope>testscope>
    6. dependency>
    • 第二步:编写单元测试类【测试用例】,测试用例中每一个测试方法上使用@Test注解进行标注。
    • 测试用例的名字以及每个测试方法的定义都是有规范的:
      • 测试用例的名字:XxxTest
      • 测试方法声明格式:public void test业务方法名(){}
    1. // 测试用例
    2. public class CarMapperTest{
    3. // 测试方法
    4. @Test
    5. public void testInsert(){}
    6. @Test
    7. public void testUpdate(){}
    8. }
      • 第三步:可以在类上执行,也可以在方法上执行
        • 在类上执行时,该类中所有的测试方法都会执行。
        • 在方法上执行时,只执行当前的测试方法。
    • 编写一个测试用例,来测试insertCar业务
    1. package com.dong.mybatis;
    2. import org.apache.ibatis.io.Resources;
    3. import org.apache.ibatis.session.SqlSession;
    4. import org.apache.ibatis.session.SqlSessionFactory;
    5. import org.apache.ibatis.session.SqlSessionFactoryBuilder;
    6. import org.junit.Test;
    7. public class CarMapperTest {
    8. @Test
    9. public void testInsertCar(){
    10. SqlSession sqlSession = null;
    11. try {
    12. // 1.创建SqlSessionFactoryBuilder对象
    13. SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
    14. // 2.创建SqlSessionFactory对象
    15. SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"));
    16. // 3.创建SqlSession对象
    17. sqlSession = sqlSessionFactory.openSession();
    18. // 4.执行SQL
    19. int count = sqlSession.insert("insertCar");
    20. System.out.println("更新了几条记录:" + count);
    21. // 5.提交
    22. sqlSession.commit();
    23. } catch (Exception e) {
    24. // 回滚
    25. if (sqlSession != null) {
    26. sqlSession.rollback();
    27. }
    28. e.printStackTrace();
    29. } finally {
    30. // 6.关闭
    31. if (sqlSession != null) {
    32. sqlSession.close();
    33. }
    34. }
    35. }
    36. }

    执行单元测试,查看数据库表的变化:

    2.7 引入日志框架logback

    • 引入日志框架的目的是为了看清楚mybatis执行的具体sql。
    • 启用标准日志组件,只需要在mybatis-config.xml文件中添加以下配置:【可参考mybatis手册】

    mybatis-config.xml

    1. <settings>
    2. <setting name="logImpl" value="STDOUT_LOGGING" />
    3. settings>

    标准日志也可以用,但是配置不够灵活,可以集成其他的日志组件,例如:log4j,logback等。

    • logback是目前日志框架中性能较好的,较流行的,所以我们选它。
    • 引入logback的步骤:
      • 第一步:引入logback相关依赖

    logback依赖:

    1. <dependency>
    2. <groupId>ch.qos.logbackgroupId>
    3. <artifactId>logback-classicartifactId>
    4. <version>1.2.11version>
    5. <scope>testscope>
    6. dependency>
      • 第二步:引入logback相关配置文件(文件名叫做logback.xml或logback-test.xml,放到类路径当中)

     logback.xml:

    1. "1.0" encoding="UTF-8"?>
    2. <configuration debug="false">
    3. <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    4. <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
    5. <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%npattern>
    6. encoder>
    7. appender>
    8. <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
    9. <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
    10. <FileNamePattern>${LOG_HOME}/TestWeb.log.%d{yyyy-MM-dd}.logFileNamePattern>
    11. <MaxHistory>30MaxHistory>
    12. rollingPolicy>
    13. <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
    14. <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%npattern>
    15. encoder>
    16. <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
    17. <MaxFileSize>100MBMaxFileSize>
    18. triggeringPolicy>
    19. appender>
    20. <logger name="com.apache.ibatis" level="TRACE"/>
    21. <logger name="java.sql.Connection" level="DEBUG"/>
    22. <logger name="java.sql.Statement" level="DEBUG"/>
    23. <logger name="java.sql.PreparedStatement" level="DEBUG"/>
    24. <root level="DEBUG">
    25. <appender-ref ref="STDOUT"/>
    26. <appender-ref ref="FILE"/>
    27. root>
    28. configuration>
    • 再次执行单元测试方法testInsertCar,查看控制台是否有sql语句输出

    2.8 MyBatis工具类SqlSessionUtil的封装

    • 每一次获取SqlSession对象代码太繁琐,封装一个工具类

    SqlSessionUtil.java:

    1. package com.dong.mybatis.utils;
    2. import org.apache.ibatis.io.Resources;
    3. import org.apache.ibatis.session.SqlSession;
    4. import org.apache.ibatis.session.SqlSessionFactory;
    5. import org.apache.ibatis.session.SqlSessionFactoryBuilder;
    6. /**
    7. * MyBatis工具类
    8. *
    9. * @author Captain_dong
    10. * @version 1.0
    11. * @since 1.0
    12. */
    13. public class SqlSessionUtil {
    14. private static SqlSessionFactory sqlSessionFactory;
    15. /**
    16. * 类加载时初始化sqlSessionFactory对象
    17. */
    18. static {
    19. try {
    20. SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
    21. sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"));
    22. } catch (Exception e) {
    23. e.printStackTrace();
    24. }
    25. }
    26. /**
    27. * 每调用一次openSession()可获取一个新的会话,该会话支持自动提交。
    28. *
    29. * @return 新的会话对象
    30. */
    31. public static SqlSession openSession() {
    32. return sqlSessionFactory.openSession(true);
    33. }
    34. }
    • 测试工具类,将testInsertCar()改造
    1. @Test
    2. public void testInsertCar(){
    3. SqlSession sqlSession = SqlSessionUtil.openSession();
    4. // 执行SQL
    5. int count = sqlSession.insert("insertCar");
    6. System.out.println("插入了几条记录:" + count);
    7. sqlSession.close();
    8. }

    三、使用MyBatis完成CRUD

    • 准备工作
      • 创建module(Maven的普通Java模块):mybatis-002-crud
      • pom.xml
        • 打包方式jar
        • 依赖:
          • mybatis依赖
          • mysql驱动依赖
          • junit依赖
          • logback依赖
      • mybatis-config.xml放在类的根路径下
      • CarMapper.xml放在类的根路径下
      • logback.xml放在类的根路径下
      • 提供com.powernode.mybatis.utils.SqlSessionUtil工具类
      • 创建测试用例:com.powernode.mybatis.CarMapperTest

    3.1 insert(Create)

    分析以下SQL映射文件中SQL语句存在的问题

    CarMapper.xml

    1. "1.0" encoding="UTF-8" ?>
    2. mapper
    3. PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    4. "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    5. <mapper namespace="car">
    6. <insert id="insertCar">
    7. insert into t_car(car_num,brand,guide_price,produce_time,car_type) values('103', '奔驰E300L', 50.3, '2022-01-01', '燃油车')
    8. insert>
    9. mapper>

    存在的问题是:SQL语句中的值不应该写死,值应该是用户提供的。之前的JDBC代码是这样写的:

    JDBC:

    1. // JDBC中使用 ? 作为占位符。那么MyBatis中会使用什么作为占位符呢?
    2. String sql = "insert into t_car(car_num,brand,guide_price,produce_time,car_type) values(?,?,?,?,?)";
    3. // ......
    4. // 给 ? 传值。那么MyBatis中应该怎么传值呢?
    5. ps.setString(1,"103");
    6. ps.setString(2,"奔驰E300L");
    7. ps.setDouble(3,50.3);
    8. ps.setString(4,"2022-01-01");
    9. ps.setString(5,"燃油车");

    在MyBatis中可以这样做:

            在Java程序中,将数据放到Map集合中

            在sql语句中使用 #{map集合的key} 来完成传值,#{} 等同于JDBC中的 ? ,#{}就是占位符

    Java程序这样写:

    CarMapperTest.testInsertCar():

    1. package com.dong.mybatis;
    2. import com.powernode.mybatis.utils.SqlSessionUtil;
    3. import org.apache.ibatis.session.SqlSession;
    4. import org.junit.Test;
    5. import java.util.HashMap;
    6. import java.util.Map;
    7. /**
    8. * 测试MyBatis的CRUD
    9. * @author captian_dong
    10. * @version 1.0
    11. * @since 1.0
    12. */
    13. public class CarMapperTest {
    14. @Test
    15. public void testInsertCar(){
    16. // 准备数据
    17. Map map = new HashMap<>();
    18. map.put("k1", "103");
    19. map.put("k2", "奔驰E300L");
    20. map.put("k3", 50.3);
    21. map.put("k4", "2020-10-01");
    22. map.put("k5", "燃油车");
    23. // 获取SqlSession对象
    24. SqlSession sqlSession = SqlSessionUtil.openSession();
    25. // 执行SQL语句(使用map集合给sql语句传递数据)
    26. int count = sqlSession.insert("insertCar", map);
    27. System.out.println("插入了几条记录:" + count);
    28. }
    29. }

    SQL语句这样写:

            CarMapper.xml:

    1. "1.0" encoding="UTF-8" ?>
    2. mapper
    3. PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    4. "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    5. <mapper namespace="car">
    6. <insert id="insertCar">
    7. insert into t_car(car_num,brand,guide_price,produce_time,car_type) values(#{k1},#{k2},#{k3},#{k4},#{k5})
    8. insert>
    9. mapper>

    #{} 的里面必须填写map集合的key,不能随便写。

    运行测试程序,查看数据库:

    如果#{}里写的是map集合中不存在的key会有什么问题?

            CarMapper.xml:

    1. "1.0" encoding="UTF-8" ?>
    2. mapper
    3. PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    4. "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    5. <mapper namespace="car">
    6. <insert id="insertCar">
    7. insert into t_car(car_num,brand,guide_price,produce_time,car_type) values(#{kk},#{k2},#{k3},#{k4},#{k5})
    8. insert>
    9. mapper>

    运行程序:

    通过测试,看到程序并没有报错。正常执行。不过 #{kk} 的写法导致无法获取到map集合中的数据,最终导致数据库表car_num插入了NULL。

    在以上sql语句中,可以看到#{k1} #{k2} #{k3} #{k4} #{k5}的可读性太差,为了增强可读性,我们可以将Java程序做如下修改:

            CarMapperTest.testInsertCar():

    1. Map map = new HashMap<>();
    2. // 让key的可读性增强
    3. map.put("carNum", "103");
    4. map.put("brand", "奔驰E300L");
    5. map.put("guidePrice", 50.3);
    6. map.put("produceTime", "2020-10-01");
    7. map.put("carType", "燃油车");

    SQL语句做如下修改,这样可以增强程序的可读性:

            CarMapper.xml:

    1. "1.0" encoding="UTF-8" ?>
    2. mapper
    3. PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    4. "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    5. <mapper namespace="car">
    6. <insert id="insertCar">
    7. insert into t_car(car_num,brand,guide_price,produce_time,car_type) values(#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType})
    8. insert>
    9. mapper>

    运行程序,查看数据库表:

    使用Map集合可以传参,那使用pojo(简单普通的java对象)可以完成传参吗?测试一下:

    • 第一步:定义一个pojo类Car,提供相关属性。

    Car.java:

    1. package com.dong.mybatis.pojo;
    2. /**
    3. * POJOs,简单普通的Java对象。封装数据用的。
    4. * @author captain_dong
    5. * @version 1.0
    6. * @since 1.0
    7. */
    8. public class Car {
    9. private Long id;
    10. private String carNum;
    11. private String brand;
    12. private Double guidePrice;
    13. private String produceTime;
    14. private String carType;
    15. @Override
    16. public String toString() {
    17. return "Car{" +
    18. "id=" + id +
    19. ", carNum='" + carNum + '\'' +
    20. ", brand='" + brand + '\'' +
    21. ", guidePrice=" + guidePrice +
    22. ", produceTime='" + produceTime + '\'' +
    23. ", carType='" + carType + '\'' +
    24. '}';
    25. }
    26. public Car() {
    27. }
    28. public Car(Long id, String carNum, String brand, Double guidePrice, String produceTime, String carType) {
    29. this.id = id;
    30. this.carNum = carNum;
    31. this.brand = brand;
    32. this.guidePrice = guidePrice;
    33. this.produceTime = produceTime;
    34. this.carType = carType;
    35. }
    36. public Long getId() {
    37. return id;
    38. }
    39. public void setId(Long id) {
    40. this.id = id;
    41. }
    42. public String getCarNum() {
    43. return carNum;
    44. }
    45. public void setCarNum(String carNum) {
    46. this.carNum = carNum;
    47. }
    48. public String getBrand() {
    49. return brand;
    50. }
    51. public void setBrand(String brand) {
    52. this.brand = brand;
    53. }
    54. public Double getGuidePrice() {
    55. return guidePrice;
    56. }
    57. public void setGuidePrice(Double guidePrice) {
    58. this.guidePrice = guidePrice;
    59. }
    60. public String getProduceTime() {
    61. return produceTime;
    62. }
    63. public void setProduceTime(String produceTime) {
    64. this.produceTime = produceTime;
    65. }
    66. public String getCarType() {
    67. return carType;
    68. }
    69. public void setCarType(String carType) {
    70. this.carType = carType;
    71. }
    72. }
    • 第二步:Java程序

    CarMapperTest.testInsertCarByPOJO()

    1. @Test
    2. public void testInsertCarByPOJO(){
    3. // 创建POJO,封装数据
    4. Car car = new Car();
    5. car.setCarNum("103");
    6. car.setBrand("奔驰C200");
    7. car.setGuidePrice(33.23);
    8. car.setProduceTime("2020-10-11");
    9. car.setCarType("燃油车");
    10. // 获取SqlSession对象
    11. SqlSession sqlSession = SqlSessionUtil.openSession();
    12. // 执行SQL,传数据
    13. int count = sqlSession.insert("insertCarByPOJO", car);
    14. System.out.println("插入了几条记录" + count);
    15. }
    • 第三步:SQL语句
    1. <insert id="insertCarByPOJO">
    2. insert into t_car(car_num,brand,guide_price,produce_time,car_type) values(#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType})
    3. insert>
    • 运行程序,查看数据库表:

    #{} 里写的是POJO的属性名,如果写成其他的会有问题吗?

            CarMapper.xml:

    1. <insert id="insertCarByPOJO">
    2. insert into t_car(car_num,brand,guide_price,produce_time,car_type) values(#{a},#{brand},#{guidePrice},#{produceTime},#{carType})
    3. insert>

    运行程序,出现了以下异常:

    错误信息中描述:在Car类中没有找到a属性的getter方法。

    修改POJO类Car的代码,只将getCarNum()方法名修改为getA(),其他代码不变,如下:

    再运行程序,查看数据库表中数据:

    经过测试得出结论:

    如果采用map集合传参,#{} 里写的是map集合的key,如果key不存在不会报错,数据库表中会插入NULL。

    如果采用POJO传参,#{} 里写的是get方法的方法名去掉get之后将剩下的单词首字母变小写(例如:getAge对应的是#{age},getUserName对应的是#{userName}),如果这样的get方法不存在会报错。

    注意:其实传参数的时候有一个属性parameterType,这个属性用来指定传参的数据类型,不过这个属性是可以省略的

    CarMapper.xml:

    1. <insert id="insertCar" parameterType="java.util.Map">
    2. insert into t_car(car_num,brand,guide_price,produce_time,car_type) values(#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType})
    3. insert>
    4. <insert id="insertCarByPOJO" parameterType="com.powernode.mybatis.pojo.Car">
    5. insert into t_car(car_num,brand,guide_price,produce_time,car_type) values(#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType})
    6. insert>

  • 相关阅读:
    【时间序列异常检测】Anomaly Transformer论文笔记
    142 环形链表II
    Appium自动化测试基础 — APPium基础操作API(一)
    工业风格大屏:让数据更生动,直观更简单
    云服务器玲琅满目的时代,为什么我独爱Amazon EC2 云服务器?
    Word控件Spire.Doc 【表单域】教程(五):如何在 C# 中更新 Ask 字段
    手机呼叫转移怎么设置
    常用的表单校验规则——邮箱/QQ/身份证号码/微信/电话/数字字母/整数/文本/密码等
    Oracle/PLSQL: Cos Function
    Rust和Pytho写一段采集公众号代码
  • 原文地址:https://blog.csdn.net/m0_63064861/article/details/132922851