文章目錄
  1. flying-阳春
    1. 高性能的跨数据库查询
    2. customTypeHandler
      1. 跨库或逻辑查询
  2. flying-初雪
    1. 自定义主键生成器
    2. 使用或逻辑查询
      1. 普通或逻辑查询
      2. 外键或逻辑查询
    3. 优化使用体验
    4. 全面提升代码质量

flying-阳春

阳春 对应 flying 版本号为 0.9.4(适用于 mybatis-3.4.x),主要新增特性如下:

高性能的跨数据库查询

经过两个版本的沉淀,现在 flying 终于推出了简单易用的跨库查询语法,具体如下:

1
flying#{?}(smartDataSourceId:dataBaseName):select

这个方法永远都会使用 dataBaseName 数据库,无论它在哪个数据源内被调用;如果需要跨源,它会使用 smartDataSourceId 数据源(请确保这是一个智能数据源);

同时从此版本开始,您需要将需要将 flying 的语境访问器 引入您的 spring 环境配置中,具体方式有两种,一种是在环境 spring 配置文件中加入bean,另一种是在 spring 配置文件的扫描器中加上 “indi.mybatis.flying” 路径,如下:

1
<context:component-scan base-package="indi.demo.flying.*, indi.mybatis.flying" />

customTypeHandler

flying-阳春@FieldMapperAnnotation@ConditionMapperAnnotation 增加了 customTypeHandler 属性,使您可以用自定义的 TypeHandler 来处理变量映射,因为 customTypeHandler 具有最高优先级。一个使用场景如下:

跨库或逻辑查询

customTypeHandler 使跨库或查询成为可能,但仅限于外键 ID 相等这一种条件。我们假设 person 和 role 不在一个数据库中,如果想查询对应role 的 ID 为 1 或者为 2 的 person,我们可以在 person 的条件类中加入如下变量:

1
2
3
4
5
6
7
8
9
10
11
12
13
import org.apache.ibatis.type.IntegerTypeHandler;

@Or({
@ConditionMapperAnnotation(dbFieldName = "role_id", conditionType = ConditionType.Equal, customTypeHandler = IntegerTypeHandler.class),
@ConditionMapperAnnotation(dbFieldName = "role_id", conditionType = ConditionType.Equal, customTypeHandler = IntegerTypeHandler.class) })
private Object[] role1EqualsOrRole2Equals;

public Object[] getRole1EqualsOrRole2Equals () {
return role1EqualsOrRole2Equals;
}
public void setRole1EqualsOrRole2Equals (Object... role1EqualsOrRole2Equals) {
this. role1EqualsOrRole2Equals = role1EqualsOrRole2Equals;
}

如果想查询对应role 的 ID 为 1 或者为 2 的 person 的话只需如下代码即可:

1
2
3
PersonCondition pc = new PersonCondition();
pc.setRole1EqualsOrRole2Equals(1,2);
Person<Collection> persons = personService.selectAll(pc);

flying-初雪

初雪 对应 flying 版本号为 0.8.3(适用于 mybatis-3.3.x)和 0.9.3(适用于 mybatis-3.4.x),主要新增特性如下:

自定义主键生成器

为满足个性化主键需要,flying 新增了自定义主键生成器,为此我们在flying:insert语句中新增了括号元素,如下:

1
2
3
flying:insert(uuid)                      使用标准uuid作主键
flying:insert(uuid_no_line) 使用无下横线的uuid作主键
flying:insert(millisecond) 使用毫秒数作主键(需保证每秒并发在1000以下)

当然更多的情况是您会自定义自己的主键生成器,只要您的主键生成器实现了 flying 中的 indi.mybatis.flying.type.KeyHandler 接口即可,比如这样调用一个自定义的主键生成器类:

1
flying:insert(indi.mybatis.flying.handlers.MySnowFlakeKeyHandler)

