0%

Mybatis CRUD

  • 使用MyBatis完成CRUD:
    • Create
    • Retrieve
    • Update
    • Delete

三、使用MyBatis完成CRUD

  • 准备工作
    • 创建 module(Maven 的普通 Java 模块):mybatis-002-crud
    • pom.xml
      • 打包方式 jar
      • 依赖:
        • mybatis 依赖
        • mysql 依赖
        • JUnit 依赖
        • logback 依赖
    • mybatis-config.xml 放在类的根路径下(放在 resources 目录下)
    • CarMapper.xml 放在类的根路径下(放在 resources 目录下)
    • logback.xml 放在类的根路径下(放在 resources 目录下)
    • 提供 com.f.mybatis.utils.SqlSessionUtil 工具类

3.1 ★insert (Create)

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <!-- 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">
    <mapper namespace="car">
    <!-- insert语句, id是这条sql语句的唯一标识,这个id就代表了这条sql语句-->
    <insert id="insertCar">INSERT INTO t_car(id, car_num, brand, guide_price, produce_time, car_type)
    VALUES (null, '1003', '问界', 35.00, '2023-10-10', '新能源')</insert>
    </mapper>

    存在的问题是:SQL 语句中的值不应该写死,值应该是用户提供的。

  • 类似于 JDBC,**在 Mybatis 中,用 #{} 来充当占位符,作用类似于 JDBC 中的 ?**。

    • 在 MyBatis 中可以这样做:
      • 在 Java 程序中,将数据放到 Map 集合中,然后在 sql 语句中使用 #{map集合的key} 来完成传值
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    package com.f.mybatis.test;

    import com.f.mybatis.utils.SqlSessionUtil;
    import org.apache.ibatis.session.SqlSession;
    import org.junit.Test;

    import java.util.HashMap;
    import java.util.Map;

    /**
    * @author fzy
    * @date 2023/12/31 21:33
    */
    public class CarMapperTest {
    @Test
    public void testInsertCar() {
    SqlSession sqlSession = SqlSessionUtil.openSession();
    Map<String, Object> map = new HashMap<>();
    map.put("k1", "1003");
    map.put("k2", "问界");
    map.put("k3", "41.00");
    map.put("k4", "2023-11-11");
    map.put("k5", "新能源");

    // insert方法的参数:
    // 第一个参数:sqlId,从 CarMapper.xml 文件中复制
    // 第二个参数:封装数据的对象
    sqlSession.insert("insertCar", map);
    sqlSession.commit();
    sqlSession.close();
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    <!-- 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">
    <mapper namespace="car">
    <!-- insert语句, id是这条sql语句的唯一标识,这个id就代表了这条sql语句-->
    <insert id="insertCar">INSERT INTO t_car(id, car_num, brand, guide_price, produce_time, car_type) VALUES (null, #{k1}, #{k2}, #{k3}, #{k4}, #{k5})</insert>
    </mapper>
    • 如果 #{} 里写的是 map 集合中不存在的 key 会有什么问题?例如

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      ...
      Map<String, Object> map = new HashMap<>();
      map.put("k1", "1003");
      map.put("k2", "问界");
      map.put("k3", 41.00);
      map.put("k4", "2023-11-11");
      map.put("k5", "新能源");

      sqlSession.insert("insertCar", map);
      ...
      1
      <insert id="insertCar">INSERT INTO t_car(id, car_num, brand, guide_price, produce_time, car_type) VALUES (null, #{kkkkkk}, #{k2}, #{k3}, #{k4}, #{k5})</insert>
      • 经过测试,看到程序并没有报错。正常执行。不过 #{kkkkkk} 的写法导致无法获取到 map 集合中的数据,最终导致数据库表 car_num 插入了 NULL。

思考

  • 使用 Map 集合可以传参,那使用 pojo(简单普通的 java 对象)可以完成传参吗?–> 答案是可以的。

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

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73
      74
      75
      76
      77
      78
      79
      80
      81
      82
      83
      84
      85
      86
      87
      88
      package com.f.mybatis.bean;

      /**
      * @author fzy
      * @date 2023/12/31 22:06
      * 封装汽车相关信息的javabean类。普通的的java类。
      */
      public class Car {
      // javabean 的属性应该和数据表中的字段一一对应
      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;
      }

      @Override
      public String toString() {
      return "Car{" +
      "id=" + id +
      ", carNum='" + carNum + '\'' +
      ", brand='" + brand + '\'' +
      ", guidePrice=" + guidePrice +
      ", produceTime='" + produceTime + '\'' +
      ", 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;
      }
      }
    • 第二步:Java 程序:

      1
      2
      3
      4
      5
      6
      7
      8
      @Test
      public void testInsertCarByPojo() {
      SqlSession sqlSession = SqlSessionUtil.openSession();
      Car car = new Car(null, "1004", "比亚迪", 33.23, "2020-10-01", "燃油车");
      sqlSession.insert("insertCar", car);
      sqlSession.commit();
      sqlSession.close();
      }
    • 第三步:xml 配置文件:

      1
      2
      3
      4
      5
      6
      7
      8
      <?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="car">
      <!-- #{}里写的是POJO的属性名 -->
      <insert id="insertCar">INSERT INTO t_car(id, car_num, brand, guide_price, produce_time, car_type) VALUES (null, #{carNum}, #{brand}, #{guidePrice}, #{produceTime}, #{carType})</insert>
      </mapper>
    • 第四步:执行 java 程序,成功插入数据。

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

    1
    <insert id="insertCar">INSERT INTO t_car(id, car_num, brand, guide_price, produce_time, car_type) VALUES (null, #{xyz}, #{brand}, #{guidePrice}, #{produceTime}, #{carType})</insert>

    运行程序会出现异常,错误信息中描述:在 Car 类中没有找到 xyz 属性的 getter 方法。

    There is no getter for property named 'xyz' in 'class com.f.mybatis.bean.Car'

    • 如果修改 POJO 类 Car 的代码,只将 getCarNum() 方法名修改为 getXyz(),其他代码不变,再运行 java 程序,发现也能成功插入数据。
  • 总结

    • 如果采用 map 集合传参#{} 里写的是 map 集合的 key,如果 key 不存在,并不会报错,数据库表中会插入 NULL。
    • 如果采用 POJO 传参#{} 里写的是 get 方法的方法名去掉 get 之后将剩下的单词首字母变小写(例如:getAge 对应的是 #{age}getUserName 对应的是 #{userName}),如果这样的 get 方法不存在则会报错。
  • 注意:其实传参数的时候有一个属性 parameterType ,这个属性用来指定传参的数据类型,不过这个属性是可以省略的。

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

    <insert id="insertCarByPOJO" parameterType="com.powernode.mybatis.pojo.Car">
    insert into t_car(car_num,brand,guide_price,produce_time,car_type) values(#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType})
    </insert>

3.2 delete (Delete)

  • 需求:根据 car_num 进行删除。

    • sql 语句这样写:

      1
      2
      3
      4
      5
      6
      7
      <?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="car">
      <delete id="deleteCarByNum">DELETE FROM t_car WHERE car_num = #{SuiBianXie}</delete>
      </mapper>
    • java 程序这样写:

      1
      2
      3
      4
      5
      6
      7
      @Test
      public void testDeleteCarByNum() {
      SqlSession sqlSession = SqlSessionUtil.openSession();
      sqlSession.delete("deleteCarByNum", "1003");
      sqlSession.commit();
      sqlSession.close();
      }

    注意:当占位符只有一个的时候,#{} 里面的内容可以随便写,Mybatis 会自动识别并填入参数。但是最好见名知意。

3.3 update (Update)

  • 需求:修改 car_num 为 1001 的 Car 信息,将其 guide_price 改为 50.00 万元。

    • sql 语句这样写:

      1
      2
      3
      4
      5
      6
      7
      <?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="car">
      <update id="updateCar">UPDATE t_car SET guide_price = #{guidePrice} WHERE car_num = #{carNum}</update>
      </mapper>
    • java 程序这样写:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      @Test
      public void testUpdateCar() {
      SqlSession sqlSession = SqlSessionUtil.openSession();
      Car car = new Car();
      car.setCarNum("1001");
      car.setGuidePrice(50.00);
      sqlSession.update("updateCar", car);
      sqlSession.commit();
      sqlSession.close();
      }

    当然,如果使用 map 传数据也是可以的。

3.4 ★select (Retrieve)

  • select 语句和其它语句不同的是:查询会有一个结果集。来看 mybatis 是怎么处理结果集的。

3.4.1 查询一条数据 selectOne

resultType 属性
  • 需求:查询 id 为 1 的 Car 信息。

    • sql 语句这样写:

      1
      2
      3
      4
      5
      6
      7
      <?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="car">
      <select id="selectCarById">SELECT car_num, brand, guide_price, produce_time, car_type FROM t_car WHERE id = #{id}</select>
      </mapper>
    • java 程序这样写:

      1
      2
      3
      4
      5
      6
      7
      @Test
      public void testSelectCarById() {
      SqlSession sqlSession = SqlSessionUtil.openSession();
      Car car = sqlSession.selectOne("selectCarById", 1);
      System.out.println(car);
      sqlSession.close();
      }
    • 运行结果出现异常:A query was run and no Result Maps were found for the Mapped Statement 'car.selectCarById'. It's likely that neither a Result Type nor a Result Map was specified.

      也就是说,对于一个查询语句,你需要指定它的“结果类型”或者“结果映射”,mybatis 才能知道要将查询的结果封装为什么类型的对象。

      • 因此,如果你想让 mybatis 查询之后返回一个 Java 对象的话,至少你要告诉 mybatis 返回一个什么类型的 Java 对象,可以<select> 标签中添加 resultType 属性,用来指定查询结果要转换的类型:

        1
        <select id="selectCarById" resultType="com.f.mybatis.bean.Car">SELECT car_num, brand, guide_price, produce_time, car_type FROM t_car WHERE id = #{id}</select>

        resultType 通常写的是全限定类名。

  • 运行后之前的异常不再出现了,这说明添加了 resultType 属性之后,解决了之前的异常,可以看出 resultType 是不能省略的。

    但是奇怪的是返回的 Car 对象,只有 brand 属性有值,其它属性的值都是 null,这是为什么呢?

    Car{id=null, carNum='null', brand='宝马', guidePrice=null, produceTime='null', carType='null'}

    • 我们来观察一下查询结果列名和 Car 类的属性名是否能一一对应:

      • 查询结果集的列名:car_num, brand, guide_price, produce_time, car_type
      • Car 类的属性名:carNum, brand, guidePrice, produceTime, carType

      观察发现:只有 brand 是一致的,其他字段名和属性名对应不上,这就是导致 null 的原因。

      • 为了解决这个问题,可以在 sql 语句中采用 as 关键字起别名:

        1
        <select id="selectCarById" resultType="com.f.mybatis.bean.Car">SELECT car_num as carNum, brand, guide_price as guidePrice, produce_time as produceTime, car_type as carType FROM t_car WHERE id = #{id}</select>

        此时返回的 Car 对象就为:Car{id=null, carNum='1001', brand='宝马', guidePrice=50.0, produceTime='2022-10-10', carType='燃油车'}

        当然还有其它解决方案,我们后面再看。

3.4.2 查询多条数据 selectList

  • 需求:查询所有 Car 的信息。

    • sql 语句这样写:

      1
      2
      3
      4
      5
      6
      7
      8
      <?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="car">
      <!-- 虽然结果是List集合,但是resultType属性需要指定的是List集合中元素的类型。 -->
      <select id="selectCar" resultType="com.f.mybatis.bean.Car">SELECT car_num as carNum, brand, guide_price as guidePrice, produce_time as produceTime, car_type as carType FROM t_car</select>
      </mapper>
    • java 程序这样写:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      @Test
      public void testSelectCar() {
      SqlSession sqlSession = SqlSessionUtil.openSession();
      List<Car> cars = sqlSession.selectList("selectCar");
      for (Car car : cars) {
      System.out.println(car);
      }
      sqlSession.close();
      }
---------------The End---------------