mybatis 基础知识
mybatis 基础知识
官网:https://mybatis.net.cn/index.html
1、基本的 crud
既然和数据库交互,避免不了 crud 操作,就安心做一个妥妥的crud boy 吧。针对 Mybatis 其实有两套方法映射,一个是 XML 文件的方式,一个是注解的方式。但是今天只讲 XML 文件的方式,原因很简单,注解的方式企业不用,谁用谁倒霉,哈哈。
1.1、查询
查询语句是 MyBatis 中最常用的元素之一——光能把数据存到数据库中价值并不大,还要能重新取出来才有用,多数应用也都是查询比修改要频繁。 MyBatis 的基本原则之一是:在每个插入、更新或删除操作之间,通常会执行多个查询操作。因此,MyBatis 在查询和结果映射做了相当多的改进。一个简单查询的 select 元素是非常简单的。
<select id="selectPersonById" parameterType="int" resultType="com.study.domain.Person">
SELECT name,age,id FROM PERSON WHERE ID = #{id}
</select>
对应的interface 的方法如下:
Person selectPersonById(int id);
<select> 这个标签有很多属性,比较常用的属性如下:
- id (必填):在命名空间中唯一的标识符,可以被用来引用这条语句。和 interface 中 的 方 法 名 要 一 致 。parameterType (可选):将会传入这条语句的参数的类全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过类型处理器(TypeHandler)推断出具体传入语句的参数,默认值为未设置(unset)。
- resultType :期望从这条语句中返回结果的类全限定名或别名。 注意, 如果返回的是集合,那应该设置为集合包含的类型,而不是集合本身的类型。 resultType 和 resultMap 之间只能同时使用一个。
- resultMap :对外部 resultMap 的命名引用。结果映射是 MyBatis 最强大的特性,如果你对其理解透彻,许多复杂的映射问题都能迎刃而解。resultType 和 resultMap 之间只能同时使用一个。
1.2、变更
数据变更语句 insert,update 和 delete 的实现非常接近。下面是 insert,update 和 delete 语句的示例:
<insert id="insertAuthor">
insert into Author (id,username,password,email,bio) values (#{id},#{username},#{password},#{email},#{bio})
</insert>
<update id="updateAuthor"> update Author set
username = #{username}, password = #{password}, email = #{email},bio = #{bio} where id = #{id}
</update>
<delete id="deleteAuthor">
delete from Author where id = #{id}
</delete>
2、#{} 和 ${}的区别
上面的例子中我们可以看到使用的都是#{} ,关于#{} 和${} 的区别也是在很多初级工程师的面试最常被问到的,现在只需要记住区别就是#{} 使用了 JDBC 的预编译,可以防止 SQL 注入,提高了安全性, ${} 并没有预编译,安全性不够。在后面 Mybatis 的源码讲解中将会涉及到为什么一个用了预编译,一个没用。
3、自增 ID 的返回
自增 ID,插入之后并不能自动返回,但是我们又需要这个 ID 值,那么如何返回呢?<insert> 标签提供了两个属性用来解决这个问题,如下:
useGeneratedKeys :设置为 true,表示使用自增主键返回 keyProperty :指定返回的自增主键映射到 parameterType 的哪个属性中。
假设插入Person ,并且 person 表中的自增主键 id 需要返回,XML 文件如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xxx.dao.PersonMapper">
<insert id='addPerson' parameterType='com.xxx.domain.Person' useGeneratedKeys="true" keyProperty="id">
insert into person(name,age) values(#{name},#{age});
</insert>
</mapper>
4、SQL 代码片段
这个元素可以用来定义可重用的 SQL 代码片段,以便在其它语句中使用。 参数可以静态地(在加载的时候)确定下来,并且可以在不同的 include 元素中定义不同的参数值。比如:
<sql id="userColumns">
${alias}.id,${alias}.username,${alias}.password
</sql>
这个 SQL 片段可以在其它语句中使用,例如:
<select id="selectUsers" resultType="map"> select
<include refid="userColumns"><property name="alias" value="t1"/></include>,
<include refid="userColumns"><property name="alias" value="t2"/></include>
from some_table t1
cross join some_table t2
</select>
5、开启驼峰映射
DBA 在设计数据库的时候,往往使用的是下划线( _ )的方式,比如user_id 。但是Java 是不规范的,我们通常将它转换为userId ,这就是驼峰命名方法。但是在使用 Mybatis 查询的时候,比如:
<select id='selectById' resultType='com.xxx.doamin.User'>
select user_id from user_info
</select>
上面的user_id 和User 中的userId 根本不对应,也就映射不进去,此时查询的结果就是 userId 是 null,当然我们可以使用别名的方式,SQL 可以改写为 select user_id as userId from user_info
另外一种方式是不用别名,直接开启 Mybatis 的驼峰映射规则,会自动映射,开启的方式很简单,就是在application.properties 文件配置一下,如下:
mybatis.configuration.map-underscore-to-camel-case=true
6、结果映射
简单的来说就是一条SQL查询语句返回的字段如何与Java实体类中的属性相对应。如下一条SQL语句,查询患者的用户id,科室id,主治医生id:
<select id='selectPatientInfos' resultType='com.xxx.domain.PatientInfo'>
select user_id,dept_id,doc_id from patient_info;
</select>
Java实体类PatientInfo 如下:
@Data public class PatientInfo{
private String userId;
private String deptId;
private String docId;
}
程序员写这条SQL的目的就是想查询出来的user_id , dept_id , doc_id 分别赋值给实体类中的userId , deptId , docId 。这就是简单的结果映射。Myabtis中的结果映射有很多种方式,下面会逐一介绍。
6.1、别名映射
这个简单,保持查询的SQL返回的字段和Java实体类一样即可,比如上面例子的SQL可以写成:
<select id='selectPatientInfos' resultType='com.xxx.domain.PatientInfo'>
select user_id as userId, dept_id as deptId, doc_id as docId from patient_info;
</select>
这样就能和实体类中的属性映射成功了。
6.2、驼峰映射
Mybatis提供了驼峰命名映射的方式,比如数据库中的user_id 这个字段,能够自动映射到userId 属性。那么此时的查询的SQL变成如下即可:
<select id='selectPatientInfos' resultType='com.xxx.domain.PatientInfo'>
select user_id,dept_id,doc_id from patient_info;
</select>
如何开启呢?与SpringBoot整合后开启其实很简单,有两种方式,一个是配置文件中开启,一个是配置类开启。
配置文件开启驼峰映射, 只需要在application.properties 文件中添加如下一行代码即可:
mybatis.configuration.map-underscore-to-camel-case=true
配置类中开启驼峰映射【简单了解,后续源码章节着重介绍】
这种方式需要你对源码有一定的了解,Mybatis与Springboot整合后适配了一个starter,那么肯定会有自动配置类,Mybatis的自动配置类是MybatisAutoConfiguration ,其中有这么一段代码,如下:
@ConditionalOnMissingBean 这个注解的意思就是当IOC容器中没有SqlSessionFactory 这个Bean对象这个配置才会生效; applyConfiguration(factory) 这行代码就是创建一个org.apache.ibatis.session.Configuration 赋值给SqlSessionFactoryBean 。源码分析到这,应该很清楚了,无非就是自己在容器中创建一个SqlSessionFactory ,然后设置属性即可,如下代码:
@Bean("sqlSessionFactory")
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
//设置数据源
sqlSessionFactoryBean.setDataSource(dataSource);
//设置xml文件的位置
sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(MAPPER_LOCATOIN)
);
//创建Configuration
org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
// 开启驼峰命名映射
configuration.setMapUnderscoreToCamelCase(true);
configuration.setDefaultFetchSize(100);
configuration.setDefaultStatementTimeout(30);
sqlSessionFactoryBean.setConfiguration(configuration);
//将typehandler注册到mybatis
sqlSessionFactoryBean.setTypeHandlers(typeHandlers());
return sqlSessionFactoryBean.getObject();
}
6.3、resultMap映射
什么是resultMap ?简单的说就是一个类似Map的结构,将数据库中的字段和JavaBean中的属性字段对应起来,这样就能做到一一映射了。上述的例子使用resultMap又会怎么写呢?如下:
<!--创建一个resultMap映射-->
<resultMap id="patResultMap" type="com.xxx.domain.PatientInfo">
<id property="userId" column="user_id" />
<result property="docId" column="doc_id"/>
<result property="deptId" column="dept_id"/>
</resultMap>
<!--使用resultMap映射结果到com.xxx.domain.PatientInfo这个Bean中-->
<select id='selectPatientInfos' resultMap='patResultMap'>
select user_id,dept_id,doc_id from patient_info;
</select>
其实很简单,就是创建一个<resultMap> ,然后<select> 标签指定这个resultMap即可。
<resultMap> 的属性如下:
-
id :唯一标识这个resultMap,同一个Mapper.xml中不能重复
- type :指定JavaBean的类型,可以是全类名,也可以是别名子标签<result> 的属性如下:
- column :SQL返回的字段名称
- property :JavaBean中属性的名称
-
javaType :一个 Java 类的全限定名,或一个类型别名(关于内置的类型别名,可以参考上面的表格)。 如果你映射到一个 JavaBean,MyBatis 通常可以推断类型。然而,如果你映射到的是 HashMap,那么你应该明确地指定 javaType 来保证行为与期望的相一致。
- jdbcType :JDBC 类型,所支持的 JDBC 类型参见这个表格之后的“支持的 JDBC 类型”。 只需要在可能执行插入、更新和删除的且允许空值的列上指定 JDBC 类型。这是 JDBC 的要求而非 MyBatis 的要求。如果你直接面向 JDBC 编程,你需要对可以为空值的列指定这个类型。
- typeHandler : 这个属性值是一个类型处理器实现类的全限定名,或者是类型别名。
- resultMap :结果映射的 ID,可以将此关联的嵌套结果集映射到一个合
6.4、高级结果映射
我们知道在数据库的关系中一对一,多对一,一对多,多对多的关系,那么这种关系如何在Mybatis中体现并映射成功呢?
1、关联(association)
关联(association)元素处理有一个类型的关系。 比如,在我们的示例中,一个员工属于一个部门。关联结果映射和其它类型的映射工作方式差不多。 你需要指定目标属性名以及属性的javaType (很多时候 MyBatis 可以自己推断出来),在必要的情况下你还可以设置 JDBC 类型,如果你想覆盖获取结果值的过程,还可以设置类型处理器。
- property : 映射到列结果的字段或属性。如果用来匹配的 JavaBean 存在给定名字的属性,那么它将会被使用。
- javaType :一个 Java 类的完全限定名,或一个类型别名(关于内置的类型别名,可以参考上面的表格)
- jdbcType : JDBC 类型, 只需要在可能执行插入、更新和删除的且允许空值的列上指定 JDBC 类型
- typeHandler :使用这个属性,你可以覆盖默认的类型处理器。 这个属性值是一个类型处理器实现类的完全限定名,或者是类型别名。
- column : 数据库中的列名,或者是列的别名。一般情况下,这和传递给resultSet.getString(columnName) 方法的参数一样。 注意:在使用复合主键的时候,你可以使用 column="{prop1=col1,prop2=col2}" 这样的语法来指定多个传递给嵌套 Select 查询语句的列名。这会使得prop1 和 prop2 作为参数对象,被设置为对应嵌套 Select 语句的参数。
- select :用于加载复杂类型属性的映射语句的 ID,它会从 column 属性指定的列中检索数据,作为参数传递给目标 select 语句。 具体请参考下面的例子。注意:在使用复合主键的时候,你可以使用column="{prop1=col1,prop2=col2}" 这样的语法来指定多个传递给嵌套 Select 查询语句的列名。这会使得 prop1 和 prop2 作为参数对象,被设置为对应嵌套 Select 语句的参数。
- fetchType :可选的。有效值为 lazy 和 eager 。 指定属性后,将在映射中忽略全局配置参数 lazyLoadingEnabled ,使用属性的值。
例子:
一对一的关系比如:一个员工属于一个部门,那么数据库表就会在员工表中加一个部门的id作为逻辑外键。
创建员工JavaBean:
@Data public class User {
private Integer id; private String username;
private String password;
private Integer age; private Integer deptId; //部门 private Department department; }
部门JavaBean:
@Data
public class Department {
private Integer id;
private String name;
}
那么我们想要查询所有的用户信息和其所在的部门信息,此时的sql语句为: select* from user u left join department d on u.department_id=d.id ;。但是我们在mybaits中如果使用这条语句查询,那么返回的结果类型是什么呢?如果是User类型的,那么查询结果返回的还有Department 类型的数据,那么肯定会对应不上的。此时<resultMap> 来了,它来了!!!
关联的嵌套 Select 查询(不推荐使用)
查询员工和所在的部门在Mybatis如何写呢?代码如下:
<resultMap id="userResult" type="com.xxx.domain.User">
<id column="id" property="id"/>
<result column="password" property="password"/>
<result column="age" property="age"/>
<result column="username" property="username"/>
<result column="dept_id" property="deptId"/>
<!--关联查询,select嵌套查询-->
<association property="department" column="dept_id" javaType="com.xxx.domain.Department" select="selectDept"/>
</resultMap>
<!--查询员工-->
<select id="selectUser" resultMap="userResult">
SELECT * FROM user WHERE id = #{id}
</select>
<!--查询部门-->
<select id="selectDept" resultType="com.xxx.domain.Department ">
SELECT * FROM department WHERE ID = #{id}
</select>
就是这么简单,两个select语句,一个用来加载员工,一个用来加载部门。这种方式虽然很简单,但在大型数据集或大型数据表上表现不佳。这个问题被称为N+1 查询问题。 概括地讲,N+1 查询问题是这样子的:你执行了一个单独的 SQL 语句来获取结果的一个列表(就是+1 )。对列表返回的每条记录,你执行一个 select 查询语句来为每条记录加载详细信息(就是N )。
这个问题会导致成百上千的 SQL 语句被执行。有时候,我们不希望产生这样的后果。
关联的嵌套结果映射(推荐)
<association > 标签中还可以直接嵌套结果映射,此时的Mybatis的查询如下:
<!-- 定义resultMap -->
<resultMap id="UserDepartment" type="com.xxx.domain.User" >
<id column="user_id" property="id"/>
<result column="password" property="password"/>
<result column="age" property="age"/>
<result column="username" property="username"/>
<result column="dept_id" property="deptId"/>
<!-- property: 指定User中对应的部门属性名称; javaType: 指定类型,可以是全类名或者别名 -->
<association property="department" javaType="com.xx.domain.Department">
<!--指定Department中的属性映射,这里也可以使用单独拎出来,然后使用association中的resultMap属性指定-->
<id column="id" property="id"/>
<result column="dept_name" property="name"/>
</association>
</resultMap>
<!-- resultMap: 指定上面resultMap的id的值 -->
<select id="findUserAndDepartment" resultMap="UserDepartment">
select u.id as user_id, u.dept_id, u.name, u.password, u.age, d.id, d.name as dept_name
from user u
left join department d on u.department_id=d.id
</select>
注意: 关联的嵌套 Select 查询不建议使用, N+1 是个重大问题,虽说Mybatis提供了延迟加载的功能,但是仍然不建议使用,企业开发中也是不常用的。
6.5、集合collection
集合,顾名思义,就是处理 有很多个类型的关联。其中的属性和association 中的属性类似,不再重复了。
比如这样一个例子:查询一个部门中的全部员工,查询SQL如何写呢?如下:
select * from department d left join user u on u.department_id=d.id;
此时的User 实体类如下:
@Data public class User {
private Integer id;
private String username;
private String password;
private Integer age;
private Integer deptId; }
此时的Department 实体类如下:
@Data public class Department {
private Integer id;
private String name;
private List<User> users; }
和association 类似,同样有两种方式,我们可以使用嵌套 Select 查询,或基于连接的嵌套结果映射集合。
集合的嵌套 Select 查询【不推荐】
<resultMap id="deptResult" type="com.xxx.domain.Department">
<!--指定Department中的属性映射,这里也可以使用单独拎出来,然后使用association中的resultMap属性指定-->
<id column="id" property="id"/>
<result column="name" property="name"/>
<!--ofType:指定实际的JavaBean的全类型或者别名select:指定嵌套的select查询javaType:集合的类型,可以不写,Mybatis可以推测出来-->
<collection property="users" javaType="java.util.ArrayList" column="id" ofType="com.xxx.doamin.User" select="selectByDeptId"/>
</resultMap>
<select id="selectDept" resultMap="deptResult">
SELECT * FROM department WHERE ID = #{id}
</select>
<select id="selectByDeptId" resultType="com.xxx.domain.User">
SELECT * FROM user WHERE dept_id = #{id}
</select>
集合的嵌套结果映射(推荐方式)
<!--部门的resultMap--> <resultMap id="deptResult" type="com.xxx.domain.Department"> <!--指定Department中的属性映射,这里也可以使用单独拎出来,然后使用association中的resultMap属性指定--> <id column="dept_id" property="id"/> <result column="dept_name" property="name"/> <!--ofType:指定实际的JavaBean的全类型或者别名resultMap:指定员工的resultMap--> <collection property="users" ofType="com.xxx.doamin.User" resultMap='userResult'/> </resultMap> <!--员工的resultMap--> <resultMap id="userResult" type="com.xxx.domain.User"> <id column="user_id" property="id"/> <result column="password" property="password"/> <result column="age" property="age"/> <result column="username" property="username"/> </resultMap> <select id="selectDeptById" resultType="com.xxx.domain.Department">
select d.id as dept_id, d.name as dept_name, u.id as user_id, u.password,u.name from department d
left join user u on u.department_id=d.id where d.id=#{id} </select>
7、Mybatis动态SQL
动态 SQL 是 MyBatis 的强大特性之一。顾名思义,就是会动的SQL,即是能够灵活的根据某种条件拼接出完整的SQL语句。这种类似于MySQL中的case whenthen else then end.... 这种语法,能够根据某种条件动态的拼接出需要的SQL。至于Mybatis如何实现动态SQL呢,Mybatis提供了非常多的标签,能够让我们在XML文件中灵活的运用这些标签达到拼接SQL的目的。
常用的标签
7.1、if
只有判断条件为true 才会执行其中的SQL语句。示例:
<select id ='selectPats' resultType='com.xxx.domain.PatientInfo'>
select * from patient_info where status=1
<!--前端传来的住院号不为null,表示需要根据住院号筛选,此时Where语句就需要加上 这个条件-->
<if test="iptNum!=null">
and ipt_num=#{iptNum}
</if>
<!--床位号筛选-->
<if test="bedNum!=null">
and bed_num=#{bedNum}
</if>
</select>
<if> 标签中的属性test 用来指定判断条件,那么问题来了,上面的例子中的test 中判断条件都是一个条件,如果此时变成两个或者多个条件呢?和SQL的语法类似, and 连接即可,如下:
<if test="bedNum!=null and bedNum!='' ">
and bed_num=#{bedNum} </if>
除了 and 关键字还有其他的支持如下:
<EOF>
"," ...
"=" ...
"?" ...
"||" ...
"or" ...
"&&" ...
"and" ...
"|" ...
"bor" ...
"^" ...
"xor" ...
"&" ...
"band" ...
"==" ...
"eq" ...
"!=" ...
"neq" ...
"<" ...
"lt" ...
">" ...
"gt" ...
"<=" ...
"lte" ...
">=" ...
"gte" ...
"in" ...
"not" ...
"<<" ...
"shl" ...
">>" ...
"shr" ...
">>>" ...
"ushr" ...
"+" ...
"-" ...
"*" ...
"/" ...
"%" ...
"instanceof" ...
"." ...
"(" ...
"[" ...
<DYNAMIC_SUBSCRIPT> ...
7.2、choose、when、otherwise
有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。
还是上面的例子改变一下:此时只能满足一个筛选条件,如果前端传来住院号就只按照住院号查找,如果传来床位号就只按照床位号筛选,如果什么都没传,就筛选所有在院的。此时的查询如下:
<select id="selectPats" resultType="com.xxx.domain.PatientInfo">
select * from patient_info where 1=1
<choose>
<!--住院号不为null时,根据住院号查找-->
<when test="iptNum != null"> AND ipt_num=#{iptNum} </when>
<!--床位号不是NUll-->
<when test="bedNum != null"> AND bed_num = #{bedNum} </when>
<otherwise>
AND status=1
</otherwise>
</choose>
</select>
MyBatis 提供了 choose 元素,按顺序判断 when 中的条件出否成立,如果有一个成立,则 choose 结束。当 choose 中所有 when 的条件都不满则时,则执行otherwise 中的 sql。类似于 Java 的 switch 语句, choose 为 switch , when 为case , otherwise 则为default。
7.3、where
举个栗子:对于choose 标签的例子中的查询,如果去掉where 后的1=1 此时的SQL 语句会变成什么样子,有三种可能的SQL,如下:
select * from patient_info where AND ipt_num=#{iptNum};
select * from patient_info where AND bed_num = #{bedNum};
select * from patient_info where AND status=1;
发生了什么,以上三条SQL语句对吗?很显然是不对的,显然where 后面多了个AND 。如何解决呢?此时就要用到where 这个标签了。where 元素只会在子元素返回任何内容的情况下才插入 WHERE 子句。而且,若子句的开头为 AND 或 OR , where 元素也会将它们去除。
此时的查询改造如下:
<select id="selectPats" resultType="com.xxx.domain.PatientInfo">
select * from patient_info
<where>
<choose>
<!--住院号不为null时,根据住院号查找-->
<when test="iptNum != null"> AND ipt_num=#{iptNum} </when>
<!--床位号不是NUll-->
<when test="bedNum != null"> AND bed_num = #{bedNum} </when>
<otherwise> AND status=1 </otherwise>
</choose>
</where>
</select>
7.4、foreach
foreach 是用来对集合的遍历,这个和Java中的功能很类似。通常处理SQL中的in 语 句 。 foreach 元素的功能非常强大,它允许你指定一个集合,声明可以在元素体内使用的集合项( item )和索引( index )变量。它也允许你指定开头与结尾的字符串以及集合项迭代之间的分隔符。这个元素也不会错误地添加多余的分隔符。你可以将任何可迭代对象(如 List 、Set 等)、Map 对象或者数组对象作为集合参数传递给 foreach。当使用可迭代对象或者数组时, index 是当前迭代的序号, item 的值是本次迭代获取到的元素。当使用 Map 对象(或者 Map.Entry 对象的集合)时, index 是键, item 是值。
示例:
<select id="selectPats" resultType="com.xxx.domain.PatientInfo">
SELECT * FROM patient_info WHERE ID in <foreach item="item" index="index" collection="list" open="(" separator="," close=")"> #{item} </foreach> </select>
改标签中的各个属性的含义如下:
- item 表示在迭代过程中每一个元素的别名
-
index 表示在迭代过程中每次迭代到的位置(下标)
- open 前缀
- close 后缀
- separator 分隔符,表示迭代时每个元素之间以什么分隔
7.5、set
讲这个标签之前,先看下面这个例子:
<update id="updateStudent" parameterType="Object">
UPDATE STUDENT SET NAME = #{name},MAJOR = #{major}, HOBBY = #{hobby} WHERE ID = #{id};
</update>
<update id="updateStudent" parameterType="Object">
UPDATE STUDENT SET
<if test="name!=null and name!='' "> NAME = #{name},</if>
<if test="hobby!=null and hobby!='' "> MAJOR = #{major},</if>
<if test="hobby!=null and hobby!='' "> HOBBY = #{hobby}</if>
WHERE ID = #{id};
</update>
上面的例子中没有使用 if 标签时,如果有一个参数为 null ,都会导致错误。当在 update 语句中使用 if 标签时,如果最后的 if 没有执行,则或导致逗号多余错误。使用 set 标签可以将动态的配置 set 关键字,和剔除追加到条件末尾的任何不相关的逗号。
使用 set+if 标签修改后,如果某项为 null 则不进行更新,而是保持数据库原值。
此时的查询如下:
<update id="updateStudent" parameterType="Object">
UPDATE STUDENT
<set>
<if test="name!=null and name!='' "> NAME = #{name},</if>
<if test="hobby!=null and hobby!='' "> MAJOR = #{major},</if>
<if test="hobby!=null and hobby!='' "> HOBBY = #{hobby}</if>
</set>
WHERE ID = #{id};
</update>
7.6、sql
在实际开发中会遇到许多相同的SQL,比如根据某个条件筛选,这个筛选很多地方都能用到,我们可以将其抽取出来成为一个公用的部分,这样修改也方便,一旦出现了错误,只需要改这一处便能处处生效了,此时就用到了<sql> 这个标签了。当多种类型的查询语句的查询字段或者查询条件相同时,可以将其定义为常量,方便调用。为求 <select> 结构清晰也可将 sql 语句分解。如下:
<!-- 查询字段 -->
<sql id="Base_Column_List"> ID,MAJOR,BIRTHDAY,AGE,NAME,HOBBY </sql>
<!-- 查询条件 -->
<sql id="Example_Where_Clause">
where 1=1
<trim suffixOverrides=",">
<if test="id != null and id !=''"> and id = #{id}</if>
<if test="major != null and major != ''"> and MAJOR = #{major}</if>
<if test="birthday != null "> and BIRTHDAY = #{birthday}</if>
<if test="age != null "> and AGE = #{age}</if>
<if test="name != null and name != ''"> and NAME = #{name}</if>
<if test="hobby != null and hobby != ''"> and HOBBY = #{hobby}</if>
</trim>
</sql>
7.7、include
这个标签和<sql> 是天仙配,是共生的, include 用于引用sql 标签定义的常量。比如引用上面sql标签定义的常量,如下:
<select id="selectAll" resultMap="BaseResultMap"> SELECT
<include refid="Base_Column_List" /> FROM student
<include refid="Example_Where_Clause" />
</select>
- refid 这个属性就是指定<sql> 标签中的id 值(唯一标识)。
总结:至此,Mybatis动态SQL中常用的标签就已经介绍完了,这部分的内容在实际工作 中是必须会用到的,除非你们公司不用Mybatis。
8、Mybatis中如何避免魔数?
开过阿里巴巴开发手册的大概都知道代码中是不允许出现 魔数的,何为 魔数?简单的说就是一个数字,一个只有你知道,别人不知道这个代表什么意思的数字。通常我们在Java代码中都会定义一个常量类专门定义这些数字。
<if test="type!=null and type==1">
-- ....获取医生的权限
</if>
<if test="type!=null and type==2">
-- ....获取护士的权限
</if>
这样写也没什么错,但是一旦这个type 代表的含义变了,那你是不是涉及到的SQL都要改一遍。开发中通常定义一个常量类,如下:
package com.xxx.core.Constants;
public class CommonConstants{ //医生 public final static int DOC_TYPE=1; //护士 public final static int NUR_TYPE=2; }
那么此时的SQL应该如何写呢?如下:
<if test="type!=null and type==@com.xxx.core.Constants.CommonConstants@DOC_TYPE">
-- ....获取医生的权限
</if>
<if test="type!=null and type==@com.xxx.core.Constants.CommonConstants@NUR_TYPE">
-- ....获取护士的权限
</if>
就是这么简单,就是@ + 全类名+ @ + 常量。除了调用常量类中的常量,还可以类中的方法,很少用到,不再介绍了,感兴趣的可以问下度娘。
9、如何引用其他XML中的SQL片段?
实际开发中你可能遇到一个问题,比如这个resultMap 或者这个<sql> 片段已经在另外一个xxxMapper.xml 中已经定义过了,此时当前的xml还需要用到,难不成我复制一份?小白什么也不问上来就复制了,好吧,后期修改来了,每个地方都需要修改了。难受不?
其实Mybatis中也是支持引用其他Mapper文件中的SQL片段的。其实很简单,比如:
你在com.xxx.dao.xxMapper 这个Mapper的XML中定义了一个SQL片段如下:
<sql id="Base_Column_List"> ID,MAJOR,BIRTHDAY,AGE,NAME,HOBBY </sql>
此时我在com.xxx.dao.PatinetMapper 中的XML文件中需要引用,如下:
<include refid="com.xxx.dao.xxMapper.Base_Column_List"></include>
如此简单,类似于Java中的全类名。<select> 标签中的resultMap 同样可以这么引用,和上面引用的方式一样,不再赘述了。