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。
网友评论