问题:

  由于公司业务扩大,各个子系统陆续迁移和部署在不同的数据源上,这样方便扩容,但是因此引出了一些问题。

  举个例子:在查询"订单"(位于订单子系统)列表时,同时需要查询出所关联的"用户"(位于账户子系统)的姓名,而这时由于数据存储在不同的数据源上,没有办法通过一条连表的sql获取到全部的数据,而是必须进行两次数据库查询,从不同的数据源分别获取数据,并且在web服务器中进行关联映射。在观察了一段时间后,发现进行关联映射的代码大部分都是模板化的,因此产生一个想法,想要把这些模板代码抽象出来,简化开发,也增强代码的可读性。同时,即使在同一个数据源上,如果能将多表联查的需求转化为单表多次查询,也能够减少代码的耦合,同时提高数据库效率。

设计主要思路:

在关系型数据库中:

  一对一的关系一般表示为:一方的数据表结构中存在一个业务上的外键关联另一张表的主键(订单和用户是一对一的关系,则订单表中存在外键对应于用户表的主键)。

  一对多的关系一般表示为:多方的数据中存在一个业务上的外键关联一方的主键(门店和订单是一对多的关系,则订单表中存在外键对应于门店的主键)。

而在非关系型数据库中:

  一对一的关系一般表示为:一方中存在一个属性,值为关联的另一方的数据对象(订单和用户是一对一的关系,则订单对象中存在一个用户属性)。

  一对多的关系一般表示为:一方中存在一个属性,值为关联的另一方的数据对象列表(门店和所属订单是一对多的关系,则门店对象存在一个订单列表(List)属性)。

  可以看出,java的对象机制,天然就支持非关系型的数据模型,因此大概的思路就是将查询出来的两个列表进行符合要求的映射即可。

pojo类:

public class OrderForm {/*** 主键id* */private String id;/*** 所属门店id* */private String shopID;/*** 关联的顾客id* */private String customerID;/*** 关联的顾客model* */private Customer customer;
}public class Customer {/*** 主键id* */private String id;/*** 姓名* */private String userName;
}public class Shop {/*** 主键id* */private String id;/*** 门店名* */private String shopName;/*** 订单列表 (一个门店关联N个订单 一对多)* */private List<OrderForm> orderFormList;
}

辅助工具函数:

  /**** 将通过keyName获得对应的bean对象的get方法名称的字符串* @param keyName 属性名* @return  返回get方法名称的字符串*/private static String makeGetMethodName(String keyName){//:::将第一个字母转为大写String newKeyName = transFirstCharUpperCase(keyName);return "get" + newKeyName;}/**** 将通过keyName获得对应的bean对象的set方法名称的字符串* @param keyName 属性名* @return  返回set方法名称的字符串*/private static String makeSetMethodName(String keyName){//:::将第一个字母转为大写String newKeyName = transFirstCharUpperCase(keyName);return "set" + newKeyName;}/*** 将字符串的第一个字母转为大写* @param str 需要被转变的字符串* @return 返回转变之后的字符串*/private static String transFirstCharUpperCase(String str){return str.replaceFirst(str.substring(0, 1), str.substring(0, 1).toUpperCase());}/*** 判断当前的数据是否需要被转换** 两个列表存在一个为空,则不需要转换* @return 不需要转换返回 false,需要返回 true* */private static boolean needTrans(List beanList,List dataList){if(listIsEmpty(beanList) || listIsEmpty(dataList)){return false;}else{return true;}}/*** 列表是否为空* */private static boolean listIsEmpty(List list){if(list == null || list.isEmpty()){return true;}else{return false;}}/*** 将javaBean组成的list去重 转为map, key为bean中指定的一个属性** @param beanList list 本身* @param keyName 生成的map中的key* @return* @throws Exception*/public static Map<String,Object> beanListToMap(List beanList,String keyName) throws Exception{//:::创建一个mapMap<String,Object> map = new HashMap<>();//:::由keyName获得对应的get方法字符串String getMethodName = makeGetMethodName(keyName);//:::遍历beanListfor(Object obj : beanList){//:::如果当前数据是hashMap类型if(obj.getClass() == HashMap.class){Map currentMap = (Map)obj;//:::使用keyName从map中获得对应的keyString result = (String)currentMap.get(keyName);//:::放入map中(如果key一样,则会被覆盖去重)
                map.put(result,currentMap);}else{//:::否则默认是pojo对象//:::获得get方法Method getMethod = obj.getClass().getMethod(getMethodName);//:::通过get方法从bean对象中得到数据keyString result = (String)getMethod.invoke(obj);//:::放入map中(如果key一样,则会被覆盖去重)
                map.put(result,obj);}}//:::返回结果return map;}

