其他分享
首页 > 其他分享> > 掌握Mybatis动态映射,我可是下了功夫的

掌握Mybatis动态映射,我可是下了功夫的

作者:互联网

掌握Mybatis动态映射,我可是下了功夫的

动态 SQL 是 MyBatis 的强大特性之一。如果你使用过 JDBC 或其它类似的框架,你应该能理解根据不同条件拼接 SQL 语句有多痛苦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL,可以彻底摆脱这种痛苦。

使用动态 SQL 并非一件易事,但借助可用于任何 SQL 映射语句中的强大的动态 SQL 语言,MyBatis 显著地提升了这一特性的易用性。

如果你之前用过 JSTL 或任何基于类 XML 语言的文本处理器,你对动态 SQL 元素可能会感觉似曾相识。在 MyBatis 之前的版本中,需要花时间了解大量的元素。借助功能强大的基于 OGNL 的表达式,MyBatis 3 替换了之前的大部分元素,大大精简了元素种类,现在要学习的元素种类比原来的一半还要少。

6371c164157c7ff370437d22d5d77ff6.jpg

if:利用if实现简单的条件选择。
choose(when,otherwise):相当于java中的switch语句,通常与when和otherwise搭配。
set:解决动态更新语句。
trim:灵活的去除多余的关键字。
foreach:迭代一个集合,通常用于in条件。

实际工作中很多时候,这几个标签都是组合着使用。

今天的演示使用的是Spring-Boot+Mybatis进行演示,对于Spring-Boot整合Mybatis推荐:

e3d475d0a1998de952f953bdfc1f93b0.jpeg

if+where实现多条件查询

创建一种昂数据库表:

