SpringBoot整合Mybatis详解,看这篇就够了

96 阅读12分钟

1、Mybatis简介

Mybatis 是一个优秀的基于 java 的持久层框架,它内部封装了 jdbc,使开发者只需要关注 sql 语句本身,而不需要花费精力去处理加载驱动、创建连接、创建 statement 等繁杂的过程。 Mybatis 通过 xml 或注解的方式将要执行的各种 statement 配置起来,并通过 java 对象和 statement 中sql 的动态参数进行映射生成最终执行的 sql 语句,最后由 mybatis 框架执行 sql 并将结果映射为 java 对象并返回。 采用 ORM 思想解决了实体和数据库映射的问题,对 jdbc 进行了封装,屏蔽了 jdbc api 底层访问细节,使我们不用与 jdbc api 打交道,就可以完成对数据库的持久化操作。

2、SpringBoot集成Mybatis步骤

2.1、在pom.xml文件中导入相关依赖

Mybatis相关依赖:

   <dependency>
       <groupId>org.mybatis.spring.boot</groupId>
       <artifactId>mybatis-spring-boot-starter</artifactId>
       <version>2.1.4</version>
   </dependency>

MySQL相关依赖:(版本为8)

   <dependency>
       <groupId>mysql</groupId>
       <artifactId>mysql-connector-java</artifactId>
       <version>8.0.15</version>
       <scope>runtime</scope>
   </dependency>

lombok相关依赖:

   <dependency>
       <groupId>org.projectlombok</groupId>
       <artifactId>lombok</artifactId>
       <version>1.18.12</version>
   </dependency>

2.2、在application.yml中配置相关信息

添加MySQL配置信息:

# 数据源配置
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver # MySQL数据库驱动
url: jdbc:mysql://localhost:3306/mybatislearning?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=UTC  # 该url仅供参考,根据实际情况进行修改(一般情况下,没有问题)
username: root # MySQL用户名
password: 123456 # MySQL密码
# 数据库连接池的配置  druid是更优秀的数据库连接池
# Spring Boot 2.0 以上默认使用 com.zaxxer.hikari.HikariDataSource 数据连接池,此配置切换数据库连接池
# type: com.alibaba.druid.pool.DruidDataSource

注意:

在修改数据库连接池时,一定要在pom文件中添加Druid依赖

   <!--引入druid数据源-->
        <!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
   <dependency>
       <groupId>com.alibaba</groupId>
       <artifactId>druid</artifactId>
       <version>1.1.23</version>
   </dependency>

添加Mybatis配置信息:

