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 同样可以这么引用,和上面引用的方式一样,不再赘述了。

 

posted @ 2022-10-23 12:25  邓维-java  阅读(281)  评论(0)    收藏  举报