重用动态sql片段

时间:2014-07-31 09:23:44

标签: mybatis

Hei那里,我正在开发一个Primefaces应用程序,作为一个持久层,我选择了Mybatis

这是常规sql在我的mapper中的样子:

<select id="getAllTransportUnit" resultMap="TransportUnitMap">
    SELECT * FROM SSLS_GUI.VW_TU
    <if test="( hasFilters == 'yes' ) and ( parameters != null )">
        <where>
            <foreach item="clause" collection="parameters" separator=" AND "
                open="(" close=")">
                UPPER(${clause.column}) ${clause.operator} #{clause.value}
            </foreach>
        </where>
    </if>
    <if test="sort == 'true'">
        ORDER BY ${sortField}
        <if test="sortOrder == 'DESC'"> DESC</if>
        <if test="sortOder == 'ASC'"> ASC</if>
    </if>
</select>

我的几乎所有查询都使用从<if test...>开始的动态sql部分。是否可以将它放在一个单独的文件中,然后在我的查询中重复使用它?

2 个答案:

答案 0 :(得分:10)

如何重用sql片段有几种选择。

SQL代码段并包括

第一个使用include。创建单独的映射器Common.xml:

<mapper namespace="com.company.project.common">
    <sql id="orderBy>
      <if test="sort == 'true'">
        ORDER BY ${sortField}
        <if test="sortOrder == 'DESC'"> DESC</if>
        <if test="sortOder == 'ASC'"> ASC</if>
      </if>
    </sql>


    <sql id="filters">
     <if test="( hasFilters == 'yes' ) and ( parameters != null )">
      <where>
        <foreach item="clause" collection="parameters" separator=" AND "
            open="(" close=")">
            UPPER(${clause.column}) ${clause.operator} #{clause.value}
        </foreach>
     </where>
    </if>
  </sql>
</mapper>

并在其他地图制作工具MyMapper.xml中使用它:

<select id="getAllTransportUnit" resultMap="TransportUnitMap">
  SELECT * FROM SSLS_GUI.VW_TU
  <include refid="com.company.project.common.filters"/>
  <include refid="com.company.project.common.orderBy"/>
</select>

为避免在每个包含中复制命名空间,您可以在MyMapper.xml中创建快捷方式摘要:

<sql id="orderBy">
  <include refid="com.company.project.common.orderBy"/> 
</sql>

<select id="getAllTransportUnit" resultMap="TransportUnitMap">
  SELECT * FROM SSLS_GUI.VW_TU
  <include refid="orderBy"/>
</select>

Mybatis-velocity宏

另一种可能的选择是使用mybatis scripting。使用mybatis-velocity脚本引擎,您可以定义速度宏并重复使用它。

Commons.xml

<sql id="macros"
  #macro(filters)
    #if ( $_parameter.hasFilters )
      #repeat( $_parameter.parameters $clause "AND" " (" ")" )
        ${clause.column} ${clause.operator} @{clause.value}
      #end
    #end
  #end

  #macro(order_by)
  .. 
  #end
</sql>

MyMapper.xml

<select id="getAllTransportUnit" resultMap="TransportUnitMap">
  <include refid="macros"/>
  SELECT * FROM SSLS_GUI.VW_TU
  #filters()
  #order_by()
</select>

通过sql代码段包含宏并不是重用宏的最简洁方法。这只是一个想法如何使用它。

更好的选择是配置mybatis-velocity并指定可用的全局宏。在这种情况下,不需要包含macros代码段,结果查询将是这样的:

<select id="getAllTransportUnit" resultMap="TransportUnitMap">
  SELECT * FROM SSLS_GUI.VW_TU
  #filters()
  #order_by()
</select>

答案 1 :(得分:1)

请参阅@Roman Konoval关于如何在XML中执行此操作的答案。

对于纯Java方面的另一个选项(在OP的情况下,上面的XML选项更适用;我在这里留给那些可能使用纯Java的人),可以使用Mybatis'Statement Builders,允许构建与 Java 代码内联的动态 SQL ,您可以将那里的公共代码分解为类似于分解任何公共代码的方式。

他们在 Mybatis doc中提供的示例如下:

private String selectPersonSql() {
  return new SQL() {{
    SELECT("P.ID, P.USERNAME, P.PASSWORD, P.FULL_NAME");
    SELECT("P.LAST_NAME, P.CREATED_ON, P.UPDATED_ON");
    FROM("PERSON P");
    FROM("ACCOUNT A");
    INNER_JOIN("DEPARTMENT D on D.ID = P.DEPARTMENT_ID");
    INNER_JOIN("COMPANY C on D.COMPANY_ID = C.ID");
    WHERE("P.ID = A.ID");
    WHERE("P.FIRST_NAME like ?");
    OR();
    WHERE("P.LAST_NAME like ?");
    GROUP_BY("P.ID");
    HAVING("P.LAST_NAME like ?");
    OR();
    HAVING("P.FIRST_NAME like ?");
    ORDER_BY("P.ID");
    ORDER_BY("P.FULL_NAME");
  }}.toString();
}

因此,您可以定义一个函数,该函数将XML中的commmon动态 SQL 分解出来,并且可能会使用表示SELECT列和FROM表部分的参数。语句(以及可能有所不同的任何其他内容),然后可以将其传递给函数内部的动态 SQL 方法。