基于FleaJPAQuery实现JPA分表查询
本文采用 EclipseLink的JPA实现,相关FleaJPAQuery的接入使用请移步我的另外几篇博文。
首先讨论一下,为了实现JPA分表查询,我们需要做哪些事情:
- 分表规则定义(即从主表到分表的转换实现)
- 分表查询实现(即JPA标准化查询组件根据分表规则查询具体分表)
1. JPA标准化查询
在JPA中,实体对应的表由如下的注解定义:
@Entity@Table(name = \"flea_login_log\")
如上可见,实体类实际上只会对应一个表名,单纯从这里是无法实现分表查询。
那么既然这样无法分表,我们选择退而求其次,看看表名是什么时候,被那个对象使用,因为我们可以确认查询最后的表名,一定是使用的注解定义的表名。
下面是调试过程的发现:
com.huazie.frame.db.jpa.common.FleaJPAQuery
/*** <p> Flea JPA查询对象池获取之后,一定要调用该方法进行初始化 </p>** @param entityManager JPA中用于增删改查的接口* @param sourceClazz 实体类类对象* @param resultClazz 操作结果类类对象* @since 1.0.0*/public void init(EntityManager entityManager, Class sourceClazz, Class resultClazz) {this.entityManager = entityManager;this.sourceClazz = sourceClazz;this.resultClazz = resultClazz;// 从持久化接口中获取标准化生成器criteriaBuilder = entityManager.getCriteriaBuilder();// 通过标准化生成器 获取 标准化查询对象if (ObjectUtils.isEmpty(resultClazz)) {// 行记录查询结果criteriaQuery = criteriaBuilder.createQuery(sourceClazz);} else {// 单个查询结果criteriaQuery = criteriaBuilder.createQuery(resultClazz);}// 通过标准化查询对象,获取根SQL表达式对象root = criteriaQuery.from(sourceClazz);}
如下两张图是根SQL表达式对象 Root 的Debug视图,发现存储实际表名的是 DatabaseTable 对象。
那么既然找到了表名实际相关的地方,下面的重点就是如何在使用的JPA标准化查询的过程中,动态改变查询的表名。下面给出上述我们需要做的事情的解决方案:
2. 分表规则定义
实体类中定义的表名,我们可以理解为主表名;分表名的命名规则首先需要确定一下,定义如下配置:
<?xml version=\"1.0\" encoding=\"UTF-8\"?><tables><!-- 定义分表配置name : 分表对应的主表名exp : 分表名表达式 (FLEA_TABLE_NAME)_(列名大写)_(列名大写)--><table name=\"flea_login_log\" exp=\"(FLEA_TABLE_NAME)_(CREATE_DATE)\" desc=\"Flea登录日志表分表规则\"><splits><!-- 定义分表后缀key : 分表类型关键字 (可查看 com.huazie.frame.db.common.table.split.TableSplitEnum )column : 分表属性列字段名implClass : 分表后缀转换实现类--><split key=\"yyyymm\" column=\"create_date\" implClass=\"com.huazie.frame.db.common.table.split.impl.YYYYMMTableSplitImpl\"/></splits></table></tables>
分表规则相关实现代码,可以移步 GitHub 查看 TableSplitHelper
3. 分表查询实现
在上述分表规则定义中, 我们可以看到分表名表达式exp是由 主表名 和 分表字段 组成,分表字段的转换实现规则由split定义。
@Overridepublic void handle(CriteriaQuery criteriaQuery, Object entity) throws Exception {if (ObjectUtils.isEmpty(criteriaQuery) || ObjectUtils.isEmpty(entity)) {return;}// 获取分表信息(包括主表名 和 分表名 【如果存在分表返回】)SplitTable splitTable = EntityUtils.getSplitTable(entity);// 存在分表,需要查询指定分表if (StringUtils.isNotBlank(splitTable.getSplitTableName())) {Set<Root<?>> roots = criteriaQuery.getRoots();if (CollectionUtils.isNotEmpty(roots)) {// 重新设置 查询的分表表名((EntityTypeImpl<?>) roots.toArray(new Root<?>[0])[0].getModel()).getDescriptor().setTableName(splitTable.getSplitTableName());}}}
JPA分表查询相关代码可以 移步 GitHub 查看 FleaJPAQuery 和 EclipseLinkTableSplitHandler; 自测类可以查看 AuthTest。