一对一连接接口定义:

/*** 一对一连接 :  beanKeyName <---> dataKeyName 作为连接条件** @param beanList 需要被存放数据的beanList(主体)* @param beanKeyName   beanList中连接字段key的名字* @param beanModelName  beanList中用来存放匹配到的数据value的属性* @param dataList  需要被关联的data列表* @param dataKeyName 需要被关联的data中连接字段key的名字** @throws Exception*/public static void oneToOneLinked(List beanList, String beanKeyName, String beanModelName, List dataList, String dataKeyName) throws Exception { }

  如果带入上述一对一连接的例子,beanList是订单列表(List<OrderFrom>),beanKeyName是订单用于关联用户的字段名称(例如外键“OrderForm.customerID”),beanModelName是用于存放用户类的字段名称("例如OrderForm.customer"),dataList是顾客列表(List<Customer>),dataKeyName是被关联数据的key(例如主键"Customer.id")。

一对一连接代码实现:

/*** 一对一连接 :  beanKeyName <---> dataKeyName 作为连接条件** @param beanList 需要被存放数据的beanList(主体)* @param beanKeyName   beanList中连接字段key的名字* @param beanModelName  beanList中用来存放匹配到的数据value的属性* @param dataList  需要被关联的data列表* @param dataKeyName 需要被关联的data中连接字段key的名字** @throws Exception*/public static void oneToOneLinked(List beanList, String beanKeyName, String beanModelName, List dataList, String dataKeyName) throws Exception {//:::如果不需要转换,直接返回if(!needTrans(beanList,dataList)){return;}//:::将被关联的数据列表,以需要连接的字段为key,转换成map,加快查询的速度Map<String,Object> dataMap = beanListToMap(dataList,dataKeyName);//:::进行数据匹配连接
       matchedDataToBeanList(beanList,beanKeyName,beanModelName,dataMap);}/*** 将批量查询出来的数据集合,组装到对应的beanList之中* @param beanList 需要被存放数据的beanList(主体)* @param beanKeyName   beanList中用来匹配数据的属性* @param beanModelName  beanList中用来存放匹配到的数据的属性* @param dataMap  data结果集以某一字段作为key对应的map* @throws Exception*/private static void matchedDataToBeanList(List beanList, String beanKeyName, String beanModelName, Map<String,Object> dataMap) throws Exception {//:::获得beanList中存放对象的key的get方法名String beanGetMethodName = makeGetMethodName(beanKeyName);//:::获得beanList中存放对象的model的set方法名String beanSetMethodName = makeSetMethodName(beanModelName);//:::遍历整个beanListfor(Object bean : beanList){//:::获得bean中key的method对象Method beanGetMethod = bean.getClass().getMethod(beanGetMethodName);//:::调用获得当前的keyString currentBeanKey = (String)beanGetMethod.invoke(bean);//:::从被关联的数据集map中找到匹配的数据Object matchedData = dataMap.get(currentBeanKey);//:::如果找到了匹配的对象if(matchedData != null){//:::获得bean中对应model的set方法Class clazz = matchedData.getClass();//:::如果匹配到的数据是hashMapif(clazz == HashMap.class){//:::转为父类map class用来调用set方法clazz = Map.class;}//:::获得主体bean用于存放被关联对象的set方法Method beanSetMethod = bean.getClass().getMethod(beanSetMethodName,clazz);//:::执行set方法,将匹配到的数据放入主体数据对应的model属性中
                beanSetMethod.invoke(bean,matchedData);}}}

一对多连接接口定义:

/*** 一对多连接 :  oneKeyName <---> manyKeyName 作为连接条件** @param oneDataList       '一方' 数据列表* @param oneKeyName        '一方' 连接字段key的名字* @param oneModelName      '一方' 用于存放 '多方'数据的列表属性名* @param manyDataList      '多方' 数据列表* @param manyKeyName       '多方' 连接字段key的名字**  注意:  '一方' 存放 '多方'数据的属性oneModelName类型必须为List** @throws Exception*/public static void oneToManyLinked(List oneDataList,String oneKeyName,String oneModelName,List manyDataList,String manyKeyName) throws Exception {}

  如果带入上述一对多连接的例子,oneDataList是门店列表(List<Shop>),oneKeyName是门店用于关联订单的字段名称(例如主键“Shop.id”),oneModelName是用于存放订单列表的字段名称(例如"Shop.orderFomrList"),manyDataList是多方列表(List<OrderForm>),manyKeyName是被关联数据的key(例如外键"OrderFrom.shopID")。