(上面的 indi.mybatis.flying.handlers.MySnowFlakeKeyHandler 是一个雪花主键生成器的 java 版本实现。雪花主键生成器由 tweeter 发明用于处理大规模并行写入,主键采用 float 类型存储以节省资源,自带递增无需 order by,单台主机每秒可产生 400 万个不同主键,最多可 1024 台主机集群同时工作。flying 中提供了一个实现方式,代码见此

在某些特殊业务场景中,您需要使用主键表达一定的业务含义,这时自定义主键生成器会显得非常有效。

这里有一个使用了自定义主键生成器的例子,相信您看完以后会对此特性了然于胸。

使用或逻辑查询

普通或逻辑查询

“或”逻辑查询是 初雪 版本新增的最重要内容。为了实现此特性 flying 新增了 Or 标签类(代码见此),这个标签的内容是ConditionMapperAnnotation标签的数组,所以在查询条件类中可以有如下标签代码:

1
2
3
4
5
@Or({
@ConditionMapperAnnotation(dbFieldName = "name", conditionType = ConditionType.HeadLike),
@ConditionMapperAnnotation(dbFieldName = "age", conditionType = ConditionType.Equal),
@ConditionMapperAnnotation(dbFieldName = "name", conditionType = ConditionType.HeadLike)
})

(上面是实现 name like ‘XXX%’ or age = ‘YYY’ or name like ‘ZZZ%’ 查询的条件)

同时为了赋值方便,我们采用Object数组的不定参数形式作为变量类型,于是整个代码变成了:

1
2
3
4
5
6
7
8
9
10
11
12
13
@Or({
@ConditionMapperAnnotation(dbFieldName = "name", conditionType = ConditionType.HeadLike),
@ConditionMapperAnnotation(dbFieldName = "age", conditionType = ConditionType.Equal),
@ConditionMapperAnnotation(dbFieldName = "name", conditionType = ConditionType.HeadLike)
})
private Object[] condition1;

public Object[] getCondition1 () {
return condition1;
}
public void setCondition1 (Object... condition1) {
this. condition1 = condition1;
}

如果我们描述 “name like ‘张%’ or age = 27 or name like ‘李%’ “,代码如下:

1
2
personCondition.setCondition1("张", 27, "李");
/* 注意参数顺序和 condition1 上 @ConditionMapperOrAnnotation 的内部顺序一致 */

您之前掌握的绝大部分 ConditionMapperAnnotation 都可以写在 @Or 中,只有本身就是集合型的条件类型例外,以下列出不能进入 @Or 的类型:

MultiLikeANDMultiLikeORInNotIn

除此之外的查询条件均可以参与或查询。

外键或逻辑查询

flying 在同库跨表查询时也可以做不同表上条件的或逻辑查询,比如我们要实现 person.name = ‘XXX’ or role.name = ‘YYY’ 查询,其中 role 是 person 业务上的父对象。我们可以在 role 的条件类中加入如下变量:

1
2
3
4
5
6
7
8
9
10
11
@Or({ 
@ConditionMapperAnnotation(dbFieldName = "name", conditionType = ConditionType.Equal),
@ConditionMapperAnnotation(dbFieldName = "name", conditionType = ConditionType.Equal, subTarget = mypackage.Person.class) })
private Object[] roleNameEqualsOrPersonNameEquals;

public Object[] getRoleNameEqualsOrPersonNameEquals () {
return roleNameEqualsOrPersonNameEquals;
}
public void setRoleNameEqualsOrPersonNameEquals (Object... roleNameEqualsOrPersonNameEquals) {
this. roleNameEqualsOrPersonNameEquals = roleNameEqualsOrPersonNameEquals;
}

上面的代码中第一行 ConditionMapperAnnotation 指的是 role 表,第二行指的是 person 表,因为是由 role 指向 person,所以第二行出现了 subTarget 参数用来引导路径,它的值就是业务上子对象的类路径。

值得注意的是,外键或逻辑查询中,跨表的 @Or 条件永远要写在业务上的父对象里,这是考虑到从子对象上寻找父对象并非唯一(例如多重外键情况,一个 person 有多个 role 型父对象,分别表示主要角色和次要角色等),然而从父对象上寻找子对象永远是唯一的。

如果您要查询用户名为“张三”或角色名为“wfadmin”的用户时,您只需这样做:

1
2
3
4
5
RoleConditon rc = new RoleCondition();
rc.setRoleNameEqualsOrPersonNameEquals("wfadmin","张三");
Person p = new Person();
p.setRole(rc);
Person<Collection> persons = personService.selectAll(p);

无论 role 是 person 业务上的直接父对象还是间接父对象都可以这样查询。

优化使用体验

@QueryMapperAnnotation 现在可以省略,只要您的某个类既继承实体 pojo 又实现 Conditionable 接口 flying 就可以判断出它是相关 pojo 的条件类。当然这个标签出现在代码里也没有任何问题。

全面提升代码质量

初雪 中,我们把代码质量作为提升的重要一环,同时使用 阿里 P3C 和 SonarQube 来修正代码缺陷,当您阅读 flying 源码时您会深切感受到这一点。

文章目錄
  1. flying-阳春
    1. 高性能的跨数据库查询
    2. customTypeHandler
      1. 跨库或逻辑查询
  2. flying-初雪
    1. 自定义主键生成器
    2. 使用或逻辑查询
      1. 普通或逻辑查询
      2. 外键或逻辑查询
    3. 优化使用体验
    4. 全面提升代码质量