CREATE TABLE `m_user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  `gender` int(11) DEFAULT NULL COMMENT '0:女生 1:男生',
  PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb4;

初始化几条数据:

60302a499e39389762e52358ad82eb18.jpeg

先来看UserMapper.xml文件:

<mapper namespace="com.tian.mybatis.mapper.UserMapper">
    <resultMap id="User" type="com.tian.mybatis.entity.User">
        <id column="id" property="id"/>
        <result column="name" property="userName"/>
    </resultMap>
    <select id="selectUserById"  resultMap="User">
        select * from m_user
        <where>
            <if test="id != null">
                id = #{id}
            </if>
            <if test="name != null and name != ''">
                and `name` = #{name}
            </if>
        </where>
    </select></mapper>

UserMapper.java内容:

import com.tian.mybatis.entity.User;import org.apache.ibatis.annotations.Param;import org.springframework.stereotype.Repository;@Repositorypublic interface UserMapper {
    User selectUserById(@Param("name") String userName, @Param("id") Integer id);}

UserService.java内容:

public interface UserService {
    User selectUserById(String userName, Integer id);}

UserServiceImpl.java内容:

import com.tian.mybatis.entity.User;import com.tian.mybatis.mapper.UserMapper;import com.tian.mybatis.service.UserService;import org.springframework.stereotype.Service;import javax.annotation.Resource;@Servicepublic class UserServiceImpl implements UserService {

    @Resource
    private UserMapper userMapper;

    @Override
    public User selectUserById(String userName, Integer id) {
        return userMapper.selectUserById(userName, id);
    }}

UserController.java内容:

import com.tian.mybatis.entity.User;import com.tian.mybatis.service.UserService;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;@RestControllerpublic class UserController {

    @Resource
    private UserService userService;

    @GetMapping("/test")
    public User selectUserById() {
        return userService.selectUserById("tian", 1);
    }}

Application.java内容:

import org.mybatis.spring.annotation.MapperScan;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication@MapperScan("com.tian.mybatis.mapper")public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }}

把项目启动起来,然后进行访问/test。

http://localhost:9002/test

返回:

d993e126908db876fb4a8a5aa2bba444.jpeg

上面的这个案例也是我们工作中的代码案例,我们工作但部分都使用这种方式。

3eac3e4fb4bf613c70f2d57bc93e48ce.jpeg

下面的所有演示都是基于上面这些代码进行调整而成的。

回到正题。

上面的案例中使用了where+if。案例中貌似有个问题:

27ab17b17017f587180f12da8384b628.jpeg

如果id=null,岂不是多了个and吗?

我们修改controller中的代码

    @GetMapping("/test")
    public User selectUserById() {
        return userService.selectUserById("tian", null);
    }

为了能让sql输出,我们在配置文件添加了一个配置项:

logging:
  level:
    com:
      tian:
        mybatis:
          mapper: debug

再次执行,输出和前面是一样的。控制台输出的sql中并没有and。这就是所谓的动态映射的强大功能之一。

84bab5bb7343019d4d1a3c836ac7f313.png

如果我们不使用动态映射标签,在处理or或者and的时候很有可能出问题。

where元素可以智能的处理and 和 or 的多余问题, 不需担心多余关键字导致语法错误。
if元素的test用于判断表达式是否符合,符合则继续拼接SQL语句。

建议

建议使用这种动态标签,不要使用原生态,因为有时候总有意想不到的判断导致多了一个and或者or,于是就出现bug,严重的可能导致线上某个功能不可能用。

if+trim+foreach实现多条件查询

对前面的代码进行调整

<select id="selectUsersByIds" resultMap="User">
        select * from m_user
        <trim prefix="where" prefixOverrides="and | or">
            <if test="idList != null">
                id in
                <foreach collection="idList" item="id" open="(" close=")" separator=",">
                    #{id}
                </foreach>
            </if>
            <if test="gender != null and gender != 0">
                AND gender = #{gender}
            </if>
        </trim></select>

UserMapper.java增加

List<User> selectUsersByIds(@Param("idList") List<Integer> idList, @Param("gender") Integer gender);

controller新增方法:

    @GetMapping("/users")
    public List<User> selectUsersByIds() {
        List<Integer> idList = new ArrayList<>();
        idList.add(1);
        idList.add(2);
        idList.add(3);
        idList.add(4);
        idList.add(5);
        return userService.selectUsersByIds(idList, null);
    }

项目跑起来,访问

http://localhost:9002/users

输出:

a5ea7dcd1d4b55d256fe7fab5df9e2a2.jpeg

sql输出:

a0205456b3925d922fac10d48126e05b.png

对上面相关属性进行说明

trim的属性

foreach的属性

ea6b347cb8e669b149d882f365173f59.png

@Param是Mybatis中的注解,写的时候别引用错了,@Param("name"),这里的name就是我们在Mapper.xml中使用的名称。

在项目中我见过很多人这么干,就是当where语句后面不太确定能有条件出现时,使用

slect ...from...where 1=1

看看你的代码是否也有?

set

set元素可以用于动态包含需要更新的列,忽略其它不更新的列。

UserMapper.xml新增

<update id="updateAuthorIfNecessary">
        update m_user
        <set>
            <if test="userName != null and userName != ''">
               `name` = #{userName},
            </if>
            <if test="gender != null and gender != 0">
                gender = #{gender},
            </if>
            <if test="age != null and age != 0">
                age = #{age},
            </if>
        </set>
        where id=#{id}</update>

UserMapper.java新增

int updateAuthorIfNecessary(User user);

controller新增

    @PostMapping("/updateUser")
    public String update() {
        User user = new User();
        user.setAge(18);
        user.setUserName("田哥");
        user.setId(1);
        return userService.updateAuthorIfNecessary(user) == 1 ? "success" : "fail";
    }

重启项目,访问

http://localhost:9002/updateUser

输出:success

数据库表中数据已经修改成功:

9047638a81bcfb8cd5442bd6a1cb428c.jpeg

SQL输出

7ec6c44021f06275d26352ef45cabb87.png

这个例子中,set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号(这些逗号是在使用条件语句给列赋值时引入的)。

换一种方式

<trim prefix="SET" suffixOverrides=",">
  ...</trim>

我们把上面的xml中diam进行调整:

    <update id="updateAuthorIfNecessary">
        update m_user
        <trim prefix="SET" suffixOverrides=",">
            <if test="userName != null and userName != ''">
                `name` = #{userName},
            </if>
            <if test="gender != null and gender != 0">
                gender = #{gender},
            </if>
            <if test="age != null and age != 0">
                age = #{age},
            </if>
        </trim>
        where id=#{id}
    </update>

controller修改:

    @PostMapping("/updateUser")
    public String update() {
        User user = new User();
        user.setAge(19);
        user.setUserName("tian");
        user.setId(1);
        return userService.updateAuthorIfNecessary(user) == 1 ? "success" : "fail";
    }

最后看看SQL输出:

4ff0a8959a9cb191c8b54b31819e4d38.jpeg

自动给我们加上了SET关键字。并且数据库修改成功。

57a135151c247a56c5181593933a78c3.jpeg

choose

相当于Java中的switch语句,通常与when和otherwise搭配。

有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。

下面我们继续使用上面的案例代码进行演示。

UserMapper.xml新增方法:

<select id="selectUsersByName" resultMap="User">
        select * from m_user where age = 19
        <choose>
            <when test="userName != null and userName != ''">
                and `name` = #{userName}
            </when>
            <otherwise>
                AND gender = 1
            </otherwise>
        </choose></select>

controller新增方法:

@GetMapping("/user/name")
    public  List<User>  selectUsersByName() {
        return userService.selectUsersByName("tian");}

返回:

ce4db4269d68310b8bd673a15088d51f.png

SQL输出:

ab35d93ae8867c0164bdb6d9b2fb737e.jpeg

正确的输出。如果我们userName没有是null呢?

输出和上面正常,在看看SQL输出:

56bc49d40245693098b1540128e21da7.jpeg

因为我们的userName的条件不满足的情况下,直接执行了gender。

上面<otherwise>就类似于相当于我们java语法中switch中的default,当前面条件不满足的时候,执行default模块一样。

Bind

这种方式使用的不是很多,但是也是有用的。bind 元素允许你在 OGNL 表达式以外创建一个变量,并将其绑定到当前的上下文。比如:

<select id="selectUserByName" resultType="User">
  <bind name="pattern" value="'%' + userName + '%'" />
  select * from m_user
  WHERE `name` LIKE #{pattern}</select>

还有就是script,这个就没有必要在演示了,在工作中基本上用不上。它就是把SQL卸载Java代码中。比如

    @Update({"<script>",
      "update m_user",
      "  <set>",
      "    <if test='username != null'>`name`=#{username},</if>",
      "    <if test='gender != null and gender != 0'>gender=#{gender},</if>",
      "  </set>",
      "where id=#{id}",
      "</script>"})
    void updateUserValues(User user);

总结

文章中部分知识为了演示,可能有些代码不是很规范,尤其是sql部分,我们在开发中,针对使用Mybatis开发,我个人总结了几个点:


原作者:田维常
原文链接:掌握Mybatis动态映射,我可是下了功夫的
原出处:Java后端技术全栈
侵删

484c8f47c31d5eecd49388ac8339553e.jpeg


标签:映射,gender,功夫,user,SQL,Mybatis,import,id,User
来源: https://blog.51cto.com/15050718/2621768