框架篇(一)Mybatis基础面试题
Mybatis面试题
相关文档:
https://mybatis.org/mybatis-3/zh/configuration.html
bearbrick0/mybatis: mybatis源码中文注释 (github.com)
dynamic-sql.xml — mybatis/mybatis-3 — GitHub1s
Mybatis框架入门教程 (biancheng.net)一个很不错的学习网站!可以练手!
1. 什么是Mybatis
-
Mybatis是一个半ORM(对象关系映射)框架,它内部封装了JDBC,加载驱动、创建连接、创建statement等繁杂的过程,开发者开发时只需要关注如何编写SQL语句,可以严格控制
sql
执行性能,灵活度高。 -
作为一个半ORM框架,MyBatis 可以使用 XML 或注解来配置和映射原生信息,将 POJO映射成数据库中的记录,避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。
-
通过xml 文件或注解的方式将要执行的各种
statement
配置起来,并通过java对象和statement
中sql
的动态参数进行映射生成最终执行的sql
语句,最后由mybatis框架执行sql并将结果映射为java对象并返回。(从执行sql到返回result的过程)。 -
由于MyBatis专注于SQL本身,灵活度高,所以比较适合对性能的要求很高,或者需求变化较多的项目,如互联网项目。
优点:
-
基于SQL语句编程,相当灵活,不会对应用程序或者数据库的现有设计造成任何影响,SQL写在XML里,解除sql与程序代码的耦合,便于统一管理;提供XML标签,支持编写动态SQL语句,并可重用。
-
与JDBC相比,减少了50%以上的代码量,消除了JDBC大量冗余的代码,不需要手动开关连接;
-
很好的与各种数据库兼容(因为MyBatis使用JDBC来连接数据库,所以只要JDBC支持的数据库MyBatis都支持)。
-
能够与Spring很好的集成;
-
提供映射标签,支持对象与数据库的ORM字段关系映射;提供对象关系映射标签,支持对象关系组件维护。
缺点:
-
SQL语句的编写工作量较大,尤其当字段多、关联表多时,对开发人员编写SQL语句的功底有一定要求。
-
SQL语句依赖于数据库,导致数据库移植性差,不能随意更换数据库。
2. 简述Mybatis的工作原理
- 读取
Mybatis
的配置文件mybatis-config.xml
和数据库建立连接。mybatis-config.xml
为Mybatis的全局配置文件,配置了,Mybatis
运行环境等信息,例如数据库连接信息。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--配置-->
<configuration>
<!-- 引入外部配置文件-->
<properties resource="database.properties"/>
<!--配置日志-->
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
<!-- <setting name="logImpl" value="LOG4J"/>-->
<!-- <setting name="LazyLoadingEnabled" value=""></setting>-->
</settings>
<!--类型别名-->
<typeAliases>
<typeAlias alias="User" type="com.uin.pojo.User"/>
<typeAlias alias="Role" type="com.uin.pojo.Role"/>
<typeAlias alias="Provider" type="com.uin.pojo.Provider"/>
<typeAlias alias="Bill" type="com.uin.pojo.Bill"/>
<typeAlias alias="Address" type="com.uin.pojo.Address"/>
</typeAliases>
<!--配置mybatis的运行环境-->
<environments default="development">
<environment id="development">
<!--使用JDBC的事务管理-->
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<!-- MySQL数据库驱动 -->
<property name="driver" value="${driver}"/>
<!-- 连接数据库的URL -->
<property name="url" value="${url}"/>
<!--username-->
<property name="username" value="${username}"/>
<!--password-->
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<!-- 将mapper文件加入到配置文件中 -->
<mappers>
<mapper resource="com/uin/mapper/UserMapper.xml"/>
<mapper resource="com/uin/mapper/AddressMapper.xml"/>
<mapper resource="com/uin/mapper/BillMapper.xml"/>
<mapper resource="com/uin/mapper/ProviderMapper.xml"/>
<mapper resource="com/uin/mapper/RoleMapper.xml"/>
</mappers>
</configuration>
- 加载
SQL
映射文件。映射文件就是SQL
映射文件,该文件中存储了操作数据库的SQL
语句,需要在Mybatis配置文件mybatis-config.xml
中配置加载。mybatis-config.xml
文件可以加载多个映射文件,每个映射文件对应数据库中的一张表。 - 构造会话工厂。通过Mybatis的环境等配置信息构建会话工厂
SqlSessionFactory
。 - 创建会话对象。通过会话工厂创建一个
SqlSession
对象,该对象中包含了执行sql
的所有方法。 Executor
执行器。Mybatis底层定义了一个Executor
接口来操作数据库。它将根据SqlSession
传递的参数动态的生成需要执行的sql
语句,同时负责查询缓存的维护。
![image-20220328091239234](https://bearbrick0.oss-cn-qingdao.aliyuncs.com/images/img/202203280916955.png)
MappedStatement
对象:在Executor
接口的执行方法中有一个MappedStatement
类型的参数,该参数是对映射信息的封装,用于存储要映射的SQL
语句的id
、参数等信息。
![image-20220328091554024](https://bearbrick0.oss-cn-qingdao.aliyuncs.com/images/img/202203280916742.png)
-
输入参数映射:输入参数的类型可以是
Map
、List
等集合类型,也可以是基本数据类型和POJO
类型。输入参数映射的过程类似于JDBC对PrepareStatement
对象设置参数的过程。 -
输出结果的映射。输出结果类型可以是
Map
、List
等集合类型,也可以是基本数据类型和POJO
类型。输出结果就类似于JDBC对结果集解析过程。
3. #{}和${}的区别是什么?
${ }
是拼接符,字符串替换,#{ }
是占位符,预编译处理;
Mybatis在处理${}
时,就是把${}
直接替换成变量的值。
而Mybatis在处理#{}
时,会对sql语句进行预处理,将sql中的#{}
替换为?
号,调用PreparedStatement
的set
方法来赋值;
使用#{}
可以有效的防止SQL注入,提高系统安全性。
--Mybatis在处理#{}时
select id,name,age from student where id =#{id}
当前端把id值1传入到后台的时候,就相当于:
select id,name,age from student where id ='1'
--Mybatis在处理${}时
select id,name,age from student where id =${id}
当前端把id值1传入到后台的时候,就相当于:
select id,name,age from student where id = 1
4. Mybatis是否支持延迟加载?如果支持,它的实现原理是什么?
-
Mybatis仅支持
association
关联对象和collection
关联集合对象的延迟加载,association
指的就是一对一,collection
指的就是一对多查询。在Mybatis配置文件中,可以配置是否启用延迟加载
lazyLoadingEnabled=true|false。
-
它的原理是,使用
CGLIB
创建目标对象的代理对象,当调用目标方法时,进入拦截器方法,比如调用A.getB().getName()
,拦截器invoke()
方法发现A.getB()
是null
值。那么就会单独发送事先保存好的查询关联 B 对象的
sql
,把B查询上来,然后调用A.setB(b)
,于是A的对象 b 属性就有值了,接着完成A.getB().getName()
方法的调用。这就是延迟加载的基本原理。
-
当然了,不光是Mybatis,几乎所有的包括
Hibernate
,支持延迟加载的原理都是一样的。
5. Mybatis和Hibernate的区别
-
Mybatis
和hibernate
不同 ,它不完全是一个 ORM 框架 ,因为 MyBatis 需要程序员自己编写 Sql 语句。 -
Mybatis
直接编写原生态sql
, 可以严格控制sql
执行性能, 灵活度高, 非常适合对关系数据模型要求不高的软件开发,因为这类软件需求变化频繁,一但需求变化要求迅速输出成果。 但是灵活的前提是 mybatis 无法做到数据库无关性,如果需要实现支持多种数据库的软件,则需要自定义多套 sql 映射文件,工作量大 。 -
Hibernate
对象/关系映射能力强, 数据库无关性好,对于关系模型要求高的软件,如果用 · 开发可以节省很多代码, 提高效率。
6. 模糊查询like
语句该怎么写
- 在Java代码中添加
sql
通配符。也是比较推荐用的一种。
<!--直接在代码中拼接%, 不存在sql注入-->
<select id="findUserByLikeName2" parameterType="java.lang.String" resultMap="user">
select * from t_user where name like #{name,jdbcType=VARCHAR}
</select>
@Test
public void findUserByLikeName2(){
String name = "Cloud";
List<User> test = userMapper.findUserByLikeName2("%" +name+"%");
// select * from t_user where name like ?
// %Cloud%(String)
System.out.println(test.size());
}
- 在
sql
语句中拼接通配符, 会引起sql
注入
String wildcardname = “smi”;
List<name> names = mapper.selectlike(wildcardname);
<select id=”selectlike”>
select * from foo where bar like "%"#{value}"%"
</select>
concat()
函数。在实际开发最常用的一种。
<!--concat Mysql和 Oracle区别 ,不存在sql注入-->
<select id="findUserByLikeName3" parameterType="java.lang.String" resultMap="user">
select * from t_user where name like concat('%',#{name,jdbcType=VARCHAR},'%')
</select>
@Test
public void findUserByLikeName3(){
String name = "Cloud";
List<User> test = userMapper.findUserByLikeName3(name);
// select * from t_user where name like concat('%',?,'%')
// Cloud(String)
System.out.println(test.size());
}
当使用方式三的时候,如果查询的关键字就是%
,那情况会是什么?
查询的sql如下:
select * from t_user where name like concat('%','%','%')
而是查出来全部的数据,并不是只包含了%
的数据,如果查询_
也是一样的。
那这种情况肯定是不满足查询需求的,则需要调整。
- 在代码中进行转义
@Test
public void findUserByLikeName3(){
String name = "%";
name = name.replaceAll("_", "\\\\_");
name = name.replaceAll("%", "\\\\%");
List<User> test = userMapper.findUserByLikeName3(name);
System.out.println(test.size());
}
- 使用
ESCAPE
<select id="findUserByLikeName4" parameterType="java.lang.String" resultMap="user">
select * from t_user where name like concat('%',#{name,jdbcType=VARCHAR},'%') ESCAPE '/'
</select>
测试:
@Test
public void findUserByLikeName4(){
// replaceAll("%", "/%").replaceAll("_", "/_")
String name = "%";
List<User> test = userMapper.findUserByLikeName4(name);
System.out.println(test.size());// 查到全部
List<User> test1 = userMapper.findUserByLikeName4("/" +name);
System.out.println(test1.size());//查到匹配%的记录
}
7. Mybatis的Xml映射文件中,不同的Xml映射文件,id是否可以重复?
不同的Xml映射文件,如果配置了namespace
,那么id
可以重复;
如果没有配置namespace
,那么id
不能重复;
原因就是namespace+id
是作为Map
的key
使用的,如果没有namespace
,就剩下id
,那么,id
重复会导致数据互相覆盖。
有了namespace
,自然id
就可以重复,namespace
不同,namespace+id
自然也就不同。
备注:在旧版本的Mybatis中,namespace
是可选的,不过新版本的namespace
已经是必须的了。
8. 当实体类中的属性名和表中的字段名不一样 ,怎么办 ?
- 通过在查询的
sql
语句中定义字段名的别名 , 让字段名的别名和实体类的属性名一致。
<select id="selectorder" parametertype="int" resultetype="me.gacl.domain.order">
select
order_id as id,
order_no as orderno,
order_price as price
form
orders
where
order_id=#{id};
</select>
- 通过来映射字段名和实体类属性名的一一对应的关系。
<select id="getOrder" parameterType="int" resultMap="orderresultmap">
select
*
from
orders
where
order_id=#{id}
</select>
<resultMap type="me.gacl.domain.order" id="orderresultmap">
<!–用 id 属性来映射主键字段–>
<id property="id" column="order_id">
<!–用 result 属性来映射非主键字段,property 为实体类属性名,column 为数据表中的属性–>
<result property="orderno" column="order_no"/>
<result property="price" column="order_price"/>
</reslutMap>
9. Mybatis是如何将sql执行结果封装为目标对象并返回的?都有哪些映射形式?
-
第一种是使用
标签,逐一定义列名和对象属性名之间的映射关系。 -
第二种是使用
sql
列的别名功能,将列别名书写为对象属性名,比如T_NAME as NAME
,对象属性名一般是name
,小写,但是列名不区分大小写,Mybatis会忽略列名大小写,智能找到与之对应对象属性名,你甚至可以写成T_NAME as NaMe
,Mybatis一样可以正常工作。
10. 在mapper中如何传递多个参数
- 顺序传参法
public interface UserMapper{
User UserselectUser(String name,String area);
}
<select id="selectUser"resultMap="BaseResultMap">
select
*
from
user_user_t
where
user_name = #{0} and user_area=#{1}
</select>
<!--#{0},#{1}代表的是参数的顺序,并不是什么参数-->
- 使用
@Param
注解传参
public interface UserMapper {
User selectuser(@param("username") String username,
@param("hashedpassword") tring hashedpassword);
}
然后 ,就可以在 xml
像下面这样使用:
<select id="selectuser" resulttype="user">
select
id,
username,
hashedpassword
from
some_table
where
username = #{username} and hashedpassword = #{hashedpassword};
</select>
#{}
里面的名称对应的是注解@Param
括号里面修饰的名称,这种方法在参数不多的情况话,推荐使用。
Map
传参法
public interface UserMapper{
User selectUser(Map<String,Object> map);
}
<select id="selectuser" resulttype="User" parameterType="java.util.Map">
select
id,
username,
hashedpassword
from
some_table
where
username = #{username} and hashedpassword = #{hashedpassword};
</select>
#{}
里面的名称对应的是Map里面的key,这种方法适合传递多个参数,且能灵活变化。
Java Bean
传参法
public interface UserMapper{
User selectUser(User user);
}
<select id="selectuser" resulttype="User" parameterType="User">
select
*
from
user
where
username=#{username} and password=#{password};
</select>
这种方法比较直观,就是需要建立一个实体类(VO
),扩展不容易,但代码的可读性高,业务逻辑处理方便,推荐使用。
11. 什么是MyBatis的接口绑定?有哪些实现方式?
- 接口绑定,就是在 MyBatis中任意定义接口 ,然后把接口里面的方法和 SQL语句绑定, 我们直接调用接口方法就可以 ,这样比起原来 了
SqlSession
提供的方法我们可以有更加灵活的选择和设置。 - 接口绑定有两种实现方式,
- 一种是通过注解绑定, 就是在接口的方法上面加上@Select、@Update 等注解, 里面包含 Sql 语句来绑定;
- 另外一种就是通过
xml
里面写SQL
来绑定, 在这种情况下,要指定xml
映射文件里面的namespace
必须为接口的全路径名。 当Sql
语句比较简单时候 ,用注解绑定 , 当 SQL 语句比较复杂 时候 ,用 xml 绑定 ,一般用 xml 绑定的比较多。
12. 通常一个 Xml 映射文件,都会写一个Mapper 接口与之对应,请问,这个 Mapper 接口的工作原理是什么?Mapper 接口里的方法,参数不同时,方法能重载吗?
Dao接口即Mapper接口。接口的全限名,就是映射文件中的 namespace
的值;
接口的方法名, 就是映射文件中 Mapper
的 Statement
的 id
值;
接口方法内的参数, 就是传递给 sql
的参数。
Mapper 接口是没有实现类的,当调用接口方法时 ,接口全限名 +方法名拼接字符 串作为 key
值, 可唯一定位一个 MapperdStatement
。
在 Mybatis 中, 每一个<select>、<insert>、<update>、<delete>
标签, 都会被解析为一 个 MappedStatement
对象。
举例: com.mybatis3.mappers.StudentDao.findStudentById, 可以唯一找到 namespace
为 com.mybatis3.mappers.StudentDao
下面 id
为 findStudentById
的 MapperdStatement。
Mapper 接口里的方法 ,是不能重载的 ,因为是使用全限名 +方法名的保存和寻找策略。
Mapper 接口的工作原理是JDK动态代理 , Mybatis 运行时会使用 JDK 动态代理为 Mapper接口生成代理对象 proxy
,代理对象会拦截接口方法,转而执行 MapperdStatement
所代表的 sql
, 然后将 sql 执行结果返回。
13. 一对一、一对多的关联查询
推荐练习:
MyBatis学习总结(五)——实现关联表查询 - 孤傲苍狼 - 博客园 (cnblogs.com)
<!--根据班级的Id查询对应的老师 一个班级对应一个老师 -->
<mapper namespace="com.lcb.mapping.userMapper">
<!--association 一对一关联查询 -->
<select id="getClass" parameterType="int" resultMap="ClassesResultMap">
select
*
from
class c,
teacher t
where
c.teacher_id=t.t_id and c.c_id=#{id}
</select>
<resultMap type="com.lcb.user.Classes" id="ClassesResultMap">
<!--实体类的字段名和数据表的字段名映射 -->
<id property="id" column="c_id"/>
<result property="name" column="c_name"/>
<association property="teacher" javaType="com.lcb.user.Teacher">
<id property="id" column="t_id"/>
<result property="name" column="t_name"/>
</association>
</resultMap>
<!--根据班级的Id查询班级下面的老师和学生 一个班级对应一个老师,一个班级对应很多个学生 -->
<!--collection 一对多关联查询 -->
<select id="getClass2" parameterType="int" resultMap="ClassesResultMap2">
select
*
from
class c,
teacher t,
student s
where
c.teacher_id=t.t_id and c.c_id=s.class_id and c.c_id=#{id}
</select>
<resultMap type="com.lcb.user.Classes" id="ClassesResultMap2">
<id property="id" column="c_id"/>
<result property="name" column="c_name"/>
<association property="teacher" javaType="com.lcb.user.Teacher">
<id property="id" column="t_id"/>
.......
<result property="name" column="t_name"/>
</association>
<collection property="student" ofType="com.lcb.user.Student">
<id property="id" column="s_id"/>
<result property="name" column="s_name"/>
</collection>
</resultMap>
</mapper>
14. MyBatis实现一对一有几种方式?具体怎么操作的
有联合查询和嵌套查询。
联合查询是几个表联合查询,只查询一次, 通过在resultMap
里面配置association
节点配置一对一的类就可以完成;
嵌套查询是先查一个表,根据这个表里面的结果的外键id,去再另外一个表里面查询数据,也是通过association
配置,但另外一个表的查询通过select
属性配置。
例如一个大学生只有一个学号,一个学号只属于一个学生。同样,人与身份证也是一对一的级联关系。
Mybatis_test: mybatis的一对一和一对多的嵌套查询和联合查询 的测试案例。 (gitee.com)
自己写的一个demo能够很好的理解!
14. 说一下Mybatis的一级、二级缓存
-
一级缓存: 基于
PerpetualCache
的HashMap
本地缓存,其存储作用域为Session
,当Session
flush 或 close 之后,该Session
中的所有Cache
就将清空,默认打开一级缓存。 -
二级缓存与一级缓存其机制相同,默认也是采用
PerpetualCache
,HashMap
存储,不同在于其存储作用域为Mapper(Namespace)
,并且可自定义存储源,如 Ehcache。默认不打开二级缓存,要开启二级缓存,使用二级缓存属性类需要实现
Serializable
序列化接口(可用来保存对象的状态),可在它的映射文件中配置 ; -
对于缓存数据更新机制,当某一个作用域(一级缓存
Session
/二级缓存Namespaces
)的进行了C/U/D
操作后,默认该作用域下所有 select 中的缓存将被clear
。如果开启了二级缓存,则只根据配置判断是否刷新。
15. Mybatis是如何进行分页的?分页插件的原理是什么?
先回顾一下,平时Mysql中是怎么做分页的:
#其中office指相对于首行的偏移量(首行为0),rows指返回记录条数
limit [office,] rows
在JDBC中我们做分页:
JavaWeb实现分页的四种方法 - 光何 - 博客园 (cnblogs.com)
这个值得一看!提供了很多的思路,并分析了各种的好处以及不好之处。
RowBounds
对象有2个属性,offset
和limit
。
offset
:起始行数。
limit
:需要的数据行数。
因此,取出来的数据就是:从第offset+1
行开始,取limit
行。
简单上手使用。和 【MyBatis】实现分页功能_小皮猪的博客-CSDN博客
Mybatis使用RowBounds
对象进行分页,它是针对ResultSet
结果集执行的内存分页,而非物理分页。
可以在sql
内直接书写带有物理分页的参数来完成物理分页功能,也可以使用分页插件来完成物理分页。
分页插件的基本原理是使用Mybatis
提供的插件接口,实现自定义插件,在插件的拦截方法内拦截待执行的sql
,然后重写sql
,根据dialect
方言,添加对应的物理分页语句和物理分页参数。
平时还有一个比较推荐的PageHelper
.如何使用分页插件 (pagehelper.github.io)
16. Mybatis动态sql有什么用?执行原理?有哪些动态sql?
Mybatis动态sql可以在Xml映射文件内,以标签的形式编写动态sql,执行原理是根据表达式的值 完成逻辑判断 并动态拼接sql的功能。
Mybatis提供了9种动态sql标签:trim
| where
| set
| foreach
| if
| choose
| when
| otherwise
| bind
。
跟着官方文档浅看一下:
if
使用动态 SQL 最常见情景是根据条件包含 where
子句的一部分
<select id="findActiveBlogWithTitleLike" resultType="Blog">
SELECT * FROM BLOG
WHERE state = ‘ACTIVE’
<if test="title != null">
AND title like #{title}
</if>
</select>
这条语句提供了可选的查找文本功能。如果不传入 “title”,那么所有处于 “ACTIVE” 状态的 BLOG 都会返回;如果传入了 “title” 参数,那么就会对 “title” 一列进行模糊查找并返回对应的 BLOG 结果(细心的读者可能会发现,“title” 的参数值需要包含查找掩码或通配符字符)。
如果希望通过 “title” 和 “author” 两个参数进行可选搜索该怎么办呢?
<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG WHERE state = ‘ACTIVE’
<if test="title != null">
AND title like #{title}
</if>
<if test="author != null and author.name != null">
AND author_name like #{author.name}
</if>
</select>
choose、when、otherwise
有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用。针对这种情况,MyBatis 提供了 choose
元素,它有点像 Java 中的 switch
语句。
还是上面的例子,但是策略变为:传入了 “title” 就按 “title” 查找,传入了 “author” 就按 “author” 查找的情形。若两者都没有传入,就返回标记为 featured 的 BLOG(这可能是管理员认为,与其返回大量的无意义随机 Blog,还不如返回一些由管理员精选的 Blog)。
<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG WHERE state = ‘ACTIVE’
<choose>
<when test="title != null">
AND title like #{title}
</when>
<when test="author != null and author.name != null">
AND author_name like #{author.name}
</when>
<otherwise>
AND featured = 1
</otherwise>
</choose>
</select>
trim、where、set
前面几个例子已经方便地解决了一个臭名昭著的动态 SQL 问题。现在回到之前的 “if
” 示例,这次我们将 “state = ‘ACTIVE’” 设置成动态条件,看看会发生什么。
<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG
WHERE
<if test="state != null">
state = #{state}
</if>
<if test="title != null">
AND title like #{title}
</if>
<if test="author != null and author.name != null">
AND author_name like #{author.name}
</if>
</select>
如果没有匹配的条件会怎么样?最终这条 SQL 会变成这样:
SELECT * FROM BLOG
WHERE
这会导致查询失败。如果匹配的只是第二个条件又会怎样?这条 SQL 会是这样:
SELECT * FROM BLOG
WHERE
AND title like ‘someTitle’
这个查询也会失败。这个问题不能简单地用条件元素来解决。这个问题是如此的难以解决,以至于解决过的人不会再想碰到这种问题。
MyBatis 有一个简单且适合大多数场景的解决办法。而在其他场景中,可以对其进行自定义以符合需求。而这,只需要一处简单的改动:
<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG
<where>
<if test="state != null">
state = #{state}
</if>
<if test="title != null">
AND title like #{title}
</if>
<if test="author != null and author.name != null">
AND author_name like #{author.name}
</if>
</where>
</select>
where
元素只会在子元素返回任何内容的情况下才插入 “WHERE
” 子句。
而且,若子句的开头为 “AND
” 或 “OR
”,where 元素也会将它们去除。
#如果走的是第二个 title
SELECT * FROM BLOG WHERE title like 'sometitle'
如果 where
元素与你期望的不太一样,你也可以通过自定义 trim
元素来定制where
元素的功能。
比如,和 where
元素等价的自定义 trim
元素为:
<trim prefix="WHERE" prefixOverrides="AND |OR ">
...
</trim>
prefixOverrides
属性会忽略通过管道符分隔的文本序列(注意此例中的空格是必要的)。
上述例子会移除所有 prefixOverrides 属性中指定的内容,并且插入 prefix 属性中指定的内容。
用于动态更新语句的类似解决方案叫做 set
。
set
元素可以用于动态包含需要更新的列,忽略其它不更新的列。比如:
<update id="updateAuthorIfNecessary">
update Author
<set>
<if test="username != null">username=#{username},</if>
<if test="password != null">password=#{password},</if>
<if test="email != null">email=#{email},</if>
<if test="bio != null">bio=#{bio}</if>
</set>
where id=#{id}
</update>
这个例子中,set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号(这些逗号是在使用条件语句给列赋值时引入的)。
来看看与 set 元素等价的自定义 trim 元素吧:
<trim prefix="SET" suffixOverrides=",">
...
</trim>
注意,我们覆盖了后缀值设置,并且自定义了前缀值。
foreach
动态 SQL 的另一个常见使用场景是对集合进行遍历(尤其是在构建 IN
条件语句的时候)。比如:
<select id="selectPostIn" resultType="domain.blog.Post">
SELECT *
FROM POST P
<where>
<foreach item="item" index="index" collection="list"
open="ID in (" separator="," close=")" nullable="true">
#{item}
</foreach>
</where>
</select>
foreach
元素的功能非常强大,它允许你指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量。它也允许你指定开头与结尾的字符串以及集合项迭代之间的分隔符。这个元素也不会错误地添加多余的分隔符,看它多智能!
本文来自博客园,作者:{BearBrick0},转载请注明原文链接:{https://www.cnblogs.com/bearbrick0}