• java实现类SQL的join操作


    SQL是结构化查询语言的简称,给数据库操作带来很大的方便。

    随着大数据的流行,hive、spark、flink都开始支持SQL的操作。

    但在java内部并没有对SQL操作的支持,使用java stream API进行数据关联操作的代码惨不忍睹。

    因此,我基于Google guava的Table,封装了常用的join操作。分享并记录在这里。

    目前支持的join类型包括如下几类,基本上常见的join都已经覆盖到了。

    public enum JoinStrategy {
        INNER_JOIN,//内连接
        LEFT_JOIN,//左连接
        RIGHT_JOIN,//右连接
        LEFT_SEMI_JOIN,//左半连接,结果只取左表,连接条件是左表和右表的交集
        LEFT_ANTI_JOIN,//左半连接,结果只取左表,连接条件是左表与右表的差集
        FULL_JOIN;//全连接
    }

    闲言少叙,下面就是JoinUtils的硬代码

    一、构造Table对象

    由于大部分人拿到的数据要么并不是Table类型,所以我首先提供了从这两个对象转Table对象的方法

    由于从jdbc查出来的数据,默认格式就是一个List<Map<>>,下面这个createTable方法可以将List<Map<>>生成一个Table对象

    因为我们后面要进行join操作,所以需要提供一个id作为每一行的rowKey.

    /**
         * 基于List<Map>创建一个表对象,将使用用户提供的id的在map中对应的value值作为rowKey
         *
         * @param listMap
         * @param id
         * @param <R>
         * @param <C>
         * @param <V>
         * @return
         */
        public static <R, C, V> Table<R, C, V> createTable(List<Map<C, V>> listMap, R id) {
            Table<R, C, V> table = HashBasedTable.create();
            for (Map<C, V> map : listMap) {
                for (Map.Entry<C, V> entry : map.entrySet()) {
                    table.put((R) map.get(id), entry.getKey(), entry.getValue());
                }
            }
            return table;
        }

    有些情况下,我们的数据并不是List<Map<>>,而是别人给我们传递的一组对象,这个情况我也考虑到了。

    下面是一个从List<T>对象生成Table对象的方法。

     public static <T, R, C, V> Table<R, C, V> createTable(List<T> objects, Class<T> aClass, String primaryKey) {
            Table<R, C, V> table = HashBasedTable.create();
            List<String> fieldNames = getFieldNames(aClass);
            for (T t : objects) {
                Object primaryKeyValue = getFieldValue(aClass, t, primaryKey);
                for (String field : fieldNames) {
                    Object fieldValue = getFieldValue(aClass, t, field);
                    table.put((R) primaryKeyValue, (C) field, (V) fieldValue);
                }
            }
            return table;
        }

    二、join操作

    对外我暴露了一个join工厂方法,用来基于用户提供的策略选择不同的底层实现来操作。

    public static <R, C, V> Table<R, C, V> join(Table<R, C, V> left, Table<R, C, V> right, JoinStrategy strategy) {
            switch (strategy) {
                case LEFT_JOIN:
                    return leftJoin(left, right);
                case RIGHT_JOIN:
                    return rightJoin(left, right);
                case LEFT_SEMI_JOIN:
                    return leftSemiJoin(left, right);
                case LEFT_ANTI_JOIN:
                    return leftAntiJoin(left, right);
                case FULL_JOIN:
                    return fullJoin(left, right);
                default:
                    return innerJoin(left, right);
            }
        }

    测试一下

    //测试代码:
    class JoinUtilsTest {
    
        private Table<String, String, Object> leftTable;
        private Table<String, String, Object> rightTable;
    
        @BeforeEach
        void createTable() {
            Map<String, Object> map = new HashMap<>();
            Map<String, Object> map1 = new HashMap<>();
            Map<String, Object> map2 = new HashMap<>();
            map.put("id", "a");
            map.put("name", "x");
            map1.put("id", "b");
            map1.put("name", "y");
            map2.put("id", "c");
            map2.put("name", "z");
            List<Map<String, Object>> list = new ArrayList<>();
            list.add(map);
            list.add(map1);
            list.add(map2);
            leftTable = JoinUtils.createTable(list, "id");
    
            Map<String, Object> map3 = new HashMap<>();
            Map<String, Object> map4 = new HashMap<>();
            Map<String, Object> map5 = new HashMap<>();
            map3.put("id", "a");
            map3.put("val", "o");
            map4.put("id", "b");
            map4.put("val", "p");
            map5.put("id", "d");
            map5.put("val", "q");
            List<Map<String, Object>> list2 = new ArrayList<>();
            list2.add(map3);
            list2.add(map4);
            list2.add(map5);
            rightTable = JoinUtils.createTable(list2, "id");
            System.out.println("LeftTable");
            JoinUtils.printTable(leftTable);
            System.out.println("RightTable");
            JoinUtils.printTable(rightTable);
        }
    
    
        @Test
        void testCreateTable2() {
            User tom = new User(1, "tom", false);
            User john = new User(2, "john", true);
            User pet = new User(3, "pet", true);
            ArrayList<User> users = new ArrayList<>();
            users.add(tom);
            users.add(john);
            users.add(pet);
            Table<Integer, String, Object> userTable = JoinUtils.createTable(users, User.class, "name");
            JoinUtils.printTable(userTable);
        }
    
        @Test
        void testStrategy() {
            JoinStrategy[] values = JoinStrategy.values();
            for (JoinStrategy strategy : values) {
                System.out.println(strategy);
                JoinUtils.printTable(JoinUtils.join(leftTable, rightTable, strategy));
            }
        }
    } 

    输出:

    LeftTable
            name    id      
    a       x       a       
    b       y       b       
    c       z       c       
    
    RightTable
            val     id      
    a       o       a       
    b       p       b       
    d       q       d       
    
    INNER_JOIN
            name    val     id      
    a       x       o       a       
    b       y       p       b       
    
    LEFT_JOIN
            name    val     id      
    a       x       o       a       
    b       y       p       b       
    c       z       null    c       
    
    RIGHT_JOIN
            val     name    id      
    a       o       x       a       
    b       p       y       b       
    d       q       null    d       
    
    LEFT_SEMI_JOIN
            name    id      
    a       x       a       
    b       y       b       
    
    LEFT_ANTI_JOIN
            name    id      
    c       z       c       
    
    FULL_JOIN
            name    val     id      
    a       x       o       a       
    b       y       p       b       
    c       z       null    c       
    d       null    q       d       

    具体join操作的实现代码,功能肯定是没问题的,性能还没有完全测试。

    如果你有更靠谱的实现,请留言告诉我。

    /**
         * 未指定连接key的内连接,以rowKey作为连接键
         *
         * @param left
         * @param right
         * @return
         */
        private static <R, C, V> Table<R, C, V> innerJoin(Table<R, C, V> left, Table<R, C, V> right) {
            Table<R, C, V> result = HashBasedTable.create();
            Set<R> leftRowKey = Sets.newHashSet(left.rowKeySet());
            Set<R> rightRowKey = Sets.newHashSet(right.rowKeySet());
            leftRowKey.retainAll(rightRowKey);
            for (R key : leftRowKey) {
                Map<C, V> leftRow = left.row(key);
                Map<C, V> rightRow = right.row(key);
                for (Map.Entry<C, V> leftEntry : leftRow.entrySet()) {
                    result.put(key, leftEntry.getKey(), leftEntry.getValue());
                }
                for (Map.Entry<C, V> rightEntry : rightRow.entrySet()) {
                    result.put(key, rightEntry.getKey(), rightEntry.getValue());
                }
            }
            return result;
        }
    
    
        private static <R, C, V> Table<R, C, V> leftJoin(Table<R, C, V> left, Table<R, C, V> right) {
            Table<R, C, V> result = HashBasedTable.create();
            Set<R> leftRowKey = Sets.newHashSet(left.rowKeySet());
            for (R key : leftRowKey) {
                Map<C, V> leftRow = left.row(key);
                Map<C, V> rightRow = right.row(key);
                for (Map.Entry<C, V> leftEntry : leftRow.entrySet()) {
                    result.put(key, leftEntry.getKey(), leftEntry.getValue());
                }
                for (Map.Entry<C, V> rightEntry : rightRow.entrySet()) {
                    result.put(key, rightEntry.getKey(), rightEntry.getValue());
                }
            }
            return result;
    
        }
    
    
        private static <R, C, V> Table<R, C, V> rightJoin(Table<R, C, V> left, Table<R, C, V> right) {
            return leftJoin(right, left);
    
        }
    
    
        private static <R, C, V> Table<R, C, V> leftSemiJoin(Table<R, C, V> left, Table<R, C, V> right) {
            Table<R, C, V> result = HashBasedTable.create();
            Set<R> leftRowKey = Sets.newHashSet(left.rowKeySet());
            Set<R> rightRowKey = Sets.newHashSet(right.rowKeySet());
            leftRowKey.retainAll(rightRowKey);
            for (R key : leftRowKey) {
                Map<C, V> leftRow = left.row(key);
                for (Map.Entry<C, V> leftEntry : leftRow.entrySet()) {
                    result.put(key, leftEntry.getKey(), leftEntry.getValue());
                }
            }
            return result;
        }
    
    
        private static <R, C, V> Table<R, C, V> leftAntiJoin(Table<R, C, V> left, Table<R, C, V> right) {
            Table<R, C, V> result = HashBasedTable.create();
            Set<R> leftRowKey = Sets.newHashSet(left.rowKeySet());
            Set<R> rightRowKey = Sets.newHashSet(right.rowKeySet());
            for (R key : leftRowKey) {
                if (!rightRowKey.contains(key)) {
                    Map<C, V> leftRow = left.row(key);
                    for (Map.Entry<C, V> leftEntry : leftRow.entrySet()) {
                        result.put(key, leftEntry.getKey(), leftEntry.getValue());
                    }
                }
            }
            return result;
        }
    
    
        private static <R, C, V> Table<R, C, V> fullJoin(Table<R, C, V> left, Table<R, C, V> right) {
            Table<R, C, V> result = HashBasedTable.create();
            Set<R> leftRowKey = Sets.newHashSet(left.rowKeySet());
            Set<R> rightRowKey = Sets.newHashSet(right.rowKeySet());
            Set<R> union = new HashSet<>(leftRowKey);
            union.addAll(rightRowKey);
            for (R key : union) {
                Map<C, V> leftRow = left.row(key);
                Map<C, V> rightRow = right.row(key);
                for (Map.Entry<C, V> leftEntry : leftRow.entrySet()) {
                    result.put(key, leftEntry.getKey(), leftEntry.getValue());
                }
                for (Map.Entry<C, V> rightEntry : rightRow.entrySet()) {
                    result.put(key, rightEntry.getKey(), rightEntry.getValue());
                }
            }
            return result;
    
        }
  • 相关阅读:
    14-ESP8266 SDK开发基础入门篇--上位机串口控制 Wi-Fi输出PWM的占空比,调节LED亮度,8266程序编写
    Zookeeper之Curator(1)客户端基本的创建,删除,更新,查找操作api
    【1】基于quartz框架和Zookeeper实现集群化定时任务系统
    Spring源码学习之:ClassLoader学习(5)-自测
    java多线程:线程体往外抛出异常的处理机制实践
    MySQL详解--锁,事务
    ubuntu下访问支付宝官网,安装安全控件
    spring源码学习之:springAOP实现底层原理
    spring源码学习之:项目公共配置项解决方案
    java多线程:synchronized和lock比较浅析
  • 原文地址:https://www.cnblogs.com/wangbin2188/p/15875731.html
Copyright © 2020-2023  润新知