mybatis:
# 指定XXXMapper.xml文件的路径
# 当mapper接口(dao接口)和mapper接口对应的配置文件 命名上不同或所在的路径不同 (满足其中之一不同) 此配置生效
# 当mapper接口(dao接口)和mapper接口对应的配置文件 命名上和所在的路径都相同 (同时满足两个条件) 则mapper-locations可以不用配置,配置不会出错,也不会生效。
mapper-locations: classpath:mapper/*.xml
# 配置实体类所在的包(pojo)mybatis自动扫描到自定义实体类(pojo)
type-aliases-package: com.chen.mybatislearning.pojo

在配置好type-aliases-package后,我们在XXXMapper.xml映射文件的type、parameterType、resultType这些属性中,直接写实体类名,而不需要写上全限定类名。(简化我们XXXMapper.xml映射文件的配置)

未添加type-aliases-package配置之前:

2.png

添加type-aliases-package配置之后:

1.png

总结: 这样就可以简化XXXMapper.xml映射文件的配置。(当然,在添加type-aliases-package配置之后, 不去修改XXXMapper.xml信息也是没问题的)

2.3、在启动类上添加@MapperScan注解

作用:指定要扫描的Mapper(Dao)类所在的包的路径。这样Mapper(Dao)包下的接口,就会与XXXMapper.xml映射文件一一对应起来(通过namespace属性,产生对应关系)。并且该包下的接口就会在编译之后自动生成相应的实现类。

具体实例:

3.png

解释:com.chen.mybatislearning.dao为Mapper(Dao)包所在的路径。

2.4、在resources目录下新建xml配置文件

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">
<!--namespace中填写mapper(dao)层接口的全限定类名,表示与哪个接口对应-->
<mapper namespace="">
</mapper>

3、XML映射文件

要点:

  • mapper接口中的方法名和mapper.xml中statement的id一致
  • mapper接口中的方法输入参数类型和mapper.xml中statement的parameterType指定的类型一致。
  • mapper接口中的方法返回值类型和mapper.xml中statement的resultType指定的类型一致。

3.1、输入映射

通过parameterType指定输入参数的类型,类型可以是:

  • 简单类型(常用)
  • hashmap
  • pojo的包装类型(常用)

简单类型:

parameterType指定输入参数的类型(Integer,Double,String等等)

<!--id为mapper接口中的方法名-->    
<select id="findUserById" parameterType="Integer" resultType="com.iot.mybatis.po.User">
   <!--#{id}中的id为mapper接口中方法的参数名,两者要保持一致-->
       SELECT * FROM  user  WHERE id=#{id}
   </select>

pojo的包装类型:

com.iot.mybatis.po.User为User类的全限定类名

<!--id为mapper接口中的方法名-->     
<update id="updateUser" parameterType="com.iot.mybatis.po.User">
   <!--以username=#{username}为例,username为数据库表的字段的名称,#{username}为实体类属性名-->
       update user set username=#{username},birthday=#{birthday},sex=#{sex},address=#{address}
       where id=#{id}
   </update>

3.2、输出映射

输出映射有两种方式

  • resultType
  • resultMap

resultType方式

  • 使用resultType进行输出映射,只有查询出来的列名和pojo中的属性名一致,该列才可以映射成功。
  • 如果查询出来的列名和pojo中的属性名全部不一致,没有创建pojo对象。
  • 只要查询出来的列名和pojo中的属性有一个一致,就会创建pojo对象。

mapper.xml文件

<!--如果查询出来的列名和pojo中的属性名一致,就直接写实体类名(实体类的全限定类名)-->  
<select id="testIfAndWhere"
   parameterType="String"
   resultType="user">
   select * from user where sex=#{sex}
 </select>

resultMap方式

  • 使用resultMap完成高级输出结果映射。(一对多,多对多)

  • 如果查询出来的列名和pojo的属性名不一致,通过定义一个resultMap对列名和pojo属性名之间作一个映射关系。

(1)定义resultMap

前提条件:user实体类属性名为id,username 数据库表USER的列名为id_ ,username_

<!-- 定义resultMap
    将数据库表的列名和User类中的属性作一个映射关系
    相当于实现了 SELECT id id_,username username_ FROM USER 这样一段sql语句
    type:resultMap最终映射的java对象类型,可以使用别名
    id:对resultMap的唯一标识
     -->
     <resultMap type="user" id="userResultMap">
        <!-- id表示查询结果集中唯一标识 
        column:查询出来的列名
        property:type指定的pojo类型中的属性名
        最终resultMap对column和property作一个映射关系 (对应关系)
        -->
        <id column="id_" property="id"/>
        <!-- 
        result:对普通名映射定义
        column:查询出来的列名
        property:type指定的pojo类型中的属性名
        最终resultMap对column和property作一个映射关系 (对应关系)
         -->
        <result column="username_" property="username"/></resultMap>

(2)使用resultMap作为statement的输出映射类型

<!-- 使用resultMap进行输出映射
        resultMap:指定定义的resultMap的id,如果这个resultMap在其它的mapper文件,前边需要加namespace
        -->
    <select id="findUserByIdResultMap" parameterType="int" resultMap="userResultMap">
        SELECT id_, username_ FROM USER WHERE id=#{id}
    </select>

4、动态SQL

静态SQL存在的问题

举个例子: 如果我们要根据 username 和 sex 来查询数据。如果username为空,那么将只根据sex来查询;如果sex为空,只根据username来查询。如果username和sex都为空,那就返回所有信息。(要求)

<select id="selectUserByUsernameAndSex"
   resultType="user" parameterType="com.ys.po.User">
select * from user where username=#{username} and sex=#{sex}
</select>

上面的查询语句,我们可以发现,如果 #{username} 或者#{sex}为空,那么查询结果也是空,如何解决这个问题呢?使用 if 来判断

4.1、if语句

<select id="selectUserByUsernameAndSex" resultType="user" parameterType="com.ys.po.User">
select * from user where
   <if test="username != null">
      username=#{username}
   </if>
​
   <if test="username != null">
      and sex=#{sex}
   </if>
</select>

注意: 这样写还是有问题的,会导致SQL语句的拼接出现语法错误。

这样写我们可以看到,如果 sex 等于 null,那么查询语句为 select * from user where username=#{username},但是如果usename 为空呢?那么查询语句为 select * from user where and sex=#{sex},这是错误的 SQL 语句,如何解决呢?请看下面的 where 语句

4.2、if+where语句

<select id="selectUserByUsernameAndSex" resultType="user" parameterType="com.ys.po.User">
select * from user
<where>
   <if test="username != null">
      username=#{username}
   </if>
​
   <if test="username != null">
      and sex=#{sex}
   </if>
</where>
</select> 

解释: 这个“where”标签会知道如果它包含的标签中有返回值的话,它就插入一个‘where’。此外,如果标签返回的内容是以AND 或OR 开头的,则它会剔除掉。

这样上面这个配置,就完全正确。

4.3、if+set 语句

<!-- 根据 id 更新 user 表的数据 -->
<update id="updateUserById" parameterType="com.ys.po.User">
update user u
   <set>
       <if test="username != null and username != ''">
           u.username = #{username},
       </if>
       <if test="sex != null and sex != ''">
           u.sex = #{sex}
       </if>
   </set>
​
where id=#{id}
</update>

set 标签的作用:

  1. 满足条件时, 会自动添加 set 关键字
  2. 会去除set 子句中多余的逗号
  3. 不满足条件时, 不会生成 set 关键字

解释: 这样写,如果第一个条件 username 为空,那么 sql 语句为:update user u set u.sex=? where id=?

如果第一个条件不为空,那么 sql 语句为:update user u set u.username = ? ,u.sex = ? where id=?

4.4、foreach 语句

需求:我们需要查询 user 表中 id 分别为1,2,3的用户

sql语句:select * from user where id=1 or id=2 or id=3

<select id="testForeach" parameterType="Integer" resultType="com.chen.mybatislearning.pojo.user">
    select * from user
    <where>
    <!--
    collection:指定输入对象中的集合属性
    item:每次遍历生成的对象
    open:开始遍历时的拼接字符串
    close:结束时拼接的字符串
    separator:遍历对象之间需要拼接的字符串
    select * from user where 1=1 and (id=1 or id=2 or id=3)
        -->
        <foreach collection="ids" item="id" open="and (" close=")" separator="or">
        <!-- 每个遍历需要拼接的串 -->
    id=#{id}
        </foreach>
    </where>
</select>

sql语句: select * from user where id in (1,2,3)

<select id="testForeach" parameterType="Integer" resultType="com.chen.mybatislearning.pojo.user">
select * from user where id in
    <!-- 实现  “ IN(1,2,3)”拼接 -->
    <foreach collection="ids" item="id" open="(" close=")" separator=",">
        #{id}
        </foreach>
</select>

4.5、sql片段

1.定义sql片段

<!-- 定义sql片段
id:sql片段的唯 一标识
​
经验:是基于单表来定义sql片段,这样话这个sql片段可重用性才高
在sql片段中不要包括 where
-->
<sql id="query_user_where">
    <if test="sex!=null">
        and sex=#{sex}
    </if>
    <if test="username!=null and username!=''">
        and username like concat("%",#{username},"%")
    </if>
</sql>

2.引用sql片段

<select id="testSql" parameterType="com.chen.mybatislearning.pojo.TestEntity"       resultType="com.chen.mybatislearning.pojo.user">
select * from user
<!--  where 可以自动去掉条件中的第一个and -->
<!-- 引用sql片段 的id,如果refid指定的id不在本mapper文件中,需要前边加namespace -->
    <where>
        <include  refid="query_user_where"></include>
    </where>
</select>

5、订单商品数据模型分析

现在一共有4个数据库表:

  • 用户表user:记录了购买商品的用户信息 user.png

  • 订单表orders:记录了用户所创建的订单(购买商品的订单) orders.png

  • 订单明细表orderdetail:记录了订单的详细信息即购买商品的信息 orderdetail.png

  • 商品表items:记录了商品信息 items.png

表与表之间的业务关系:

  • usre和orders:

user--->orders:一个用户可以创建多个订单,一对多 orders--->user:一个订单只由一个用户创建,一对一

  • orders和orderdetail:

orders--->orderdetail:一个订单可以包括多个订单明细,因为一个订单可以购买多个商品,每个商品的购买信息在orderdetail记录,一对多关系

orderdetail---> orders:一个订单明细只能包括在一个订单中,一对一

  • orderdetail和itesm:

orderdetail--->itesms:一个订单明细只对应一个商品信息,一对一

items---> orderdetail:一个商品可以包括在多个订单明细 ,一对多

再分析数据库级别没有关系的表之间是否有业务关系:

  • orders和items:

orders和items之间可以通过orderdetail表建立关系。

5.1、一对一查询

目的:查询订单信息关联查询用户信息

确定查询的主表:订单 确定查询的关联表:用户表

创建pojo实体类:

  • user
@Data
public class user {
   private Integer id;
   private String username;
   private LocalDate birthday;
   private String sex;
   private String address;
}
  • orders
@Data
public class orders {
​
   private Integer id;
   private Integer user_id;
   private String number;
   private LocalDate createtime;
   private String note;
    //orders类中添加User属性,将关联查询出来的用户信息映射到orders对象中的user属性中。
   private user user;
}

XML配置:

   <resultMap id="BaseResultMap" type="com.chen.mybatislearning.pojo.orders">
       <!-- 配置映射的订单信息 -->
       <!-- id:指定查询列中的唯一标识,订单信息的中的唯 一标识,如果有多个列组成唯一标识,配置多个id
           column:订单信息的唯一标识列(数据库表列名)
           property:订单信息的唯一标识列所映射到Orders中哪个属性(实体类属性名)
         -->
       <id column="id" property="id"></id>
       <result column="user_id" property="user_id"></result>
       <result column="number" property="number"></result>
       <result column="createtime" property="createtime"></result>
       <result column="note" property="note"></result>
   </resultMap>
​
   <resultMap id="OrdersUserResultMap" type="com.chen.mybatislearning.pojo.orders" extends="BaseResultMap">
       <id column="id" property="id"></id>
       <result column="user_id" property="user_id"></result>
       <result column="number" property="number"></result>
       <result column="createtime" property="createtime"></result>
       <result column="note" property="note"></result>
       <!--        配置映射的关联的用户信息-->
<!--        association:用于映射关联查询单个对象的信息   在一对一关联的情况下 使用association-->
<!--        property:要将关联查询的用户信息映射到Orders中哪个属性-->
       <association property="user" javaType="com.chen.mybatislearning.pojo.user">
<!--                     id:关联查询用户的唯 一标识-->
<!--        column:指定唯 一标识用户信息的列-->
<!--        javaType:映射到user的哪个属性-->
           <id column="id" property="id"></id>
           <result column="username" property="username"></result>
           <result column="birthday" property="birthday"></result>
           <result column="sex" property="sex"></result>
           <result column="address" property="address"></result>
       </association>
   </resultMap>

5.2、一对多查询

目的:查询订单及订单明细的信息

确定主查询表:订单表 确定关联查询表:订单明细表 用户表

在一对一查询基础上添加订单明细表关联即可。

创建pojo实体类

  • orders
@Data
public class orders {
​
   private Integer id;
   private Integer user_id;
   private String number;
   private LocalDate createtime;
   private String note;
    //在orders.java类中添加`List<orderDetail> orderdetails`属性。
    //最终会将订单信息映射到orders中,订单所对应的订单明细映射到orders中的orderdetails属性中。
   private List<orderdetail> orderdetails;
}
  • orderdetail
@Data
public class orderdetail {
   private Integer id;
   private Integer orders_id;
   private Integer items_id;
   private Integer items_num;
}
  • user
@Data
public class user {
   private Integer id;
   private String username;
   private LocalDate birthday;
   private String sex;
   private String address;
}

XML配置:

<!--OrdersUserResultMap在一对一查询的XML配置中查看-->
<resultMap id="OrdersAndOrderDetailResultMap" type="com.chen.mybatislearning.pojo.orders" extends="OrdersUserResultMap">
   <!-- 订单明细信息
   一个订单关联查询出了多条明细,要使用collection进行映射
   collection:对关联查询到多条记录映射到集合对象中
   property:将关联查询到多条记录映射到com.iot.mybatis.po.Orders哪个属性
   ofType:指定映射到list集合属性中pojo的类型
   -->
   <collection property="orderdetails" ofType="com.chen.mybatislearning.pojo.orderdetail">
    <id column="orderdetail_id" property="id"></id>
       <result column="items_id" property="items_id"></result>
       <result column="items_num" property="items_num"></result>
       <result column="orders_id" property="orders_id"></result>
   </collection>
</resultMap><select id="findOrdersAndOrderDetailResultMap" resultMap="OrdersAndOrderDetailResultMap">
       SELECT
           orders.*,
           user.username,
           user.sex,
           user.address,
           orderdetail.id orderdetail_id,
           orderdetail.items_id,
           orderdetail.items_num,
           orderdetail.orders_id
       FROM
           orders,
           user,
           orderdetail
       WHERE orders.user_id = user.id AND orderdetail.orders_id=orders.id
</select>

5.3、多对多查询

目的:查询用户及用户购买商品信息

查询主表是:用户表

关联表:由于用户和商品没有直接关联,通过订单和订单明细进行关联,所以关联表:orders、orderdetail、items

  • user
@Data
public class user {
   private Integer id;
   private String username;
   private LocalDate birthday;
   private String sex;
   private String address;
    //在user类中添加订单列表属性`List<Orders> orderslist`,将用户创建的订单映射到orderslist
   private List<orders> orderslist;
​
}
  • items
@Data
public class items {
   private Integer id;
   private String name;
   private Float price;
   private String detail;
   private String pic;
   private LocalDate createtime;
}
  • orders
@Data
public class orders {
​
   private Integer id;
   private Integer user_id;
   private String number;
   private LocalDate createtime;
   private String note;
   //在Orders中添加订单明细列表属性`List<OrderDetail>orderdetials`,将订单的明细映射到orderdetials
   private List<orderdetail> orderdetails;
}
  • orderdetail
@Data
public class orderdetail {
    private Integer id;
    private Integer orders_id;
    private Integer items_id;
    private Integer items_num;
    //在OrderDetail中添加`Items`属性,将订单明细所对应的商品映射到Items
    private items items;
}

XML配置:

<!-- 查询用户及购买的商品 -->
<resultMap type="com.chen.mybatislearning.pojo.user" id="UserAndItemsResultMap">
   <!-- 用户信息 -->
   <id column="user_id" property="id"/>
   <result column="username" property="username"/>
   <result column="sex" property="sex"/>
   <result column="address" property="address"/>
   <!-- 订单信息
   一个用户对应多个订单,使用collection映射
   -->
   <collection property="orderslist" ofType="com.chen.mybatislearning.pojo.orders">
       <id column="id" property="id"/>
       <result column="user_id" property="user_id"/>
       <result column="number" property="number"/>
       <result column="createtime" property="createtime"/>
       <result column="note" property="note"/>
​
       <!-- 订单明细
       一个订单包括 多个明细
       -->
       <collection property="orderdetails" ofType="com.chen.mybatislearning.pojo.orderdetail">
           <id column="orderdetail_id" property="id"/>
           <result column="items_id" property="items_id"/>
           <result column="items_num" property="items_num"/>
           <result column="orders_id" property="orders_id"/>
           <!-- 商品信息
           一个订单明细对应一个商品
           -->
           <association property="items" javaType="com.chen.mybatislearning.pojo.items">
               <id column="items_id" property="id"/>
               <result column="items_name" property="name"/>
               <result column="items_detail" property="detail"/>
               <result column="items_price" property="price"/>
           </association>
       </collection>
   </collection>
</resultMap><select id="findUserAndItemsResultMap" resultMap="UserAndItemsResultMap">
       SELECT
           orders.*,
           user.username,
           user.sex,
           user.address,
           orderdetail.id orderdetail_id,
           orderdetail.items_id,
           orderdetail.items_num,
           orderdetail.orders_id,
           items.name items_name,
           items.detail items_detail,
           items.price items_price
       FROM
           orders,
           user,
           orderdetail,
           items
       WHERE orders.user_id = user.id AND orderdetail.orders_id=orders.id AND orderdetail.items_id = items.id
</select>

后记

本次重点介绍了多种查询在Mybatis框架中的具体应用,增删改的使用与查询类似,甚至更加简单,后续会继续完善此内容。