MyBatis多条件查询、动态SQL、多表操作、注解开发

大家好,我是一安,随着基于注解开发越来越流行,Mybatis也可以使用注解开发方式,可以减少编写Mapper映射文件了。今天围绕一些基本的CRUD来介绍,再引入复杂映射多表操作

前言

MyBatis封装了JDBC通过Mapper代理的方式,以前繁琐的操作通过“属性与字段映射”就简单化解,MyBatis的动态SQL完美展现了DBMS的独特魅力

多条件查询

基于Mybatis的多条件查询,是在Mapper代理的映射文件中写上原有的SQL,然后接口中写一个带参的方法即可,就像这样:MyBatis多条件查询、动态SQL、多表操作、注解开发相比于原生的JDBC那一套,通过MyBatis确实解决了不少硬编码的问题

但是用户的查询永远是动态的操作,他可能在多个条件中选择其中少量条件进行查询,我们的SQL是死的,而用户需求对应的SQL却是活的,这样就会造成不匹配而形成语法错误

比如,根据这张表,若是要根据部分字段查出整体,我们可以写对应需求的SQL,但是我要是查询的条件变少了或者变多了呢?若用户只想通过一个条件来查询,那么在其他占位符的位置不输入于是成了null,过不了语法自然查不了,还得重新写SQL,多麻烦MyBatis多条件查询、动态SQL、多表操作、注解开发这个时候MyBatis的特色就体现出来了——动态SQL

SQL语句会随着用户的输入或者外部条件的变化而变化,则称之为动态SQL

if-where

因为采用了Mapper代理开发,我们可以通过写xml的形式来编写我们的SQL,动态SQL的特性也就在这一举动中所蕴育,在原有的Mapper文件里我们进行如下改造,让平平无奇的SQL焕然一新:

    <select id="selByCondition" resultMap="rm">
        select *
        from mybatis  
        <where>
        <if test="status !=null">
           and STATUS=#{STATUS}
        </if>
        <if test="companyName !=null and companyName !=''">
        and company_name like #{companyName}
        </if>
        <if test="bracdName !=null and bracdName !=''">
        and bracd_name like #{bracdName}
        </if>
        </where>
    </select>

<where>标签可以自动帮我们去掉and”

这样,不管查询的条件怎么变,我跟着这个逻辑流程走就不会出现SQL语法毛病而导致查询不出来的毛病啦,因为null的情况已经被if所过滤掉了,真是太哇塞了!

choose-when-ortherwise

对于从多个条件中选择一个的单条件查询的场景,利用分支嵌套就可以实现动态选择单条件:

在MyBatis的Mapper代理中,<choose>相当于switch,<when>相当于case

    <select id="selByCondition2" resultMap="rm">
        select *
        from mybatis where
        <choose>
            <when test="status !=null">
                STATUS=#{STATUS}
            </when>
            <when test="companyName !=null and companyName !=''">
                company_name like #{companyName}
            </when>
            <when test="bracdName !=null and bracdName !=''">
                bracd_name like #{bracdName}
            </when>
            <otherwise>1=1</otherwise>
        </choose>
    </select>

与多条件查询不同的是,SQL语句中只会有一个分支生效

当用户一个条件都不选时,可以在<otherwise>中写上1=1让语法成立,反之,若选择了条件则会返回正常结果

foreach

对于批量删除的场景,传统的方法是通过in关键字结合占位符来确定,就像这样where id in (?,?,?)

但对于动态的场景,批量的数量永远是不确定的,这就导致还需要去改SQL里的占位符数量啊,又是一件麻烦事

PS:MyBatis会将数组参数封装成一个Map集合,默认情况(K-V)array=数组

下面使用了@Param注解改变了map集合中默认的keyMyBatis多条件查询、动态SQL、多表操作、注解开发于是MyBatis中的解决了这一麻烦:

本质是通过遍历的形式,批量删除的数据是由id数组或者集合来决定,collection属性决定了要遍历哪个数组/集合,item属性则来存放选出的元素,并把它放在占位符里,separator属性表示分隔符

    <delete id="deleteById">
        delete frpm mybatis where id in
        <foreach collection="ids" item="id" separator="," open="(" close=")">
            #{id}
        </foreach>;
    </delete>

