最近遇到了一个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: 0 Call: 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. 一切有因有果(这里指那三万多这个数字是有他的来头的)。
后记
都说程序员,或者软件行业是是需要高智商的,我不这么认为,论据如下:解决完这个问题之后想起来每天都能听到公交车司机咆哮的那句话,“上不去的乘客等下一辆吧!后面跟着一辆呢!!后面的那辆马上就到了!!!”这不就是人太多坐不下然后就需要分批运送么,和上面的问题解决思路是一样的啊!如此可见公交车司机和程序员(或者说工程师)的职位对智商要求是一样的。最后的最后,附图一张聊表心意。
(在车上拍的,当时算了一下,不禁心中一惊)