Mybatis常用的OGNL表达式

在Mybatis的动态SQL和${}形式的参数中都用到了OGNL表达式。

Mybatis常用的OGNL表达式

e1 or e2:或

<if test="userEmail != null or userEmail == '1'">
</if>

e1 and e2:且

<if test="userEmail != null and userEmail != ''">
</if>

e1 == e2 或e1 eq e2:相等

<if test="userEmail == null and userEmail == ''">
</if>

e1 != e2 或 e1 neq e2:不等

<if test="userEmail != null and userEmail != ''">
</if>

e1 lt e2:小于

<if test="age lt 10">
</if>

e1 lte e2:小于等于

e1 gt e2:大于

e1 gte e2:大于等于

e1 + e2(加),e1 - e2(减),e1 * e2(乘),e1/e2(除),e1%e2(余)

!e或not e:非,取反

e.method(args):调用对象方法

<if test="list != null and list.size() > 0 ">
		#{userEmail,jdbcType=VARCHAR},
</if>

e.property:对象属性值

<!-- 多接口参数的查询方法(@Param + javaBean方式) -->
  <select id="selectByUserIdAndEnabledUseBean" resultMap="BaseResultMap">
    select r.id, r.role_name, r.enabled, r.create_by, r.create_time, 
    u.user_name as "user.userName", u.user_email as "user.userEmail"
    from sys_user u 
    inner join sys_user_role ur on u.id = ur.user_id 
    inner join sys_role r on ur.role_id = r.id 
    where u.id = #{user.id} and r.enabled = #{role.enabled}
</select>

e1[e2]:按索引取值(List、数组和map)

@class@method(args):调用类的静态方法

<bind name="name" value="@ex.mybatis.rbac.mapper.UserMaperTest@setName()"/>

@class@field:调用类的静态字段值

<bind name="name" value="@ex.mybatis.rbac.mapper.UserMaperTest@NAME"/>

MyBatis中什么地方可以使用OGNL

下面这两处地方在MyBatis中处理的时候都是使用OGNL处理的。

动态SQL表达式中

示例1

