动态sql_动态sql查询 - CSDN
精华内容
参与话题
  • MyBatis——动态SQL总结

    千次阅读 多人点赞 2018-05-02 10:31:03
    MyBatis的动态SQL是基于OGNL表达式的,它可以帮助我们方便的在SQL语句中实现某些逻辑。 MyBatis中用于实现动态SQL的元素主要有:ifwheresetchoose(when,otherwise)trimforeach (1)if标签此时如果...

    MyBatis的动态SQL是基于OGNL表达式的,它可以帮助我们方便的在SQL语句中实现某些逻辑。 
    MyBatis中用于实现动态SQL的元素主要有:

    • if
    • where
    • set
    • choose(when,otherwise)
    • trim
    • foreach    

    (1)if标签

    此时如果CNAINDCLABASINFID为null,此语句很可能报错或查询结果为空。此时我们使用if动态sql语句先进行判断,如果值为null或等于空字符串,我们就不进行此条件的判断,增加灵活性。

    <where>
       com.CNAINDCLABASINFID = #{industryNum}
       <if test="id!=null and id!=''"><!-- 项目编号 -->
          and bas.id = #{id}
       </if>
       <if test="projectName!=null and projectName!=''"><!-- 项目名称 -->
          and bas.PROJECT_NAME = #{projectName}
       </if>
    </where>

    (2)if + where 条件判断

    如上所示

    (3)if + set 更新语句

    当update语句中没有使用if标签时,如果有一个参数为null,都会导致错误。 
    当在update语句中使用if标签时,如果前面的if没有执行,则或导致逗号多余错误。使用set标签可以将动态的配置SET 关键字,和剔除追加到条件末尾的任何不相关的逗号。如果set包含的内容为空的话则会出错。

    使用if+set标签修改后,如果某项为null则不进行更新,而是保持数据库原值。如下示例:

    <update id="editRateAnalysTask" parameterType="RateAnalystScale" >
    UPDATE STUDENT_TBL
    <set>
       <if test="studentName != null and studentName != '' ">
          STUDENT_TBL.STUDENT_NAME = #{studentName},
       </if>
       <if test="studentSex != null and studentSex != '' ">
          STUDENT_TBL.STUDENT_SEX = #{studentSex},
       </if>
       <if test="studentBirthday != null ">
          STUDENT_TBL.STUDENT_BIRTHDAY = #{studentBirthday},
       </if>
       <if test="studentPhoto != null ">
          STUDENT_TBL.STUDENT_PHOTO = #{studentPhoto, javaType=byte[], jdbcType=BLOB, typeHandler=org.apache.ibatis.type.BlobTypeHandler},
       </if>
       <if test="classId != '' ">
          STUDENT_TBL.CLASS_ID = #{classId}
       </if>
       <if test="placeId != '' ">
          STUDENT_TBL.PLACE_ID = #{placeId}
       </if>
    </set>
    WHERE STUDENT_TBL.STUDENT_ID = #{studentId};
    </update>

    (4)choose (when,otherwise)

            有时候我们并不想应用所有的条件,而只是想从多个选项中选择一个。而使用if标签时,只要test中的表达式为true,就会执行if标签中的条件。MyBatis提供了choose 元素。if标签是与(and)的关系,而choose标签是或(or)的关系.

            choose标签是按顺序判断其内部when标签中的test条件出否成立,如果有一个成立,则choose结束当choose中所有when的条件都不满则时,则执行otherwise中的sql。类似于Java 的switch 语句,choose为switch,when为case,otherwise则为default。

    例如下面例子,同样把所有可以限制的条件都写上,方面使用。choose会从上到下选择一个when标签的test为true的sql执行。安全考虑,我们使用where将choose包起来,放置关键字多于错误。

    <select id="">
       SELECT ST.STUDENT_ID,
       ST.STUDENT_NAME,
       ST.STUDENT_SEX,
       ST.STUDENT_BIRTHDAY,
       ST.STUDENT_PHOTO,
       ST.CLASS_ID,
       ST.PLACE_ID
       FROM STUDENT_TBL ST
       <where>
          <choose>
             <when test="studentName !=null ">
                ST.STUDENT_NAME LIKE CONCAT(CONCAT('%', #{studentName, jdbcType=VARCHAR}),'%')
             </when >
             <when test="studentSex != null and studentSex != '' ">
                AND ST.STUDENT_SEX = #{studentSex, jdbcType=INTEGER}
             </when >
             <when test="studentBirthday != null ">
                AND ST.STUDENT_BIRTHDAY = #{studentBirthday, jdbcType=DATE}
             </when >
             <when test="classId != null and classId!= '' ">
                AND ST.CLASS_ID = #{classId, jdbcType=VARCHAR}
             </when >
             <when test="classEntity != null and classEntity.classId !=null and classEntity.classId !=' ' ">
                AND ST.CLASS_ID = #{classEntity.classId, jdbcType=VARCHAR}
             </when >
             <when test="placeId != null and placeId != '' ">
                AND ST.PLACE_ID = #{placeId, jdbcType=VARCHAR}
             </when >
             <when test="placeEntity != null and placeEntity.placeId != null and placeEntity.placeId != '' ">
                AND ST.PLACE_ID = #{placeEntity.placeId, jdbcType=VARCHAR}
             </when >
             <when test="studentId != null and studentId != '' ">
                AND ST.STUDENT_ID = #{studentId, jdbcType=VARCHAR}
             </when >
             <otherwise>
             </otherwise>
          </choose>
       </where>
    </select>

    (5)trim标签

    trim元素的主要功能是可以在自己包含的内容前加上某些前缀,也可以在其后加上某些后缀,与之对应的属性是prefixsuffix;可以把包含内容的首部某些内容覆盖,即忽略,也可以把尾部的某些内容覆盖,对应的属性是prefixOverridessuffixOverrides。正因为trim有这样的功能,所以我们也可以非常简单的利用trim来代替where/set标签的功能,示例代码如下:

    trim代替where标签:

     select * from user 

      <trim prefix="WHERE" prefixoverride="AND |OR">

        <if test="name != null and name.length()>0"> AND name=#{name}</if>

        <if test="gender != null and gender.length()>0"> AND gender=#{gender}</if>

      </trim>

    假如说name和gender的值都不为null的话打印的SQL为:select * from user where    name = 'xx' and gender = 'xx'

      在红色标记的地方是不存在第一个and的,上面两个属性的意思如下:

      prefix:前缀      

      prefixoverride:去掉第一个and或者是or

    trim代替set标签:

    update user

      <trim prefix="set" suffixoverride="," suffix=" where id = #{id} ">

        <if test="name != null and name.length()>0"> name=#{name} , </if>

        <if test="gender != null and gender.length()>0"> gender=#{gender} ,  </if>

      </trim>

    假如说name和gender的值都不为null的话打印的SQL为:update user set name='xx' , gender='xx'     where id='x'

      在红色标记的地方不存在逗号,而且自动加了一个set前缀和where后缀,上面三个属性的意义如下,其中prefix意义如上:

      suffixoverride:去掉最后一个逗号(也可以是其他的标记,就像是上面前缀中的and一样)

      suffix:后缀

    (6)foreach 标签

    foreach的主要用在构建in条件中,它可以在SQL语句中进行迭代一个集合。foreach元素的属性主要有item,index,collection,open,separator,close。

    • item表示集合中每一个元素进行迭代时的别名;
    • index指定一个名字,用于表示在迭代过程中,每次迭代到的位置;
    • open表示该语句以什么开始;
    • separator表示在每次进行迭代之间以什么符号作为分隔符;
    • close表示以什么结束;
    在使用foreach的时候最关键的也是最容易出错的就是collection属性,该属性是必须指定的,但是在不同情况下,该属性的值是不一样的,主要有一下3种情况: 
    • 如果传入的是单参数且参数类型是一个List的时候,collection属性值为list
    • 如果传入的是单参数且参数类型是一个array数组的时候,collection的属性值为array
    • 如果传入的参数是多个的时候,我们就需要把它们封装成一个Map了,当然单参数也可以封装成map,实际上如果你在传入参数的时候,在MyBatis里面也是会把它封装成一个Map的,map的key就是参数名,所以这个时候collection属性值就是传入的List或array对象在自己封装的map里面的key

    1)单参数List的类型:

    <select id="dynamicForeachTest" resultType="Blog">  
        select * from t_blog where id in  
        <foreach collection="list" index="index" item="item" open="(" separator="," close=")">  
            #{item}  
        </foreach>  
    </select>  

    上述collection的值为list,对应的Mapper是这样的:

    public List<Blog> dynamicForeachTest(List<Integer> ids); 

    2)单参数array数组的类型:

    <select id="dynamicForeach2Test" resultType="Blog">  
        select * from t_blog where id in  
        <foreach collection="array" index="index" item="item" open="(" separator="," close=")">  
            #{item}  
        </foreach>  
    </select>  

    上述collection为array,对应的Mapper代码:

    public List<Blog> dynamicForeach2Test(int[] ids);  

    3)自己把参数封装成Map的类型

    <select id="dynamicForeach3Test" resultType="Blog">  
        select * from t_blog where title like "%"#{title}"%" and id in  
        <foreach collection="ids" index="index" item="item" open="(" separator="," close=")">  
            #{item}  
        </foreach>  
    </select>  

    上述collection的值为ids,是传入的参数Map的key,对应的Mapper代码:

    public List<Blog> dynamicForeach3Test(Map<String, Object> params); 


    展开全文
  • Mybatis之动态sql标签

    万次阅读 2018-03-24 18:16:15
    1.Mybatis动态sql MyBatis 的强大特性之一便是它的动态 SQL。如果你有使用 JDBC 或其它类似框架的经验,你就能体会到根据不同条件拼接 SQL 语句的痛苦。例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表...

    1.Mybatis动态sql

    MyBatis 的强大特性之一便是它的动态 SQL。如果你有使用 JDBC 或其它类似框架的经验,你就能体会到根据不同条件拼接 SQL 语句的痛苦。例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL 这一特性可以彻底摆脱这种痛苦。
    虽然在以前使用动态 SQL 并非一件易事,但正是 MyBatis 提供了可以被用在任意 SQL 映射语句中的强大的动态 SQL 语言得以改进这种情形。
    动态 SQL 元素和 JSTL 或基于类似 XML 的文本处理器相似。在 MyBatis 之前的版本中,有很多元素需要花时间了解。MyBatis 3 大大精简了元素种类,现在只需学习原来一半的元素便可。MyBatis 采用功能强大的基于 OGNL 的表达式来淘汰其它大部分元素。

    2.常见的动态sql标签

    2.1 if

    在现实的工作场景中,我们通常需要按照不同的维度对数据进行查询。比如我们
    通过员工管理系统要查询一个name 为”Tom”的人,在大一点的公司可能有几个name都为”Tom”的同事并且他们有可能分部在不同的部门,而在小点的公司可能只有一个人根本就不用按部门来过滤,这个时候我们可以通过传参来控制我们的过滤条件如下:

    /**
     * @Description employee的dao层代码
     * @Author xiaoqx <Javxuan@163.com>
     * @Version V1.0.0
     * @Since 2017/11/26
     */
    public interface EmployeeMapper {
    
        List<Employee> selectEmployeeList(Employee employee);
    }
    <select id="selectEmployeeList" resultType="com.worldly.config.entity.Employee" databaseId="mysql">
            select
            *
            from t_emp e
            where
                <if test="name!=null and name!=''">
                    e.emp_name=#{name,jdbcType=VARCHAR}
                </if>
                <if test="dep!=null">
                    and e.emp_dep=#{dep.id,jdbcType=INTEGER}
                </if>
        </select>

    配合一个“_databaseId”变量的 databaseIdProvider 可用于动态代码中,这样就可以根据不同的数据库厂商构建特定的语句。比如下面的例子:

    <insert id="insert">
      <selectKey keyProperty="id" resultType="int" order="BEFORE">
        <if test="_databaseId == 'oracle'">
          select seq_users.nextval from dual
        </if>
        <if test="_databaseId == 'db2'">
          select nextval for seq_users from sysibm.sysdummy1"
        </if>
      </selectKey>
      insert into users values (#{id}, #{name})
    </insert>

    2.2 where

    我们可以想象一下如果我们只要按部门编号查询某个部门的同事时,生成的sql 语句会是怎么样的? 很容易得出结论,最终生成的sql 就会如下:

    这里写图片描述
    执行后将会报sql语法错误。我们可以用另外一个动态标签来解决这个问题:

     <select id="selectEmployeeList" resultType="com.worldly.config.entity.Employee" databaseId="mysql">
            select
            *
            from t_emp e
            <where>
                <if test="name!=null and name!=''">
                    and  e.emp_name=#{name,jdbcType=VARCHAR}
                </if>
                <if test="dep!=null">
                    and e.emp_dep=#{dep.id,jdbcType=INTEGER}
                </if>
            </where>
        </select>

    只要将sql放入where动态标签内,至少有一个条件符合的时候,才会插入where语句并且会将条件语句前的 and 去掉。
    这里写图片描述

    2.3 trim

    常用的属性:
    prefix=”where”//给第一符合条件的语句 加上前缀where
    prefixOverrides=”and” //将最后一条语句的 前缀and 覆盖
    suffix=”and” //给第一符合条件的语句 加上后缀 and
    suffixOverrides=”and”//将最后一条语句的后缀 and 覆盖
    当我们把条件语句重新排版一下如下:

    <select id="selectEmployeeList" resultType="com.worldly.config.entity.Employee" databaseId="mysql">
            select
            *
            from t_emp e
            <where>
                <if test="name!=null and name!=''">
                      e.emp_name=#{name,jdbcType=VARCHAR} and
                </if>
                <if test="dep!=null">
                    and  e.emp_dep=#{dep.id,jdbcType=INTEGER} and
                </if>
            </where>
        </select>

    然后运行,结果如下:发现 动态where 标签只会去除 条件语句的第一个and ,这时候动态where就解决不了这个问题了,就有了一个新的动态标签trim
    这里写图片描述
    动态xml代码

      <select id="selectEmployeeList" resultType="com.worldly.config.entity.Employee" databaseId="mysql">
            select
            *
            from t_emp e
            //表示给第一个符合条件的语句前加 where,把最后一个语句的suffixOverrides="and" 指定的and 覆盖掉
              <trim  prefix="where" suffixOverrides="and">
                <if test="name!=null and name!=''">
                      e.emp_name=#{name,jdbcType=VARCHAR} and
                </if>
                <if test="dep!=null">
                      e.emp_dep=#{dep.id,jdbcType=INTEGER} and
                </if>
              </trim>
        </select>

    2.4 set

    类似的用于动态更新语句的解决方案叫做 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 关键字,同时也会删掉无关的逗号,因为用了条件语句之后很可能就会在生成的 SQL 语句的后面留下这些逗号。(译者注:因为用的是“if”元素,若最后一个“if”没有匹配上而前面的匹配上,SQL 语句的最后就会有一个逗号遗留)

    2.5 choose

    有时我们不想应用到所有的条件语句,而只想从中择其一项。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。
    还是上面的例子,但是这次变为提供了“title”就按“title”查找,提供了“author”就按“author”查找的情形,若两者都没有提供,就返回所有符合条件的 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>

    2.6 foreach

    常用的属性:
    collection 要遍历的集合;
    item 要遍历的元素;
    index 元素在集合中的索引;
    open 遍历以什么开头 比如 open=”and id in (“;
    seprator 遍历出来的元素以什么分隔;
    end 遍历以什么结束 end=”)”
    动态 SQL 的另外一个常用的操作需求是对一个集合进行遍历,通常是在构建 IN 条件语句的时候。比如:

    <select id="selectPostIn" resultType="domain.blog.Post">
      SELECT *
      FROM POST P
      WHERE ID in
      <foreach item="item" index="index" collection="list"
          open="(" separator="," close=")">
            #{item}
      </foreach>
    </select>

    你可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象传递给 foreach 作为集合参数。当使用可迭代对象或者数组时,index 是当前迭代的次数,item 的值是本次迭代获取的元素。当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。

    2.7 bind

    bind 元素可以从 OGNL 表达式中创建一个变量并将其绑定到上下文。这个动态标签可以完美解决#{}在某些时候不适用,而用美元{}又有sql注入的风险的情况( ${}与#{}的区别)比如:

    <select id="selectBlogsLike" resultType="Blog">
      <bind name="pattern" value="'%' + _parameter.getTitle() + '%'" />
      SELECT * FROM BLOG
      WHERE title LIKE #{pattern}
    </select>

    2.8 insert

    批量插入mysql 与oracle的区别:

    2.8.1 mysql 批量插入

    插入语句

    <insert id="insertEmp">
            insert into t_emp (id,username)
            values 
            <foreach collection="userList" item="u" separator="," open="(" close=")">
              #{u.id},#{u.username}
            </foreach>
    </insert>

    预编译结果

    insert into t_emp (id,username)
     values(?,?),(?,?),(?,?)

    你可能会想把整个插入语句进行循环如下:
    用;来分隔每一条插入语句

       <insert id="insertEmp">
            <foreach collection="userList" item="u" separator=";">
            insert into t_emp (id,username)
            values (#{u.id},#{u.username} )
            </foreach>
        </insert>
    

    预编译结结果

    insert into t_emp (id,username) values (?,?);
    insert into t_emp (id,username) values (?,?);
    insert into t_emp (id,username) values (?,?);

    mysql默认是不支持这种语法,需要在url 后面的连接属性增加一个 allowMultiQueries=true; 该属性默认是关闭的。

    2.8.2 oracle批量插入

    oracle并不支持mysql这种语法

    insert into t_emp (id,username) values(?,?),(?,?),(?,?) 

    他只能通过如下来完成插入

    begin 
    insert into t_emp (id,username) values(?,?); 
    insert into t_emp (id,username) values(?,?); 
    insert into t_emp (id,username) values(?,?); 
    end;

    2.9 sql

    这个元素可以被用来定义可重用的 SQL 代码段,可以包含在其他语句中。它可以被静态地(在加载参数) 参数化. 不同的属性值通过包含的实例变化. 比如:

    <sql id="userColumns"> ${alias}.id,${alias}.username,${alias}.password </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>
    展开全文
  • 动态SQL

    千次阅读 2017-10-17 11:55:20
    1 动态SQL 那么,问题来了: 什么是动态SQL? 动态SQL有什么作用? 传统的使用JDBC的方法,相信大家在组合复杂的的SQL语句的时候,需要去拼接,稍不注意哪怕少了个空格,都会导致错误。Mybatis的动态SQL功能正是...

    1 动态SQL

    那么,问题来了: 什么是动态SQL? 动态SQL有什么作用?

    传统的使用JDBC的方法,相信大家在组合复杂的的SQL语句的时候,需要去拼接,稍不注意哪怕少了个空格,都会导致错误。Mybatis的动态SQL功能正是为了解决这种问题, 其通过 if, choose, when, otherwise, trim, where, set, foreach标签,可组合成非常灵活的SQL语句,从而提高开发人员的效率。下面就去感受Mybatis动态SQL的魅力吧。

    2 if: 你们能判断,我也能判断!

    作为程序猿,谁不懂 if ! 在mybatis中也能用 if 啦:

    <select id="findUserById" resultType="user">
        select * from user where 
            <if test="id != null">
                   id=#{id}
            </if>
        and deleteFlag=0;
    </select>

    上面例子: 如果传入的id 不为空, 那么才会SQL才拼接id = #{id}。 这个相信大家看一样就能明白,不多说。细心的人会发现一个问题:“你这不对啊! 要是你传入的id为null, 那么你这最终的SQL语句不就成了 select * from user where and deleteFlag=0, 这语句有问题!”

    是啊,这时候,mybatis的 where 标签就该隆重登场啦。

    3 where, 有了我,SQL语句拼接条件神马的都是浮云!

    咱们通过where改造一下上面的例子:

    <select id="findUserById" resultType="user">
        select * from user 
            <where>
                <if test="id != null">
                    id=#{id}
                </if>
                and deleteFlag=0;
            </where>
    </select>

    有些人就要问了: “你这都是些什么玩意儿! 跟上面的相比, 不就是多了个where标签嘛! 那这个还会不会出现 select * from user where and deleteFlag=0 ?”

    的确,从表面上来看,就是多了个where标签而已, 不过实质上, mybatis是对它做了处理,当它遇到AND或者OR这些,它知道怎么处理。其实我们可以通过 trim 标签去自定义这种处理规则。

    4 trim : 我的地盘,我做主!

    上面的where标签,其实用trim 可以表示如下:

    <trim prefix="WHERE" prefixOverrides="AND |OR ">
        ... 
    </trim>

    它的意思就是:当WHERE后紧随AND或则OR的时候,就去除AND或者OR。 除了WHERE以外,其实还有一个比较经典的实现,那就是SET。

    5 set: 信我,不出错!

    <update id="updateUser" parameterType="com.dy.entity.User">
        update user set 
            <if test="name != null">
                name = #{name},
            </if> 
            <if test="password != null">
                password = #{password},
            </if> 
            <if test="age != null">
                age = #{age}
            </if> 
            <where>
                <if test="id != null">
                    id = #{id}
                </if>
                and deleteFlag = 0;
            </where>
    </update>

    问题又来了: “如果我只有name不为null, 那么这SQL不就成了 update set name = #{name}, where ........ ? 你那name后面那逗号会导致出错啊!”

    是的,这时候,就可以用mybatis为我们提供的set 标签了。下面是通过set标签改造后:

    <update id="updateUser" parameterType="com.dy.entity.User">
        update user
            <set>
                <if test="name != null">name = #{name},</if> 
                <if test="password != null">password = #{password},</if> 
                <if test="age != null">age = #{age},</if> 
            </set>
            <where>
                <if test="id != null">
                    id = #{id}
                </if>
                and deleteFlag = 0;
            </where>
    </update>

    这个用trim 可表示为:

    <trim prefix="SET" suffixOverrides=",">
      ...
    </trim>

    WHERE是使用的 prefixOverrides(前缀), SET是使用的 suffixOverrides (后缀), 看明白了吧!

    6 foreach: 你有for, 我有foreach, 不要以为就你才屌!

    java中有for, 可通过for循环, 同样, mybatis中有foreach, 可通过它实现循环,循环的对象当然主要是java容器和数组。

    <select id="selectPostIn" resultType="domain.blog.Post">
        SELECT *
        FROM POST P
        WHERE ID in
        <foreach item="item" index="index" collection="list"
            open="(" separator="," close=")">
            #{item}
        </foreach>
    </select>

    将一个 List 实例或者数组作为参数对象传给 MyBatis:当这么做的时候,MyBatis 会自动将它包装在一个 Map 中并以名称为键。List 实例将会以“list”作为键,而数组实例的键将是“array”。

    同样,当循环的对象为map的时候,index其实就是map的key。

    7 choose: 我选择了你,你选择了我!

    Java中有switch, mybatis有choose。

    <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>

    以上例子中:当title和author都不为null的时候, 那么选择二选一(前者优先), 如果都为null, 那么就选择 otherwise中的, 如果tilte和author只有一个不为null, 那么就选择不为null的那个。

    8 动态SQL解析原理

    我们在使用mybatis的时候,会在xml中编写sql语句。比如这段动态sql代码:

    <update id="update" parameterType="org.format.dynamicproxy.mybatis.bean.User">
        UPDATE users
        <trim prefix="SET" prefixOverrides=",">
            <if test="name != null and name != ''">
                name = #{name}
            </if>
            <if test="age != null and age != ''">
                , age = #{age}
            </if>
            <if test="birthday != null and birthday != ''">
                , birthday = #{birthday}
            </if>
        </trim>
        where id = ${id}
    </update>

    mybatis底层是如何构造这段sql的?下面带着这个疑问,我们一步一步分析。

    8.1 关于动态SQL的接口和类

    1. SqlNode接口,简单理解就是xml中的每个标签,比如上述sql的update,trim,if标签:

      public interface SqlNode {
          boolean apply(DynamicContext context);
      }

      SqlNode相关类图
    2. SqlSource Sql源接口,代表从xml文件或注解映射的sql内容,主要就是用于创建BoundSql,有实现类DynamicSqlSource(动态Sql源),StaticSqlSource(静态Sql源)等:

      public interface SqlSource {
          BoundSql getBoundSql(Object parameterObject);
      }

      SqlSource相关类图
    3. BoundSql类,封装mybatis最终产生sql的类,包括sql语句,参数,参数源数据等参数:


      BoundSql类
    4. XNode,一个Dom API中的Node接口的扩展类:


      XNode类
    5. BaseBuilder接口及其实现类(属性,方法省略了,大家有兴趣的自己看),这些Builder的作用就是用于构造sql:


      BaseBuilder相关类图

      下面我们简单分析下其中4个Builder:

      XMLConfigBuilder:解析mybatis中configLocation属性中的全局xml文件,内部会使用XMLMapperBuilder解析各个xml文件。

      XMLMapperBuilder:遍历mybatis中mapperLocations属性中的xml文件中每个节点的Builder,比如user.xml,内部会使用XMLStatementBuilder处理xml中的每个节点。

      XMLStatementBuilder:解析xml文件中各个节点,比如select,insert,update,delete节点,内部会使用XMLScriptBuilder处理节点的sql部分,遍历产生的数据会丢到Configuration的mappedStatements中。

      XMLScriptBuilder:解析xml中各个节点sql部分的Builder。

    6. LanguageDriver接口及其实现类(属性,方法省略了,大家有兴趣的自己看),该接口主要的作用就是构造sql:


      LanguageDriver相关类图

      简单分析下XMLLanguageDriver(处理xml中的sql,RawLanguageDriver处理静态sql):XMLLanguageDriver内部会使用XMLScriptBuilder解析xml中的sql部分

    8.2 源码分析走起

    Spring与Mybatis整合的时候需要配置SqlSessionFactoryBean,该配置会加入数据源和mybatis xml配置文件路径等信息:

    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="configLocation" value="classpath:mybatisConfig.xml"/>
        <property name="mapperLocations" value="classpath*:org/format/dao/*.xml"/>
    </bean>

    我们就分析这一段配置背后的细节:

    SqlSessionFactoryBean实现了Spring的InitializingBean接口,InitializingBean接口的afterPropertiesSet方法中会调用buildSqlSessionFactory方法,该方法内部会使用XMLConfigBuilder解析属性configLocation中配置的路径,还会使用XMLMapperBuilder属性解析mapperLocations属性中的各个xml文件。部分源码如下:

      protected SqlSessionFactory buildSqlSessionFactory() throws IOException {
    
          Configuration configuration;
    
          XMLConfigBuilder xmlConfigBuilder = null;
          if (this.configLocation != null) {
              // 1. 构建XMLConfigBuilder
              xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
              configuration = xmlConfigBuilder.getConfiguration();
          } else {
              if (logger.isDebugEnabled()) {
                  logger.debug("Property 'configLocation' not specified, using default MyBatis Configuration");
              }
              configuration = new Configuration();
              configuration.setVariables(this.configurationProperties);
          }
    
          if (this.objectFactory != null) {
              configuration.setObjectFactory(this.objectFactory);
          }
    
          if (this.objectWrapperFactory != null) {
              configuration.setObjectWrapperFactory(this.objectWrapperFactory);
          }
    
          if (hasLength(this.typeAliasesPackage)) {
              String[] typeAliasPackageArray = tokenizeToStringArray(this.typeAliasesPackage,
              ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
              for (String packageToScan : typeAliasPackageArray) {
                  configuration.getTypeAliasRegistry().registerAliases(packageToScan,
                    typeAliasesSuperType == null ? Object.class : typeAliasesSuperType);
                  if (logger.isDebugEnabled()) {
                      logger.debug("Scanned package: '" + packageToScan + "' for aliases");
                  }
              }
          }
    
          if (!isEmpty(this.typeAliases)) {
              for (Class<?> typeAlias : this.typeAliases) {
                  configuration.getTypeAliasRegistry().registerAlias(typeAlias);
                  if (logger.isDebugEnabled()) {
                      logger.debug("Registered type alias: '" + typeAlias + "'");
                  }
              }
          }
    
          if (!isEmpty(this.plugins)) {
              for (Interceptor plugin : this.plugins) {
                  configuration.addInterceptor(plugin);
                  if (logger.isDebugEnabled()) {
                      logger.debug("Registered plugin: '" + plugin + "'");
                  }
              }
          }
    
          if (hasLength(this.typeHandlersPackage)) {
              String[] typeHandlersPackageArray = tokenizeToStringArray(this.typeHandlersPackage,
              ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
              for (String packageToScan : typeHandlersPackageArray) {
                  configuration.getTypeHandlerRegistry().register(packageToScan);
                  if (logger.isDebugEnabled()) {
                      logger.debug("Scanned package: '" + packageToScan + "' for type handlers");
                  }
              }
          }
    
          if (!isEmpty(this.typeHandlers)) {
              for (TypeHandler<?> typeHandler : this.typeHandlers) {
                  configuration.getTypeHandlerRegistry().register(typeHandler);
                  if (logger.isDebugEnabled()) {
                      logger.debug("Registered type handler: '" + typeHandler + "'");
                  }
              }
          }
    
          if (xmlConfigBuilder != null) {
              try {
                  // 2. 解析xmlConfigBuilder
                  xmlConfigBuilder.parse();
    
                  if (logger.isDebugEnabled()) {
                      logger.debug("Parsed configuration file: '" + this.configLocation + "'");
                  }
              } catch (Exception ex) {
                  throw new NestedIOException("Failed to parse config resource: " + this.configLocation, ex);
              } finally {
                  ErrorContext.instance().reset();
              }
          }
    
          if (this.transactionFactory == null) {
              this.transactionFactory = new SpringManagedTransactionFactory();
          }
    
          Environment environment = new Environment(this.environment, this.transactionFactory, this.dataSource);
          configuration.setEnvironment(environment);
    
          if (this.databaseIdProvider != null) {
              try {
                  configuration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));
              } catch (SQLException e) {
                  throw new NestedIOException("Failed getting a databaseId", e);
              }
          }
    
          if (!isEmpty(this.mapperLocations)) {
              for (Resource mapperLocation : this.mapperLocations) {
                  if (mapperLocation == null) {
                      continue;
                  }
    
                  try {
                      // 3. 构建XMLMapperBuilder,并解析Mapper文件
                      XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
                  configuration, mapperLocation.toString(), configuration.getSqlFragments());
                      xmlMapperBuilder.parse();
                  } catch (Exception e) {
                      throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
                  } finally {
                      ErrorContext.instance().reset();
                  }
    
                  if (logger.isDebugEnabled()) {
                      logger.debug("Parsed mapper file: '" + mapperLocation + "'");
                  }
              }
          } else {
              if (logger.isDebugEnabled()) {
                  logger.debug("Property 'mapperLocations' was not specified or no matching resources found");
              }
          }
    
          return this.sqlSessionFactoryBuilder.build(configuration);
      }

    再来看下,XMLConfigBudiler.parse()方法源码细节:

      public Configuration parse() {
          if (parsed) {
              throw new BuilderException("Each XMLConfigBuilder can only be used once.");
          }
          parsed = true;
          parseConfiguration(parser.evalNode("/configuration"));
          return configuration;
      }
    
      private void parseConfiguration(XNode root) {
          try {
              propertiesElement(root.evalNode("properties")); //issue #117 read properties first
              typeAliasesElement(root.evalNode("typeAliases"));
              pluginElement(root.evalNode("plugins"));
              objectFactoryElement(root.evalNode("objectFactory"));
              objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
              settingsElement(root.evalNode("settings"));
              environmentsElement(root.evalNode("environments")); // read it after objectFactory and objectWrapperFactory issue #631
              databaseIdProviderElement(root.evalNode("databaseIdProvider"));
              typeHandlerElement(root.evalNode("typeHandlers"));
              // 解析Mapper映射文件
              mapperElement(root.evalNode("mappers"));
          } catch (Exception e) {
              throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
          }
      }
    
      private void mapperElement(XNode parent) throws Exception {
          if (parent != null) {
              for (XNode child : parent.getChildren()) {
                  if ("package".equals(child.getName())) {
                      String mapperPackage = child.getStringAttribute("name");
                      configuration.addMappers(mapperPackage);
                  } else {
                      String resource = child.getStringAttribute("resource");
                      String url = child.getStringAttribute("url");
                      String mapperClass = child.getStringAttribute("class");
                      if (resource != null && url == null && mapperClass == null) {
                          ErrorContext.instance().resource(resource);
                          InputStream inputStream = Resources.getResourceAsStream(resource);
                          // 构建XMLMapperBuilder对象
                          XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
                          mapperParser.parse();
                      } else if (resource == null && url != null && mapperClass == null) {
                          ErrorContext.instance().resource(url);
                          InputStream inputStream = Resources.getUrlAsStream(url);
                          // 构建XMLMapperBuilder对象
                          XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
                          mapperParser.parse();
                      } else if (resource == null && url == null && mapperClass != null) {
                          Class<?> mapperInterface = Resources.classForName(mapperClass);
                          configuration.addMapper(mapperInterface);
                      } else {
                          throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
                      }
                  }
              }
          }
      }

    由于XMLConfigBuilder内部也是使用XMLMapperBuilder,我们就看看XMLMapperBuilder的解析细节:


    XMLMapperBuilder.parse()源码

    XMLMapperBuilder.configurationElement()源码

    我们关注一下,增删改查节点的解析:


    增删改查节点的解析

    XMLStatementBuilder的解析:


    XMLStatementBuilder的解析

    默认会使用XMLLanguageDriver创建SqlSource(Configuration构造函数中设置)。

    XMLLanguageDriver创建SqlSource:


    XMLLanguageDriver创建SqlSource

    XMLScriptBuilder解析sql:


    XMLScriptBuilder解析sql

    得到SqlSource之后,会放到Configuration中,有了SqlSource,就能拿BoundSql了,BoundSql可以得到最终的sql。

    8.3 实例分析

    以下面的xml解析大概说下parseDynamicTags的解析过程:

    <update id="update" parameterType="org.format.dynamicproxy.mybatis.bean.User">
        UPDATE users
        <trim prefix="SET" prefixOverrides=",">
            <if test="name != null and name != ''">
                name = #{name}
            </if>
            <if test="age != null and age != ''">
                , age = #{age}
            </if>
            <if test="birthday != null and birthday != ''">
                , birthday = #{birthday}
            </if>
        </trim>
        where id = ${id}
    </update>

    parseDynamicTags方法的返回值是一个List,也就是一个Sql节点集合。SqlNode本文一开始已经介绍,分析完解析过程之后会说一下各个SqlNode类型的作用。

    1. 首先根据update节点(Node)得到所有的子节点,分别是3个子节点:

      (1) 文本节点 \n UPDATE users;

      (2) trim子节点 ...;

      (3) 文本节点 \n where id = #{id};

    2. 遍历各个子节点:

      (1) 如果节点类型是文本或者CDATA,构造一个TextSqlNode或StaticTextSqlNode;

      (2) 如果节点类型是元素,说明该update节点是个动态sql,然后会使用NodeHandler处理各个类型的子节点。这里的NodeHandler是XMLScriptBuilder的一个内部接口,其实现类包括TrimHandler、WhereHandler、SetHandler、IfHandler、ChooseHandler等。看类名也就明白了这个Handler的作用,比如我们分析的trim节点,对应的是TrimHandler;if节点,对应的是IfHandler...这里子节点trim被TrimHandler处理,TrimHandler内部也使用parseDynamicTags方法解析节点。

    3. 遇到子节点是元素的话,重复以上步骤:

      trim子节点内部有7个子节点,分别是文本节点、if节点、是文本节点、if节点、是文本节点、if节点、文本节点。文本节点跟之前一样处理,if节点使用IfHandler处理。遍历步骤如上所示,下面我们看下几个Handler的实现细节。

      IfHandler处理方法也是使用parseDynamicTags方法,然后加上if标签必要的属性:

      private class IfHandler implements NodeHandler {
         public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
             List<SqlNode> contents = parseDynamicTags(nodeToHandle);
             MixedSqlNode mixedSqlNode = new MixedSqlNode(contents);
             String test = nodeToHandle.getStringAttribute("test");
             IfSqlNode ifSqlNode = new IfSqlNode(mixedSqlNode, test);
             targetContents.add(ifSqlNode);
         }
      }

      TrimHandler处理方法也是使用parseDynamicTags方法,然后加上trim标签必要的属性:

      private class TrimHandler implements NodeHandler {
         public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
             List<SqlNode> contents = parseDynamicTags(nodeToHandle);
             MixedSqlNode mixedSqlNode = new MixedSqlNode(contents);
             String prefix = nodeToHandle.getStringAttribute("prefix");
             String prefixOverrides = nodeToHandle.getStringAttribute("prefixOverrides");
             String suffix = nodeToHandle.getStringAttribute("suffix");
             String suffixOverrides = nodeToHandle.getStringAttribute("suffixOverrides");
             TrimSqlNode trim = new TrimSqlNode(configuration, mixedSqlNode, prefix, prefixOverrides, suffix, suffixOverrides);
             targetContents.add(trim);
         }
      }
    4. 以上update方法最终通过parseDynamicTags方法得到的SqlNode集合如下:


      Paste_Image.png

      trim节点:


      Paste_Image.png

      由于这个update方法是个动态节点,因此构造出了DynamicSqlSource。DynamicSqlSource内部就可以构造sql了:


      Paste_Image.png

    DynamicSqlSource内部的SqlNode属性是一个MixedSqlNode。然后我们看看各个SqlNode实现类的apply方法。下面分析一下各个SqlNode实现类的apply方法实现:

    1. MixedSqlNode:MixedSqlNode会遍历调用内部各个sqlNode的apply方法。

      public boolean apply(DynamicContext context) {
         for (SqlNode sqlNode : contents) {
             sqlNode.apply(context);
         }
         return true;
      }
    2. StaticTextSqlNode:直接append sql文本。

      public boolean apply(DynamicContext context) {
         context.appendSql(text);
         return true;
      }
    3. IfSqlNode:这里的evaluator是一个ExpressionEvaluator类型的实例,内部使用了OGNL处理表达式逻辑。

      public boolean apply(DynamicContext context) {
         if (evaluator.evaluateBoolean(test, context.getBindings())) {
             contents.apply(context);
             return true;
         }
         return false;
      }
    4. TrimSqlNode:

      public boolean apply(DynamicContext context) {
          FilteredDynamicContext filteredDynamicContext = new FilteredDynamicContext(context);
          boolean result = contents.apply(filteredDynamicContext);
          filteredDynamicContext.applyAll();
          return result;
      }
      
      public void applyAll() {
          sqlBuffer = new StringBuilder(sqlBuffer.toString().trim());
          String trimmedUppercaseSql = sqlBuffer.toString().toUpperCase(Locale.ENGLISH);
          if (trimmedUppercaseSql.length() > 0) {
              applyPrefix(sqlBuffer, trimmedUppercaseSql);
              applySuffix(sqlBuffer, trimmedUppercaseSql);
          }
          delegate.appendSql(sqlBuffer.toString());
      }
      
      private void applyPrefix(StringBuilder sql, String trimmedUppercaseSql) {
          if (!prefixApplied) {
              prefixApplied = true;
              if (prefixesToOverride != null) {
                  for (String toRemove : prefixesToOverride) {
                      if (trimmedUppercaseSql.startsWith(toRemove)) {
                          sql.delete(0, toRemove.trim().length());
                          break;
                      }
                  }
              }
              if (prefix != null) {
                  sql.insert(0, " ");
                  sql.insert(0, prefix);
              }
         }
      }

      TrimSqlNode的apply方法也是调用属性contents(一般都是MixedSqlNode)的apply方法,按照实例也就是7个SqlNode,都是StaticTextSqlNode和IfSqlNode。 最后会使用FilteredDynamicContext过滤掉prefix和suffix。



    作者:陶邦仁
    链接:http://www.jianshu.com/p/e309ae5e4a77
    來源:简书
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
    展开全文
  • Mybatis动态SQL的实现

    万次阅读 多人点赞 2019-02-27 19:28:42
    Mybatis提供了动态SQL,也就是可以根据用户提供的参数,动态决定查询语句依赖的查询条件或SQL语句的内容。 动态SQL标签 if 和 where 标签 &amp;lt;!--动态Sql : where / if--&amp;gt; &amp;lt;select ...

    场景

    在实际应用开发过程中,我们往往需要写复杂的 SQL 语句,需要拼接,而拼接SQL语句又稍微不注意,由于引号,空格等缺失可能都会导致错误。
    Mybatis提供了动态SQL,也就是可以根据用户提供的参数,动态决定查询语句依赖的查询条件或SQL语句的内容。

    动态SQL标签

    if 和 where 标签

      <!--动态Sql : where / if-->
        <select id="dynamicSql"  resultType="com.lks.domain.User">
            select <include refid="tableAllkey"/> from users
            <where>
                <if test="id != null and id != 0">
                    AND id = #{id}
                </if>
                <if test="name != null and name != ''">
                    AND name = #{name}
                </if>
                <if test="county != null and county != ''">
                    AND county = #{county}
                </if>
            </where>
        </select>
    

    一般开发列表业务的查询条件时,如果有多个查询条件,通常会使用 标签来进行控制。 标签可以自动的将第一个条件前面的逻辑运算符 (or ,and) 去掉,正如代码中写的,id 查询条件前面是有“and”关键字的,但是在打印出来的 SQL 中却没有,这就是 的作用。打印SQL语句的使用可以在mybatis-config文件中添加setting标签:

    <settings>
            <!-- 打印查询语句 -->
            <setting name="logImpl" value="STDOUT_LOGGING" />
        </settings>
    

    choose、when、otherwise 标签

    这三个标签需要组合在一起使用,类似于 Java 中的 switch、case、default。只有一个条件生效,也就是只执行满足的条件 when,没有满足的条件就执行 otherwise,表示默认条件。

     <!--动态Sql: choose、when、otherwise 标签-->
        <select id="dynamicSql2" resultType="com.lks.domain.User">
            select * from users
            <where>
                <choose>
                    <when test="name != null and name != ''">
                        AND name = #{name}
                    </when>
                    <when test="county != null and county != ''">
                        AND county = #{county}
                    </when>
                    <otherwise>
                        AND id = #{id}
                    </otherwise>
                </choose>
            </where>
        </select>
    

    在测试类中,即使同时添加name和county的值,最终的sql也只会添加第一个属性值。

    set 标签

    使用set标签可以将动态的配置 SET 关键字,并剔除追加到条件末尾的任何不相关的逗号。使用 if+set 标签修改后,在进行表单更新的操作中,哪个字段中有值才去更新,如果某项为 null 则不进行更新,而是保持数据库原值。

    <!--动态Sql: set 标签-->
        <update id="updateSet" parameterType="com.lks.domain.User">
            update users
            <set>
                <if test="name != null and name != ''">
                    name = #{name},
                </if>
                <if test="county != null and county != ''">
                    county = #{county},
                </if>
            </set>
            where id = #{id}
        </update>
    

    trim 标签

    trim 是一个格式化标签,可以完成< set > 或者是 < where > 标记的功能。主要有4个参数:
    ① prefix:前缀

    ② prefixOverrides:去掉第一个and或者是or

    ③ suffix:后缀

    ④ suffixOverrides:去掉最后一个逗号,也可以是其他的标记

    <!--动态Sql: trim 标签-->
        <select id="dynamicSqlTrim" resultType="com.lks.domain.User">
            select * from users
            <trim prefix="where" suffix="order by age" prefixOverrides="and | or" suffixOverrides=",">
                <if test="name != null and name != ''">
                    AND name = #{name}
                </if>
                <if test="county != null and county != ''">
                    AND county = #{county}
                </if>
            </trim>
        </select>
    

    foreach 标签

    foreach标签主要有以下参数:
    item :循环体中的具体对象。支持属性的点路径访问,如item.age,item.info.details,在list和数组中是其中的对象,在map中是value。
    index :在list和数组中,index是元素的序号,在map中,index是元素的key,该参数可选。
    open :表示该语句以什么开始
    close :表示该语句以什么结束
    separator :表示元素之间的分隔符,例如在in()的时候,separator=","会自动在元素中间用“,“隔开,避免手动输入逗号导致sql错误,如in(1,2,)这样。该参数可选。

    list批量插入
        <!--动态Sql: foreach标签, 批量插入-->
            <insert id="dynamicSqlInsertList" useGeneratedKeys="true" keyProperty="id">
                insert into users (name, age, county, date)
                values
                <foreach collection="list" item="user" separator="," >
                    (#{user.name}, #{user.age}, #{user.county}, #{user.date})
                </foreach>
            </insert>
    

    在这里插入图片描述
    从结果可以看出,我们一下插入了两条数据,每条数据之间使用“,”进行分割,separator="," 的作用就是如此。其中< foreach >标签内部的属性务必加上item.

    list集合参数
      <!--动态Sql: foreach标签, list参数查询-->
        <select id="dynamicSqlSelectList" resultType="com.lks.domain.User">
            SELECT * from users WHERE id in
            <foreach collection="list" item="id" open="(" close=")" separator="," >
                #{id}
            </foreach>
        </select>
    

    在这里插入图片描述
    可以看出我们的 SQL 语句新增了:( ? , ? ) ,前后的括号由 open="(" close=")" 进行控制,用“?”占位符占位,并通过separator以:“,”隔开,内部两个循环遍历出的元素。array 集合与 list 的做法也是类似的:

    <!--动态Sql: foreach标签, array参数查询-->
        <select id="dynamicSqlSelectArray" resultType="com.lks.domain.User">
            select * from users WHERE id in
            <foreach collection="array" item="id" open="(" close=")" separator=",">
                #{id}
            </foreach>
        </select>
    
    map参数

    < map> 标签需要结合MyBatis的参数注解 @Param()来使用,需要告诉Mybatis配置文件中的collection="map"里的map是一个参数:

    <!--动态Sql: foreach标签, map参数查询-->
        <select id="dynamicSqlSelectMap" resultType="com.lks.bean.User">
            select * from users WHERE
            <foreach collection="map" index="key" item="value"  separator="=">
                ${key} = #{value}
            </foreach>
        </select>
    

    需要主要${}和#{}的使用。

    展开全文
  • 动态SQL语句的语法

    千次阅读 2013-12-02 22:03:20
    动态SQL是在运行时生成和执行SQL语句的编程方法。动态是和静态相对而言的。静态SQL指的是在代码编译时刻就已经包含在代码中的那些已经充分明确的固定的SQL语句。 PL/ SQL提供了两种方式来编写动态SQL: 本地动态SQL...
  • 动态 sql 是 mybatis 的主要特性之一,在 mapper 中定义的参数传到 xml 中之后,在查询之前 mybatis 会对其进行动态解析。mybatis 为我们提供了两种支持动态 sql 的语法:#{} 以及 ${}。  在下面的语句中,如果 ...
  • 强大的动态SQL

    千次阅读 2018-06-19 15:26:04
    用于实现动态SQL的元素if:利用if实现简单的条件选择choose(when,otherwise):相当于java中的switch语句,通常与when和otherwise搭配where:简化Sql语句中where的条件判断set:解决动态更新语句trim:可以灵活的去除多余...
  • 动态 SQL 你还敢用?

    千次阅读 2019-09-25 21:30:36
    2010 年左右的时候,笔者曾服务于阿里旗下服装 ERP 公司。 该公司的 ERP 产品服务于全国 8W+ 零售店。当时用的语言是 Delphi(严重暴露自己的年龄了,估计现在的小朋友们都不知道 Delphi 是何物)。...
  • 通用Mapper注解方式实现动态SQL

    万次阅读 多人点赞 2019-12-02 15:18:25
    1.通用Mapper注解方式实现动态SQL 注意:使用动态SQL后,不能使用分页助手,需要手动写分页SQL 对应的mapper接口实现SelectMapper接口 在对应的mapper接口中的方法上 ...
  • create or replace procedure P_TEST_SQL is TYPE ref_cursor_type IS REF CURSOR; --定义一个动态游标 tablename varchar2(200) default 'ess_client'; v_sql varchar2(1000); mobile varchar2(15); usrs ref_...
  • mySql拼接动态SQL并执行

    万次阅读 2019-01-05 15:59:22
    1、之前写了接收动态存储过程返回值的文章,今天写一下拼接并执行动态sql语句的方法 2、具体SQL语句如下  set pSql = concat('update kqcard set ',vFingerIndex,' = \'', vFingerData, '\' , zkkqEnable = 1 ...
  • Mybatis在注解上实现动态SQL

    万次阅读 2019-07-04 14:15:52
    使用Mybatis注解实现sql语句,但是有些时候有些字段是空的,这时候这个空的字段就要从条件查询语句中删除,这个时候就需要用到动态Sql。 注解的动态语句支持以下 trim where set foreach if choose when otherwise ...
  • 这个Mybatis动态sql的功能,就拥有有效的解决了这个问题,Mybatis动态sql语言可以被用在任意的sql语句映射中。Mybatis采用强大的功能基于OGNL的表达式消除其他元素。常用的标签:if:非空验证 如id为空时,if标签里...
  • [SQL Server] 动态sql给变量赋值(或返回值给变量)
  • 一道DBA面试题目:动态SQL超过8000的解决方案
  • Java - MyBatis中的动态SQL是什么意思?

    万次阅读 2019-03-20 11:30:36
    分享一个大牛的人工智能教程。零基础!通俗易懂!风趣幽默!希望你也加入到人工智能的队伍中来!... 对于一些复杂的查询,我们可能会指定多个查询条件,但是这些条件可能...此时就需要根据用户指定的条件动态生成SQL语...
  • spring boot(8)-mybatis三种动态sql

    万次阅读 热门讨论 2017-06-24 09:04:19
    XML配置方式的动态SQL我就不讲了,有兴趣可以自己了解,下面是用的方式把它照搬过来,用注解来实现。适用于xml配置转换到注解配置 @Select("select * from user where id = #{id} ") public List findUserById...
  • 如何对SQL Server配置动态端口?

    千次阅读 2016-03-22 13:25:30
    SQL Server默认端口是1433,我们也可以根据需要设置其他端口使用,那么如何对SQL Server设置动态端口呢?本文主要介绍具体的实现方法。
  • MySQL基础----动态SQL语句

    万次阅读 2014-09-23 17:49:48
    动态sql语句基本语法  1 :普通SQL语句可以用Exec执行  eg: Select * from tableName   Exec('select * from tableName')   Exec sp_executesql N'select * from tableName' -- 请注意...
  • [Navicat]动态生成SQL语句

    千次阅读 2018-12-30 20:57:17
    当你点击了“设计表”进行修改表结构,在保存表结构之前点击“SQL预览”,你将会看到你的所有操作生成了SQL语句。 注意:如果你保存了表结构,“SQL预览”下的内容将为空。    ...
1 2 3 4 5 ... 20
收藏数 387,931
精华内容 155,172
关键字:

动态sql