关注“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;@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
返回:
下面的这个案例也是咱们工作中的代码案例,咱们工作但局部都应用这种形式。
上面的所有演示都是基于下面这些代码进行调整而成的。
回到正题。
下面的案例中应用了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真香系列:办法区、堆、栈之间到底有什么关系