美文网首页关系型数据库相关
2019-12-05 mybatis的discriminator

2019-12-05 mybatis的discriminator

作者: FredWorks | 来源:发表于2019-12-05 10:04 被阅读0次

    mybatis的discriminator和if的基本使用,就不多说了。mybatis自家官网上的电子书《MyBatis3 教程 中文版》中就有。
    但这本书中的描述比较简单,只说了使用基础类型作为条件时的用法。

    <discriminator javaType="int" column="draft">
        <case value="1" resultType="DraftPost"/>
    </discriminator>
    
    • column="draft":结果集中的列“draft”的值,用作鉴别器的值
    • javaType="int":该列转换出来的对象中,数据类型是int
    • case value="1":当对象的值是1时,将结果集映射为对象 DraftPost 的实例。

    那么问题来了。如果我们使用对象的枚举类型属性作为鉴别器和if的条件时,如何配置?上网百度,也没有一个说这个问题的。干脆扒源代码,才找到答案。
    下面通过例子来说明具体做法。

    1. 常规枚举作为鉴别器的鉴别属性

    首先,下面例子只是为了说明使用枚举作为鉴别器的场景,不讨论模型业务上的合理性。我要映射的模型如下:
    枚举 OrderCategory 列举了订单种类;Order 是普通订单,也是特殊订单类型的父类;TimedOrder是计时订单;SiteOrder是场地订单。

    /**
     * 订单类型
     * @author FredWorks
     * 2019年12月5日 上午9:23:58
     */
    public enum OrderCategory {
        
        /**
         * 普通订单
         */
        NormalOrder,
        
        /**
         * 计时订单
         */
        TimedOrder,
        
        /**
         * 场地订单
         */
        SiteOrder;
    }
    
    /**
     * 普通订单
     * @author FredWorks
     * 2019年12月5日 上午9:16:07
     */
    public class Order implements Serializable {
    
        private static final long serialVersionUID = -8779032621915871117L;
    
        /**
         * 唯一标识
         */
        private String id;
    
        /**
         * 总价
         */
        private BigDecimal totalPrice;
        
        /**
         * 订单类型
         */
        private OrderCategory orderCategory;
    
        /**
         * 获取属性  id 的值
         * @return 属性 id 的值
         */
        public String getId() {
            return this.id;
        }
    
        /**
         * 给属性 id 赋值
         * @param id 将要给属性 id 赋予的值
         */
        public void setId(String id) {
            this.id = id;
        }
    
        /**
         * 获取属性  totalPrice 的值
         * @return 属性 totalPrice 的值
         */
        public BigDecimal getTotalPrice() {
            return this.totalPrice;
        }
    
        /**
         * 给属性 totalPrice 赋值
         * @param totalPrice 将要给属性 totalPrice 赋予的值
         */
        public void setTotalPrice(BigDecimal totalPrice) {
            this.totalPrice = totalPrice;
        }
    
        /**
         * 获取属性  orderCategory 的值
         * @return 属性 orderCategory 的值
         */
        public OrderCategory getOrderType() {
            return this.orderCategory;
        }
    
        /**
         * 给属性 orderCategory 赋值
         * @param orderCategory 将要给属性 orderCategory 赋予的值
         */
        public void setOrderType(OrderCategory orderCategory) {
            this.orderCategory = orderCategory;
        }
    }
    
    /**
     * 计时订单
     * @author FredWorks
     * 2019年12月5日 上午9:27:26
     */
    public class TimedOrder extends Order {
    
        private static final long serialVersionUID = 3426131282358762200L;
        
        /**
         * 消费时长
         */
        private int consumingMinutes;
    
        /**
         * 获取属性  consumingMinutes 的值
         * @return 属性 consumingMinutes 的值
         */
        public int getConsumingMinutes() {
            return this.consumingMinutes;
        }
    
        /**
         * 给属性 consumingMinutes 赋值
         * @param consumingMinutes 将要给属性 consumingMinutes 赋予的值
         */
        public void setConsumingMinutes(int consumingMinutes) {
            this.consumingMinutes = consumingMinutes;
        }
    }
    
    /**
     * 场地订单
     * @author FredWorks
     * 2019年12月5日 上午9:27:56
     */
    public class SiteOrder extends Order {
    
        private static final long serialVersionUID = -8420951718498548031L;
        
        /**
         * 场地对象唯一标识
         */
        private String siteId;
    
        /**
         * 获取属性  siteId 的值
         * @return 属性 siteId 的值
         */
        public String getSiteId() {
            return this.siteId;
        }
    
        /**
         * 给属性 siteId 赋值
         * @param siteId 将要给属性 siteId 赋予的值
         */
        public void setSiteId(String siteId) {
            this.siteId = siteId;
        }
    }
    

    如果我们希望在一个mybatis的结果集中,根据orderType的值,决定返回那种类型的Order实例,我们就需要使用orderType属性作为鉴别字段:

    <mapper namespace="cn.fredworks.persistence.mybatis.dao.order.IOrderDao">
        <resultMap type="cn.fredworks.model.order.Order" id="orderResultMap">
            <id column="id" property="id" />
            <result column="total_rice" property="totalPrice" />
            <result column="order_category" property="orderCategory" 
                typeHandler="cn.fredworks.persistence.mybatis.typehandler.order.OrderCategoryTypeHandler"/>
            <discriminator javaType="cn.fredworks.model.order.OrderCategory" column="order_category"
                    typeHandler="cn.fredworks.persistence.mybatis.typehandler.order.OrderCategoryTypeHandler">
                <case value="NormalOrder" resultType="cn.fredworks.model.order.Order"/>
                <case value="TimedOrder" resultType="cn.fredworks.model.order.TimedOrder"/>
                    <result column="consuming_minutes" property="consumingMinutes" />
                </case>
                <case value="SiteOrder" resultType="cn.fredworks.model.order.SiteOrder"/>
                    <result column="site_id" property="siteId" />
                </case>
            </discriminator>
        </resultMap>
        ......
    </mapper>
    

    我们对比一下使用基础类型作为鉴别字段时的差别,会发现如下几点:

    • javaType="cn.fredworks.model.order.OrderCategory":javaType使用完整的枚举类名
    • column="order_category":列名仍然需要
    • typeHandler="cn.fredworks.persistence.mybatis.typehandler.order.OrderCategoryTypeHandler":多了转换数据库存储类型为java枚举类型的转换器
    • case value="NormalOrder":这里是重点。它使用枚举项的字面值作为case进行比对的值。

    实际上,如果我们跟踪mybtais的代码的话,我们会发现在比对时,它先将Order对象的orderCategory属性,用String.toString(value)方法转换为字符串以后,再跟<case value="xxx" 中,value指定的值进行比对。
    也就是说:

    • 鉴别器认的,是转换为java类型后的值,不是数据库中存储的原始值。
    • case语句的value指定的参照值,是和鉴别属性使用String.toString()处理后的值进行比对

    2. 自定义枚举作为鉴别器的鉴别属性

    假如我们的OrderCategory更加复杂一点:

    /**
     * 订单类型
     * @author FredWorks
     * 2019年12月5日 上午9:49:14
     */
    public enum OrderCategory {
    
        /**
         * 普通订单
         */
        NormalOrder(1),
        
        /**
         * 计时订单
         */
        TimedOrder(2),
        
        /**
         * 场地订单
         */
        SiteOrder(3);
        
        /**
         * 下标值。
         */
        private int index;
            
        /**
         * 使用下标构造枚举。
         * 构造函数
         * @param index 用于构造枚举的下标。
         */
        private OrderCategory(int index) {
            this.index = index;
        }
    
        /**
         * 返回当前枚举的下标。
         * @return 返回本枚举的下标值
         */ 
        public int index() {
            return index;
        }
    
        /**
         * 根据枚举的下标获取对应的枚举。如果没有找到对应的枚举,则返回null。
         * @param index 枚举的下标值
         * @return 根据下标找到的枚举。如果没有匹配的枚举,返回空指针null。
         */ 
        public static OrderCategory indexOf(int index) {
            for (OrderCategory item : OrderCategory.values()) {
                if (item.index == index) {
                    return item;
                }
            }
    
            return null;
        }
    
        /**
         * 根据枚举的字面值获取对应的枚举。如果没有找到对应的枚举,则返回null。
         * @param name 枚举的字面值
         * @return 根据字面值找到的枚举。如果没有匹配的枚举,返回空指针null。
         */ 
        public static OrderCategory nameOf(String name) {
            for (OrderCategory item : OrderCategory.values()) {
                if (item.name().equals(name)) {
                    return item;
                }
            }
    
            return null;
        }
        
        /**
         * 输出格式为 name(index)
         */
        @Override
        public String toString() {
            return super.toString() + "(" + this.index + ")";
        }
    }
    

    此时的mybatis结果集,必须这样写:

    <mapper namespace="cn.fredworks.persistence.mybatis.dao.order.IOrderDao">
        <resultMap type="cn.fredworks.model.order.Order" id="orderResultMap">
            <id column="id" property="id" />
            <result column="total_rice" property="totalPrice" />
            <result column="order_category" property="orderCategory" 
                typeHandler="cn.fredworks.persistence.mybatis.typehandler.order.OrderCategoryTypeHandler"/>
            <discriminator javaType="cn.fredworks.model.order.OrderCategory" column="order_category"
                    typeHandler="cn.fredworks.persistence.mybatis.typehandler.order.OrderCategoryTypeHandler">
                <case value="NormalOrder(1)" resultType="cn.fredworks.model.order.Order"/>
                <case value="TimedOrder(2)" resultType="cn.fredworks.model.order.TimedOrder"/>
                    <result column="consuming_minutes" property="consumingMinutes" />
                </case>
                <case value="SiteOrder(3)" resultType="cn.fredworks.model.order.SiteOrder"/>
                    <result column="site_id" property="siteId" />
                </case>
            </discriminator>
        </resultMap>
        ......
    </mapper>
    

    因为:

    • 我override了枚举的toString()方法。
    • 假设枚举值是 enumItem,mybatis执行 String.toString(enumItem) 的结果,实际上是enumItem.toString()的结果。

    3. mybatis的<if test="">标签如何使用枚举作为条件

    仍然使用上面的OrderCategory作为例子。
    假如,我们要在查询中,使用 <if test=""来动态拼装查询语句,且test比对的属性,是基本类型,应当这样写:

                <if test="totalPrice >= 60 and totalPrice <= 100">
    

    但如果test比对的属性是枚举类型,则应当这样写:

                <if test="reserveType == @cn.fredworks.model.order.OrderCategory@TimedOrder">
    
    • mybatis的动态sql,基于ognl进行描述。
    • 对枚举作为比对值,使用@类名@枚举项来引用。
    • 无论枚举项是 OrderCategory.TimedOrder,还是OrderCategory.TimedOrder(2),其枚举项都是TimedOrder。

    相关文章

      网友评论

        本文标题:2019-12-05 mybatis的discriminator

        本文链接:https://www.haomeiwen.com/subject/jfargctx.html