有人会问为啥这里只有一个#{id},我的属性字段不止这一个呀?此id非彼id他是一个数组/集合

多表操作

多表之间的关系有一对一,一对多,多对一,多对多,每一种都有建表的原则,以用户-订单模型为例

利用传统的方法进行多表查询无非是通过id来连接表然后封装返回结果,MyBatis中也是如此,我们在Mapper文件中写好表字段之间的映射关系,定义好类型即可,只不过这一过程有点复杂,但一次配好之后即可极大减少硬编码问题,提高效率

一对一

一个用户有一张订单MyBatis多条件查询、动态SQL、多表操作、注解开发首先还是那套路,建好实体类,写好接口方法,配置Mapper文件,而多表操作的麻烦点就在于配置文件,这里通过例子细说一下

1.先把表写好

CREATE TABLE orders (
id INT PRIMARY KEY ,
ordertime VARCHAR(20) NOT NULL DEFAULT '',
total DOUBLE,
uid INT);
INSERT INTO orders VALUES(1,2020,2000,1);
INSERT INTO orders VALUES(2,2021,3000,2);
INSERT INTO orders VALUES(3,2022,4000,3);
CREATE TABLE USER (
id INT PRIMARY KEY ,
username VARCHAR(50) NOT NULL DEFAULT '',
passwords VARCHAR(50) NOT NULL DEFAULT '');
INSERT INTO USER VALUES(1,'lyy',333);
INSERT INTO USER VALUES(2,'myy',444);
INSERT INTO USER VALUES(3,'xyy',555);

2.写Mapper配置文件

在写实体类时,要把一个实体写到另一个实体的属性里面,这样才体现关联性,就比如“订单是所用户拥有的”,正因为这种关系我们才会在订单实体类里面写上private User user;这一属性,这样根据id连接的两个实体才能完美对接!就像这样:MyBatis多条件查询、动态SQL、多表操作、注解开发通过把两张表对应的实体类连接起来,只不过是主键ID要用单独的标签

property: 当前实体(order)中的属性名称(private User user)

javaType: 当前实体(order)中的属性的类型(User)

这两个user有着本质上的区别,就好像前者是在一个人的名字,后者正是被叫的那个人,MyBatis好像就利用了这一特性,通过标签的形式连接了两个实体

<select id="findAll" resultMap="orderMap">
   SELECT *,o.id oid FROM orders o,USER u WHERE o.uid=u.id
</select>

SQL环节和原来没什么区别,同样也是通过resultMap把字段和属性映射封装

一对多

一个用户有多张订单MyBatis多条件查询、动态SQL、多表操作、注解开发首先,在原有的User实体中得加上一个表示“用户有哪些订单的属性”private ListorderList;,目的是为了把订单的信息封装到用户的这个属性里,在Mapper文件中体现:

<collection property="orderList" ofType="order">
    <!--封装order的数据-->
    <id column="oid" property="id"></id>
    <result column="ordertime" property="ordertime"></result>
    <result column="total" property="total"></result>
</collection>

property:集合名称,User实体中的orderlist属性

ofType:当前集合中的数据类型,就是order实体

然后就是写一对多的SQL:

<select id="findAll" resultMap="userMap">
   SELECT *,o.id oid FROM USER u,orders o WHERE u.id=o.uid
</select>

总结来看,一对多相比于一对一就是在那个“一”中增添了封装“多”的属性而已,然后稍微调整一下SQL

多对多

多用户多角色MyBatis多条件查询、动态SQL、多表操作、注解开发多对多的建表原则是引入一张中间表,用于维护外键,就是一张表通过中间表找到另一张表

和一对多的模型类似,先在User实体类中增添一个“用户具备哪些角色”的属性private ListroleList;其次配置Mapper文件:

<collection property="roleList" ofType="role">
   <id column="roleId" property="id"></id>
   <result column="roleName" property="roleName"></result>
   <result column="roleDesc" property="roleDesc"></result>
