关于mybatis:Mybatis动态映射这次终于搞明白了

7次阅读

共计 8765 个字符,预计需要花费 22 分钟才能阅读完成。

关注“Java 后端技术全栈”

回复“面试”获取全套面试材料

动静 SQL 是 MyBatis 的弱小个性之一。如果你应用过 JDBC 或其它相似的框架,你应该能了解依据不同条件拼接 SQL 语句有多苦楚,例如拼接时要确保不能遗记增加必要的空格,还要留神去掉列表最初一个列名的逗号。利用动静 SQL,能够彻底解脱这种苦楚。

应用动静 SQL 并非一件易事,但借助可用于任何 SQL 映射语句中的弱小的动静 SQL 语言,MyBatis 显著地晋升了这一个性的易用性。

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

if:利用 if 实现简略的条件抉择。

choose(when,otherwise):相当于 java 中的 switch 语句,通常与 when 和 otherwise 搭配。

set:解决动静更新语句。

trim:灵便的去除多余的关键字。

foreach:迭代一个汇合,通常用于 in 条件。

理论工作中很多时候,这几个标签都是组合着应用。

明天的演示应用的是 Spring-Boot+Mybatis 进行演示,对于 Spring-Boot 整合 Mybatis 举荐:

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;

初始化几条数据:

先来看 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;
@Repository
public 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;
@Service
public 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;
@RestController
public 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

返回:

下面的这个案例也是咱们工作中的代码案例,咱们工作但局部都应用这种形式。

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

回到正题。

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

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

咱们批改 controller 中的代码

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

为了能让 sql 输入,咱们在配置文件增加了一个配置项:

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

再次执行,输入和后面是一样的。控制台输入的 sql 中并没有 and。这就是所谓的动静映射的弱小性能之一。

如果咱们不应用动静映射标签,在解决 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

输入:

sql 输入:

对下面相干属性进行阐明

trim 的属性

  • prefix:前缀: 作用是通过自动识别是否有返回值后,在 trim 蕴含的内容上加上前缀,如上述示例的 where。
  • suffix:后缀: 作用是在 trim 蕴含的内容上加上后缀。
  • prefixOverrides::对于 trim 蕴含内容的首部进行指定内容,(如上述示例的 and | or) 的疏忽(去余);
  • suffixOverrides::对于 trim 蕴含内容的首位部进行指定内容的疏忽。

foreach 的属性

  • item:示意汇合中每一个元素进行迭代时的别名。
  • index::指定一个名称,示意在迭代的过程中,每次迭代到的地位。
  • open:示意该语句以什么开始(既然是 in 条件语句,必然是 ‘ ( ‘ 开始)
  • separator::示意每次进行迭代的时候以什么符号作为分隔符(既然是 in 条件语句,必然是 ‘,‘ 分隔)
  • close::示意该语句以什么完结(既然是 in 条件语句,必然是 ‘) ‘ 完结)
  • collection:最要害,并且最容易出错的属性。需注意,该属性必须指定,不同状况下,该属性值是不同的,

    次要有三种状况:

@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

数据库表中数据曾经批改胜利:

SQL 输入

这个例子中,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 输入:

主动给咱们加上了 SET 关键字。并且数据库批改胜利。

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");
}

返回:

SQL 输入:

正确的输入。如果咱们 userName 没有是 null 呢?

输入和下面失常,在看看 SQL 输入:

因为咱们的 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 开发,我集体总结了几个点:

  • 表中是否曾经有索引,有索引的时候咱们的 SQL 中是否有用上。
  • 返回字段尽量不要写星号 *,倡议写成须要的字段。
  • 关键字倡议都写成大写,更好的区别非关键字。
  • 遇到表中字段和数据库要害一样的时候,记得单引号。
  • 应用 @Param 注解留神肯定要应用 Mybatis 中的注解。
  • 应用不论是一个参数还是多个参数时,应用注解 @Param 指定名称,不便日后须要再次增加字段。
  • 强烈建议应用动静标签,避免出现多出 and 或者 or 关键字的 SQL 谬误,同时也不必再写 where 1=1

举荐浏览

小学妹问我:如何利用可视化工具排查问题?

JVM 真香系列:办法区、堆、栈之间到底有什么关系

正文完
 0