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;

    }

 

posted @ 2022-02-09 16:50  Mars.wang  阅读(811)  评论(0编辑  收藏  举报