</collection>

多表的连接是靠的中间表,这点在Mapper文件中通过映射实现,具体是把两张外表的id(userId和roleId)在id标签中配置成同一个属性,就像这样:

<id column="userId" property="id"></id>
<id column="roleId" property="id"></id>

SQL环节就得用多对多的套路了

<select id="findUserAndRoleAll" resultMap="userRoleMap">
    SELECT * FROM USER u,user-role ur,role r WHERE u.id=ur.userId AND ur.roleId=r.id
</select>

回想进行多表操作时MyBatis为我们带来了什么?他确实减少了很多硬编码,我每一次新的SQL只需要在标签里改几个属性就可以,只要理清字段与属性的映射关系,在MyBatis中进行多表操作就是一个“对号入座”

注解开发

针对于简单的CRUD注解开发可以极大地提升效率,顾名思义就是把SQL写在注解里

注解 说明
@Insert 实现新增
@Delete 实现删除
@Update 实现更新
@Select 实现查询
@Result 实现结果集封装
@Results 可以与@Result 一起使用,封装多个结果集
@ResultMap 实现引用@Results 定义的封装
@One 实现一对一结果集封装
@Many 实现一对多结果集封装
@SelectProvider 实现动态 SQL 映射
@CacheNamespace 实现注解二级缓存的使用

@Results

代替的是标签<resultMap>该注解中可以使用单个@Result注解,也可以使用@Result集
合。使用格式:@Results({@Result(),@Result()})或@Results(@Result())

@Resut

代替了<id>标签和<result>标签
@Result中属性介绍:
column:数据库的列名
property:需要装配的属性名
one:需要使用的@One 注解(@Result(one=@One()))
many:需要使用的@Many 注解(@Result(many=@many()))

@One

代替了<assocation> 标签,是多表查询的关键,在注解中用来指定子查询返回单一对象。
@One注解属性介绍:
select: 指定用来多表查询的 sqlmapper
使用格式:@Result(column=" ",property="",one=@One(select=""))

@Many

代替了<collection>标签, 是是多表查询的关键,在注解中用来指定子查询返回对象集合。
使用格式:@Result(property="",column="",many=@Many(select=""))

@CacheNamespace(blocking = true)

mybatis 基于注解方式实现配置二级缓存   

一对一

public interface OrderMapper {
    
    @Select("select * from orders")
    @Results({
        @Result(id=true,property = "id",column = "id"),
        @Result(property = "ordertime",column = "ordertime"),
        @Result(property = "total",column = "total"),
        @Result(property = "user",column = "uid",
                javaType = User.class,
                one = @One(select = 
                           "com.itheima.mapper.UserMapper.findById"))
    })
    List<Order> findAll();
}

public interface UserMapper {

  @Select("select * from user where id=#{id}")
  User findById(int id);
}

一对多

public interface UserMapper {
    @Select("select * from user")
    @Results({
        @Result(id = true,property = "id",column = "id"),
        @Result(property = "username",column = "username"),
        @Result(property = "password",column = "password"),
        @Result(property = "birthday",column = "birthday"),
        @Result(property = "orderList",column = "id",
                javaType = List.class,
                many = @Many(select = 
                             "com.itheima.mapper.OrderMapper.findByUid"))
    })
    List<User> findAllUserAndOrder();
}

public interface OrderMapper {
  @Select("select * from orders  where uid=#{uid}")
  List<Order> findByUid(int uid);
}

号外!号外!

如果这篇文章对你有所帮助,或者有所启发的话,帮忙点赞、在看、转发、收藏,你的支持就是我坚持下去的最大动力!

MyBatis多条件查询、动态SQL、多表操作、注解开发

面试官:MQ 消息丢失、重复、积压问题,如何解决?


如何监听 MySQL 实现数据变化后的实时通知


实时监听MySQL同步数据到ElasticSearch

MyBatis多条件查询、动态SQL、多表操作、注解开发


原文始发于微信公众号(一安未来):MyBatis多条件查询、动态SQL、多表操作、注解开发

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/44613.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!