Oracle中存在clob字段时使用union去重报错的处理方法

写在前面

    本文章详细记录了实际问题处理中的分析思路并总结了一些涉及到的知识点,问题很简单,如果只是想要一个解决方案请直接查看问题场景和解决方案,如有问题请留言评论指正,非常感谢!

问题场景

    在最近的工作中遇到这样一种情况:要对某两个SQL子句的结果进行union all处理得到最终的结果集,但是测试后发现出现了重复记录,于是简单修改为使用union处理,通常来说,union关键字将对合并结果进行去重处理,可是我们的项目中这个表存在clob型字段,且该字段恰好在这个SQL中被select出来了,于是出现了如下错误:

    通常来说,只要修改字段类型为varchar2或者sql使用union all关键字即可解决问题,但结合业务实际,这个字段确实可能出现字段超长(varchar2的最大长度为4000)而不能修改为varchar2型数据,又必须处理重复结果,所以只能继续使用union all关键字,并对结果集进行手动去重。 

第一次分析

    最开始我写了一个很简单的去重逻辑,创建一个新的空List集合后遍历原结果集集合,如果新集合中不包含原集合的当前项,就将当前项添加到新集合中,因为List本身的有序性,这样又可以确保新集合的顺序不乱,于是简单写了如下代码并测试:

// JDBC结果集为List<实体类> temp
List<实体类> temp2 = new ArrayList<>();
for (实体类 item : temp) {
    if (!temp2.contains(item)) {
        temp2.add(item);
    }
}

    原本按照预期,temp2中应该不再包含重复结果,但事与愿违,最终结果依然包含两条一模一样的数据,这说明对于Java程序来说这两条结果根本就是两个不同的对象,于是我赶紧打了断点进行了debug,结果和猜想一致,两个对象的地址并不相同

第二次分析

    地址不同肯定不能通过这种方式来去重了,有些同学可能想那我用LinkedHashSet,set有去重特性,LinkedHashSet同时又可以保证有序,其实这也是不行的,下面解释原因:

    经过分析JDBC结果集中的各个结果地址均不相同,此时无论使用各种方式分析,都逃不过两个对象的对比,这里简单以文中提到的两种方式展开说明:

1、List的contains方法

上源码

public boolean contains(Object o) {
    return indexOf(o) >= 0;
}

public int indexOf(Object o) {
    if (o == null) {
        for (int i = 0; i < size; i++)
            if (elementData[i]==null)
                return i;
    } else {
        for (int i = 0; i < size; i++)
            if (o.equals(elementData[i]))
                return i;
    }
    return -1;
}

    很明显我们对比的对象不是null,最后还是要遍历使用equals方法对比两个对象是否相同,关于双等号和equals方法的区别,这里推荐一篇写的更好的博客Java中equals与“==”的区别详解,看到这里我赶紧检查实体类代码,果然,写实体类的同学并没有重写hashCode和equals方法,导致不可以使用这个方法进行去重处理,如果要继续使用这种方法,必须重写hashCode()方法和equals()方法才能继续。

2、LinkedHashSet的去重原理

源码太多,直接上流程图

    先比较hashCode()返回的hash值,如果全都不一致那就是没有重复,对象可以放入Set;如果找到hash值一致的元素,继续调用equals()方法判断,如果相同就是重复了,对象不能放入Set,否则没有重复就继续进入下一个元素的比较。

最终处理方案

    综上所述,要手动对JDBC的结果集去重必须重写实体类的hashCode()方法及equals()方法,之后再选用遍历比较或使用LinkedHashSet去重

    通常来说将实体类的两个方法一起重写是一种好习惯,但是结合我项目的实际情况,实体类不由我开发,只能在调用它的实现类里写一个布尔型私有方法来判断两个对象是否一致了,代码逻辑与重写equals()方法基本一致,虽然也能解决问题,不过还是建议大家对自定义实体类重写好各种常用方法。

知识点总结

1、Oracle数据库查询clob型字段时如果使用了distinct关键字或union关键字进行去重处理将导致报错,需手动进行去重处理;

2、对于JDBC来说,即使两条数据内容上完全一致也是两条结果,对应Java代码的两个对象,即与SQL执行结果完全一致;

3、List的contains方法和Set不重复原理的基础是对象的比较,涉及到equals()原理;

4、需要进行对比引用类型的对象是否相同,必须重写其hashCode()方法和equals()方法,否则仅为简单的地址比较,java.lang.String类型除外(此处涉及常量池);

5、若对结果集有操作,要使用其拷贝出来的副本对象(地址不相同)代替原JDBC结果集对象,因为此时session尚未关闭,结果集对象的变化将同步影响数据库内对应的记录造成数据丢失或脏数据问题;

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值