一对多连接代码实现:

 /*** 一对多连接 :  oneKeyName <---> manyKeyName 作为连接条件** @param oneDataList       '一方' 数据列表* @param oneKeyName        '一方' 连接字段key的名字* @param oneModelName      '一方' 用于存放 '多方'数据的列表属性名* @param manyDataList      '多方' 数据列表* @param manyKeyName       '多方' 连接字段key的名字**  注意:  '一方' 存放 '多方'数据的属性oneModelName类型必须为List** @throws Exception*/public static void oneToManyLinked(List oneDataList,String oneKeyName,String oneModelName,List manyDataList,String manyKeyName) throws Exception {if(!needTrans(oneDataList,manyDataList)){return;}//:::将'一方'数据,以连接字段为key,转成map,便于查询Map<String,Object> oneDataMap = beanListToMap(oneDataList,oneKeyName);//:::获得'一方'存放 '多方'数据字段的get方法名String oneDataModelGetMethodName = makeGetMethodName(oneModelName);//:::获得'一方'存放 '多方'数据字段的set方法名String oneDataModelSetMethodName = makeSetMethodName(oneModelName);//:::获得'多方'连接字段的get方法名String manyDataKeyGetMethodName = makeGetMethodName(manyKeyName);try {//:::遍历'多方'列表for (Object manyDataItem : manyDataList) {//:::'多方'对象连接key的值
                String manyDataItemKey;//:::判断当前'多方'对象的类型是否是 hashMapif(manyDataItem.getClass() == HashMap.class){//:::如果是hashMap类型的,先转为Map对象Map manyDataItemMap = (Map)manyDataItem;//:::通过参数key 直接获取对象key连接字段的值manyDataItemKey = (String)manyDataItemMap.get(manyKeyName);}else{//:::如果是普通的pojo对象,则通过反射获得get方法来获取key连接字段的值//:::获得'多方'数据中key的method对象Method manyDataKeyGetMethod = manyDataItem.getClass().getMethod(manyDataKeyGetMethodName);//:::调用'多方'数据的get方法获得当前'多方'数据连接字段key的值manyDataItemKey = (String) manyDataKeyGetMethod.invoke(manyDataItem);}//:::通过'多方'的连接字段key从 '一方' map集合中查找出连接key相同的 '一方'数据对象Object matchedOneData = oneDataMap.get(manyDataItemKey);//:::如果匹配到了数据,才进行操作if(matchedOneData != null){//:::将当前迭代的 '多方'数据 放入 '一方' 的对应的列表中
                    setManyDataToOne(matchedOneData,manyDataItem,oneDataModelGetMethodName,oneDataModelSetMethodName);}}}catch(Exception e){throw new Exception(e);}}/*** 将 '多方' 数据存入 '一方' 列表中* @param oneData 匹配到的'一方'数据* @param manyDataItem  当前迭代的 '多方数据'* @param oneDataModelGetMethodName 一方列表的get方法名* @param oneDataModelSetMethodName 一方列表的set方法名* @throws Exception*/private static void setManyDataToOne(Object oneData,Object manyDataItem,String oneDataModelGetMethodName,String oneDataModelSetMethodName) throws Exception {//:::获得 '一方' 数据中存放'多方'数据属性的get方法Method oneDataModelGetMethod = oneData.getClass().getMethod(oneDataModelGetMethodName);//::: '一方' 数据中存放'多方'数据属性的set方法
        Method oneDataModelSetMethod;try {//::: '一方' set方法对象oneDataModelSetMethod = oneData.getClass().getMethod(oneDataModelSetMethodName,List.class);}catch(NoSuchMethodException e){throw new Exception("未找到满足条件的'一方'set方法");}//:::获得存放'多方'数据get方法返回值类型Class modelType = oneDataModelGetMethod.getReturnType();//::: get方法返回值必须是Listif(modelType.equals(List.class)){//:::调用get方法,获得数据列表List modelList = (List)oneDataModelGetMethod.invoke(oneData);//:::如果当前成员变量为nullif(modelList == null){//:::创建一个新的ListList newList = new ArrayList<>();//:::将当前的'多方'数据存入list
                newList.add(manyDataItem);//:::将这个新创建出的List赋值给 '一方'的对象
                oneDataModelSetMethod.invoke(oneData,newList);}else{//:::如果已经存在了List//:::直接将'多方'数据存入list
                modelList.add(manyDataItem);}}else{throw new Exception("一对多连接时,一方指定的model对象必须是list类型");}}

  测试用例在我的github上面 https://github.com/1399852153/linkedQueryUtil。

  这是我的第一篇技术博客,无论是排版还是分享的内容上面都还有很多的不足之处,希望大家指出,互相交流,互相进步。

