公交司机解决PSQLException

来源:互联网 发布:全局近似算法 编辑:程序博客网 时间:2024/04/29 18:16

最近遇到了一个bug,是关于JPA语句的问题,其实代码很简单,就是使用NamedQuery数据库中符合条件的数据进行更新操作,代码如下(其实不想贴代码) 

@NamedQuery(name = "customer.updateSentToBusinessCheckByCustomerIds", query = "update Customer as c set c.sentToBusinessCheck = true where c.id in :customerIds")})

很简单就是in 后面传递一个list的参数,根据这个list当中的数值进行条件更新,因为数据量小的时候比如几百、几千还是没有问题的,但是一旦list中的数据变大之后,准确的说是超过三万多就不行了(你知道为什么是三万多吗,或者这么问你知道为什么数值是32767吗),生产上我们这里的list是五万多记录,于是会报如下错误(其实真的不想贴代码):

Internal Exception: org.postgresql.util.PSQLException: An I/O error occured while sending to the backend.Error Code: 0Call: UPDATE CUSTOMER SET SENTTOBUSINESSCHECK = ?, ENTITYVERSION = (ENTITYVERSION + ?) WHERE (ID IN (?,?,?,?,?,?,?,?,?,?......)bind => [60102 parameters bound]

最快速的解决问题的办法——Google(你知道为啥不用百度吗?),从网上找到一篇文章(轻击这里)重点引用如下。(请注意,具体的解决办法不重要,重要的是作者解决问题的思路和行动意识。) 

Not a very concrete message, right? When I first saw this exception I waskind of baffled and disaffected. But after narrowing down the problem in adebug session and looking at the PostgreSQL JDBCdriver’s source code the cause was obvious: the PostgreSQLclient/backend protocol dictates that the number of parameters be send from theclient to the Postgres backend as a 2 byte integer (aaah, now the above message actually makes sense). You’llfind details of the protocol here if you’re brave (my 2-byte friend is definedin the Parse message). 

这下明白了,出现异常的原始是因为JPA实现的时候允许的最大的参数数目是有上限的(废话~)。文章最后也说了解决办法,一旦当参数超过上限这个时候就应该把参数分别传递,在这里就是将list分别传递到JPA语句中进行更新操作。最终代码如下(真的,真的不想贴代码) 

    ......        emMain.createNamedQuery("customer.updateSentToBusinessCheckByCustomerIds").setParameter("customerIds",customerIds);        int size=customerIds.size();        if(size>30000){            List<Long> customerIdsSplit=new ArrayList<Long>();            for(int i=0;i<size;i++){                customerIdsSplit.add(customerIds.get(i));                if(customerIdsSplit.size()==30000){                    updateList(customerIdsSplit);                    customerIdsSplit.clear();                }            }            updateList(customerIdsSplit);        }else{            updateList(customerIds);        }    ......        private void updateList(List<Long> customerIdsSplit) {        Query query = emMain.createNamedQuery("customer.updateSentToBusinessCheckByCustomerIds")                     .setParameter("customerIds",customerIdsSplit);        query.executeUpdate();    }

关于解决问题的东西到此结束了,但是以上都不是本文的重点,有几点感触比较深刻的。

1. 篇博客自己很喜欢,代码很少重在思路。

2. 看源码,是90%问题的解决之道,前提是时间允许。

3. 一切有因有果(这里指那三万多这个数字是有他的来头的)。

后记

都说程序员,或者软件行业是是需要高智商的,我不这么认为,论据如下:解决完这个问题之后想起来每天都能听到公交车司机咆哮的那句话,“上不去的乘客等下一辆吧!后面跟着一辆呢!!后面的那辆马上就到了!!!”这不就是人太多坐不下然后就需要分批运送么,和上面的问题解决思路是一样的啊!如此可见公交车司机和程序员(或者说工程师)的职位对智商要求是一样的。最后的最后,附图一张聊表心意。

(在车上拍的,当时算了一下,不禁心中一惊)