<select id="xxx" ...>
    select id,name,... from country
    <where>
        <if test="name != null and name != ''">
            name like concat('%', #{name}, '%')
        </if>
    </where>
</select>

上面代码中test的值会使用OGNL计算结果。

示例2

<select id="xxx" ...>
    select id,name,... from country
    <bind name="nameLike" value="'%' + name + '%'"/>
    <where>
        <if test="name != null and name != ''">
            name like '${nameLike}'
        </if>
    </where>
</select>

这里的value值会使用OGNL计算。

注:对<bind参数的调用只能通过${}方式获取,如${nameLike}。

在通用Mapper中支持一种UUID的主键,在通用Mapper中的实现就是使用了标签,这个标签调用了一个静态方法,大概方法如下:

<bind name="username_bind" value='@java.util.UUID@randomUUID().toString().replace("-", "")' />

这种方式虽然能自动调用静态方法,但是没法回写对应的属性值,因此使用时需要注意。

${param}参数中

上面like的例子中使用下面这种方式最简单

<select id="xxx" ...>
    select id,name,... from country
    <where>
        <if test="name != null and name != ''">
            name like '${'%' + name + '%'}'
        </if>
    </where>
</select>

这里注意写的是${‘%’ + name + ‘%’},而不是%${name}%,这两种方式的结果一样,但是处理过程不一样。

在MyBatis中处理${}的时候,只是使用OGNL计算这个结果值,然后替换SQL中对应的${xxx},OGNL处理的只是${这里的表达式}。

这里表达式可以是OGNL支持的所有表达式,可以写的很复杂,可以调用静态方法返回值,也可以调用静态的属性值。

示例1:查询多个父级的所有的子级组织

<select id="selectAllSubDeptByDeptIds" resultType="long">
    SELECT
        d.id
    FROM
        dept d
        INNER JOIN(
            SELECT * FROM (
                (SELECT @ids :=
                        <!--将集合deptList使用 逗号 拼接起来, 此处使用foreach会查不出结果, 即使是使用了replace去掉了空格的情况下-->
                        '${@org.springframework.util.StringUtils@collectionToCommaDelimitedString(deptIdList)}'
                ) a
                , (SELECT @ids AS _ids ,
                          (SELECT @ids := GROUP_CONCAT(id) FROM dept WHERE FIND_IN_SET(parent_id, @ids)) AS cids
                   FROM
                        dept d
                   WHERE
                        @ids IS NOT NULL AND d.is_del = 0
                ) b
            )
        ) T ON FIND_IN_SET(d.id, T.cids)
    WHERE
        d.is_del = 0
</select>

示例2:查询未被邀请的组织

<select id="selectNoInvitedDepts" resultType="com.anbao.train.data.dto.dept.DeptTreeDto">
    SELECT
        d.id,
        d.name,
        d.parent_id,
        d.hospital_area_id
    FROM
        dept d
    WHERE
        d.hospital_area_id = #{hospitalAreaId}
        AND d.merchant_code = #{merchantCode}
        <if test="deptIds != null and deptIds.size() != 0">
            AND NOT FIND_IN_SET(d.id,
                    (SELECT
                        group_concat( _ids )
                     FROM
                        (
                            ( SELECT @ids :=
                                '${@org.springframework.util.StringUtils@collectionToCommaDelimitedString(deptIds)}'
                            ) a,
                            (
                                SELECT
                                    @ids AS _ids,
                                    (
                                    SELECT
                                        @ids := GROUP_CONCAT( id )
                                    FROM
                                        dept
                                    WHERE
                                        FIND_IN_SET( parent_id, @ids )) AS cids
                                FROM
                                    dept
                                WHERE
                                    @ids IS NOT NULL
                            ) b
                        )
                    )
                )
        </if>
</select>

示例3:删除多个父级所有的子级组织

<delete id="deleteCourseInvitationSubDepts">
    DELETE
    FROM
        course_invitation ci
    WHERE
        ci.course_id = #{courseId}
        AND FIND_IN_SET (ci.dept_id,
                  (
                     SELECT
                        group_concat( cids )
                     FROM (
                            ( SELECT @ids :=
                                '${@org.springframework.util.StringUtils@collectionToCommaDelimitedString(deptIdList)}'
                            ) a,
                            (SELECT
                                @ids AS _ids,
                                (SELECT
                                    @ids := GROUP_CONCAT( id )
                                FROM
                                    dept
                                WHERE
                                    FIND_IN_SET( parent_id, @ids )
                                ) AS cids
                            FROM
                                dept
                            WHERE
                                @ids IS NOT NULL
                            ) b
                          )
                  )
            )
</delete>

示例4:使用OGNL实现单表的分表功能

分表这个功能是通用Mapper中的新功能,允许在运行的时候指定一个表名,通过指定的表名对表进行操作。这个功能实现就是使用了OGNL。

首先并不是所有的表都需要该功能,因此定义了一个接口,当参数(接口方法只有实体类一个参数)对象继承该接口的时候,就允许使用动态表名。

public interface IDynamicTableName {
 
    /**
     * 获取动态表名 - 只要有返回值,不是null和'',就会用返回值作为表名
     *
     * @return
     */
    String getDynamicTableName();
}

然后在XML中写表名的时候使用:

<if test="@tk.mybatis.mapper.util.OGNL@isDynamicParameter(_parameter) 
            and dynamicTableName != null 
            and dynamicTableName != ''">
    ${dynamicTableName}
</if>
<if test="@tk.mybatis.mapper.util.OGNL@isNotDynamicParameter(_parameter) 
            or dynamicTableName == null 
            or dynamicTableName == ''">
    defaultTableName
</if>

由于我需要判断_parameter是否继承了IDynamicTableName接口,简单的写法已经无法实现,所以使用了静态方法,这两个方法如下:

/**
 * 判断参数是否支持动态表名
 *
 * @param parameter
 * @return true支持,false不支持
 */
public static boolean isDynamicParameter(Object parameter) {
    if (parameter != null && parameter instanceof IDynamicTableName) {
        return true;
    }
    return false;
}
 
/**
 * 判断参数是否b支持动态表名
 *
 * @param parameter
 * @return true不支持,false支持
 */
public static boolean isNotDynamicParameter(Object parameter) {
    return !isDynamicParameter(parameter);
}

根据<if>判断的结果来选择使用那个表名。

另外注意XML判断中有一个dynamicTableName,这个参数是根据getDynamicTableName方法得到的,MyBatis使用属性对应的getter方法来获取值,不是根据field来获取值。

单独使用ognl表达式

其中,DefaultMemberAccess是复制下来,放到项目中的,不然会报错:RuntimeException: MemberAccess implementation must be provided!

OgnlContext context = new OgnlContext(null,null,new DefaultMemberAccess(true));
User user = new User();
user.setUserAccount("zzhua");

System.out.println(Ognl.getValue("userAccount", context,user)); // true
System.out.println(Ognl.getValue("userAccount == 'zzhua'",context, user)); / true
ystem.out.println(Ognl.getValue("userAccount == 'zj'",context, user)); // false
System.out.println(Ognl.getValue("userAccount != null && userAccount != ''", context,user)); // true
System.out.println(Ognl.getValue("userAccount.substring(0,2)", context,user)); // zz

System.out.println(Ognl.getValue("1==1", context,new Object())); //true
System.out.println(Ognl.getValue("1=='1'", context, new Object())); // false
//--------------------------------------------------------------------------
//	Copyright (c) 1998-2004, Drew Davidson and Luke Blanshard
//  All rights reserved.
//
//	Redistribution and use in source and binary forms, with or without
//  modification, are permitted provided that the following conditions are
//  met:
//
//	Redistributions of source code must retain the above copyright notice,
//  this list of conditions and the following disclaimer.
//	Redistributions in binary form must reproduce the above copyright
//  notice, this list of conditions and the following disclaimer in the
//  documentation and/or other materials provided with the distribution.
//	Neither the name of the Drew Davidson nor the names of its contributors
//  may be used to endorse or promote products derived from this software
//  without specific prior written permission.
//
//	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
//  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
//  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
//  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
//  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
//  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
//  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
//  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
//  AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
//  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
//  THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
//  DAMAGE.
//--------------------------------------------------------------------------

import org.apache.ibatis.ognl.MemberAccess;

import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Member;
import java.lang.reflect.Modifier;
import java.util.Map;

/**
 * This class provides methods for setting up and restoring
 * access in a Field.  Java 2 provides access utilities for setting
 * and getting fields that are non-public.  This object provides
 * coarse-grained access controls to allow access to private, protected
 * and package protected members.  This will apply to all classes
 * and members.
 *
 * @author Luke Blanshard (blanshlu@netscape.net)
 * @author Drew Davidson (drew@ognl.org)
 * @version 15 October 1999
 */
public class DefaultMemberAccess implements MemberAccess
{
    public boolean      allowPrivateAccess = false;
    public boolean      allowProtectedAccess = false;
    public boolean      allowPackageProtectedAccess = false;

	/*===================================================================
		Constructors
	  ===================================================================*/
	public DefaultMemberAccess(boolean allowAllAccess)
	{
	    this(allowAllAccess, allowAllAccess, allowAllAccess);
	}

	public DefaultMemberAccess(boolean allowPrivateAccess, boolean allowProtectedAccess, boolean allowPackageProtectedAccess)
	{
	    super();
	    this.allowPrivateAccess = allowPrivateAccess;
	    this.allowProtectedAccess = allowProtectedAccess;
	    this.allowPackageProtectedAccess = allowPackageProtectedAccess;
	}

	/*===================================================================
		Public methods
	  ===================================================================*/
	public boolean getAllowPrivateAccess()
	{
	    return allowPrivateAccess;
	}

	public void setAllowPrivateAccess(boolean value)
	{
	    allowPrivateAccess = value;
	}

	public boolean getAllowProtectedAccess()
	{
	    return allowProtectedAccess;
	}

	public void setAllowProtectedAccess(boolean value)
	{
	    allowProtectedAccess = value;
	}

	public boolean getAllowPackageProtectedAccess()
	{
	    return allowPackageProtectedAccess;
	}

	public void setAllowPackageProtectedAccess(boolean value)
	{
	    allowPackageProtectedAccess = value;
	}

	/*===================================================================
		MemberAccess interface
	  ===================================================================*/
    public Object setup(Map context, Object target, Member member, String propertyName)
    {
        Object      result = null;

        if (isAccessible(context, target, member, propertyName)) {
            AccessibleObject    accessible = (AccessibleObject)member;

            if (!accessible.isAccessible()) {
                result = Boolean.FALSE;
                accessible.setAccessible(true);
            }
        }
        return result;
    }

    public void restore(Map context, Object target, Member member, String propertyName, Object state)
    {
        if (state != null) {
            ((AccessibleObject)member).setAccessible(((Boolean)state).booleanValue());
        }
    }

    /**
        Returns true if the given member is accessible or can be made accessible
        by this object.
     */
	public boolean isAccessible(Map context, Object target, Member member, String propertyName)
	{
	    int         modifiers = member.getModifiers();
	    boolean     result = Modifier.isPublic(modifiers);

	    if (!result) {
	        if (Modifier.isPrivate(modifiers)) {
	            result = getAllowPrivateAccess();
	        } else {
	            if (Modifier.isProtected(modifiers)) {
	                result = getAllowProtectedAccess();
	            } else {
	                result = getAllowPackageProtectedAccess();
	            }
	        }
	    }
	    return result;
	}
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值