转载于:https://www.cnblogs.com/xiaoxiongcanguan/p/9886730.html

java 分库关联查询工具类相关推荐

  1. NC6 查询工具类 QueryUtil.java

    NC6 查询工具类 package nc.impl.am.db;import java.util.ArrayList; import java.util.Collections; import jav ...

  2. java redis remove_最全的Java操作Redis的工具类

    RedisUtil 当前版本:1.1 增加更全的方法,对以前的部分方法进行了规范命名,请放心替换成新版本. 介绍 最全的Java操作Redis的工具类,使用StringRedisTemplate实现, ...

  3. java Excel导入导出工具类 及使用demo

    java Excel导入导出工具类 及使用demo 前言:相信进来的都是想尽快解决问题的,话不多说,按照以下步骤来,可以操作导出excel到本地,导入同理,自行学习.步骤一:直接复制以下excel工具 ...

  4. 免费天气查询工具类源码,开箱即用,根据中国气象局API编写。高效稳定

    文章目录 引言 相关依赖 WeatherUtil工具类代码 http工具类 测试 引言 使用Java语言,根据中国气象局API编写的查询天气工具类,代码引入就能用,代码中对异常做了处理,无论是否查询成 ...

  5. java项目常用的工具类

    前言 在开发过程中,我们会遇到很多繁琐或者棘手的问题,但是,这些问题往往会存在一些便捷的工具类,来简化我们的开发,下面是我工作中经常使用到的工具类 常用工具类 日期工具类 import java.te ...

  6. 【Java 代码实例 13】Java操作pdf的工具类itext

    目录 一.什么是iText? 二.引入jar 1.项目要使用iText,必须引入jar包 2.输出中文,还要引入下面```itext-asian.jar```包 3.设置pdf文件密码,还要引入下面` ...

  7. Java 线程 - 基础及工具类 (二)

    Java 并发系列文章 Java 线程 - 并发理论基础(一) Java 线程 - 基础及工具类 (二) Java 线程 - 并发设计模式 (三) Java 线程(二) 通用的线程生命周期 Java ...

  8. java图片缩放工具类,一个JAVA图形缩放处置工具类

    一个JAVA图形缩放处理工具类 调用的例子 import java.io.FileOutputStream; import java.io.IOException; import javax.imag ...

  9. JAVA I/O流工具类TextFile

    JAVA I/O流工具类TextFile由广州疯狂软件java培训分享: 本文是一个TextFile类,通过这个类我们可以调用其中的方法来简化对文件的读写,这段代码的可用性比较强.这个TextFile ...

最新文章

  1. 【Java多线程编程】选号程序
  2. Vue.js实训【基础理论(5天)+项目实战(5天)】博客汇总表【详细笔记】
  3. 如何修改Fiori launchpad里tile count调用的触发时间间隔
  4. Android基础知识大纲
  5. Mysql学习总结(66)——设置MYSQL数据库编码为UTF-8
  6. leetcode621 贪心:任务安排
  7. main方法 如何去掉http debug日志_Spring Boot 常见错误及解决方法
  8. Java HashSet的元素内容变化导致的问题
  9. ipsec over gre与gre over ipsec
  10. WAP1.X/WAP2.0以及WAP浏览器的协议版本
  11. Java实战之管家婆记账系统(1)——项目简述
  12. android 拨打电话 发送短信 权限,Android中发送短信和拨打电话
  13. 鸿蒙大陆罪恶深渊哪里出,库库马力
  14. 浅谈阿里云混合云新一代运维平台演进与实践
  15. 狂胜——Redis学习笔记
  16. 《穿普拉达的女王》-观后感
  17. 三伏天不能吃冷饮?7月这些蜚语你中招没
  18. 如何在redhat下安装办公软件(openoffice)
  19. 将数据库中的表导出到word
  20. 免费的地理位置信息geo-ip数据库maxmind

热门文章

  1. Linux管道的原子性,管道的原子性 linux写操作原子性
  2. 通过连接池无法连接mysql_连接池无法链接数据库
  3. html 正方形缩略图,html – 纯CSS图像缩略图
  4. pytorch torch.Tensor.view
  5. Elasticsearch IK分词器
  6. Ubuntu 安装R/Rstudio
  7. labview嵌入c代码_LabVIEW是什么?与其他组态软件有何分别?
  8. ERP核心业务流程和Oracle-ERP业务和数据对象分析
  9. Linux rescue救援(光盘修复)模式详解
  10. 批处理中的使用问题记录