- 浏览: 70950 次
- 性别:
- 来自: 武汉
最新评论
-
spring_springmvc:
spring mvc demo教程源代码下载,地址:http: ...
springmvc -
mrhuangok:
文章条理清晰,帮了我大忙。
springmvc -
kunsyliu:
...
get加密 -
JeffreyJia:
为什么使用JMS 就必须使用 MDB呢?没有必要吧?
基于Spring打造简单高效通用的异步任务处理系统
费了比较多的精力终于解决了这个疑难问题,在百度上查阅了大量博客,论坛,一直没有放弃。通过自己的反复试验,像福尔摩斯抽丝剥茧一样终于找到问题的原因,确实很有必要记述下来,下面将解决该问题的来龙去脉细细道来。
我们的网管平台的作业计划采集总是在运行了一段时间之后出现了java.sql.SQLException: 关闭的连接问题。异常堆栈如下:
- java.sql.SQLException: 关闭的连接
- at oracle.jdbc.driver.SQLStateMapping.newSQLException(SQLStateMapping.java:70 )
- at oracle.jdbc.driver.DatabaseError.newSQLException(DatabaseError.java:110 )
- at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:171 )
- at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:227 )
- at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:439 )
- at oracle.sql.CLOB.getDBAccess(CLOB.java:1083 )
- at oracle.sql.CLOB.getAsciiStream(CLOB.java:228 )
- at org.hibernate.lob.SerializableClob.getAsciiStream(SerializableClob.java:45 )
- at com.wri.hy.itmanagerv2.dg.dao.TaskParamDao.queryAllTaskParam(TaskParamDao.java:99 )
- at com.wri.hy.itmanagerv2.dg.autotask.dataservce.TaskParamService.queryAllTaskParam(TaskParamService.java:33 )
- at com.wri.hy.itmanagerv2.dg.autotask.sched.SchedController.init(SchedController.java:96 )
- at com.wri.hy.itmanagerv2.dg.autotask.sched.SchedReadDB.run(SchedReadDB.java:59 )
- at java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
- at java.util.concurrent.FutureTask$Sync.innerRunAndReset(Unknown Source)
- at java.util.concurrent.FutureTask.runAndReset(Unknown Source)
- at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$101 (Unknown Source)
- at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.runPeriodic(Unknown Source)
- at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(Unknown Source)
- at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(Unknown Source)
- at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
- at java.lang.Thread.run(Unknown Source)
查网上说“关闭的连接”是因为连接池中已经没有打开的Connection了,这个连接池是以前的同事仿造bonecp写的,应该不会有啥问题啊,我在连 接池代码里加了很多打印日志,还是没看出有什么问题来,过了很久我就把思路放在分析其它做数据库保存或者修改操作的代码,看是否在保存过程中的问题引发了 连接被关闭。发现在做采集数据保存时候出现了如下异常。
- org.springframework.dao.DataAccessResourceFailureException: Hibernate operation: Could not execute JDBC batch update; SQL [insert into ITMANAGERV2_PLANRESULTINFO (TASK_ID, RESULT, getTime, EXCEPTION_FLAG, EXCEPTION_LINE, ID) values (?, ?, ?, ?, ?, ?)]; Io 异 常: Software caused connection abort: socket write error; nested exception is java.sql.BatchUpdateException: Io 异 常: Software caused connection abort: socket write error
- Caused by: java.sql.BatchUpdateException: Io 异常: Software caused connection abort: socket write error
- at oracle.jdbc.driver.DatabaseError.throwBatchUpdateException(DatabaseError.java:602 )
- at oracle.jdbc.driver.OraclePreparedStatement.executeBatch(OraclePreparedStatement.java:9350 )
- at oracle.jdbc.driver.OracleStatementWrapper.executeBatch(OracleStatementWrapper.java:210 )
- at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:58 )
- at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:195 )
- at org.hibernate.jdbc.AbstractBatcher.prepareStatement(AbstractBatcher.java:91 )
- at org.hibernate.jdbc.AbstractBatcher.prepareStatement(AbstractBatcher.java:86 )
- at org.hibernate.jdbc.AbstractBatcher.prepareBatchStatement(AbstractBatcher.java:171 )
- at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:2048 )
- at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:2427 )
- at org.hibernate.action.EntityInsertAction.execute(EntityInsertAction.java:51 )
- at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:248 )
- at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:232 )
- at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:139 )
- at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:297 )
- at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:27 )
- at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:985 )
- at org.springframework.orm.hibernate3.HibernateAccessor.flushIfNecessary(HibernateAccessor.java:394 )
- at org.springframework.orm.hibernate3.HibernateTemplate.execute(HibernateTemplate.java:367 )
- at org.springframework.orm.hibernate3.HibernateTemplate.saveOrUpdateAll(HibernateTemplate.java:688 )
- at com.wri.hy.itmanagerv2.dg.dao.ResultLogDao.saveCollections(ResultLogDao.java:13 )
- at com.wri.hy.itmanagerv2.dg.autotask.sched.ResultLogThread.run(ResultLogThread.java:157 )
- at java.lang.Thread.run(Unknown Source)
- at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(Unknown Source)
- at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
- at java.lang.Thread.run(Unknown Source)
oh my god,一句简单的hibernateTemplate的saveOrUpdateAll居然会引起这个异常,在百度,谷歌上根据这个异常找来找去也找不 到一个说得有用的。只好老老实实开始分析存储代码,存储的POJO是这样定义的。
- public class PlanResultInfo {
- private Long id; //id
- private PlanTaskInfo taskInfo; //所属作业计划任务
- private Clob result; //执行结果
- private String getTime; //获取时间
- private String exception_flag; //异常标识
- private String exception_line; //异常行号
- /**
- * 存储执行的结果
- */
- private transient String strResult;
- public String getException_flag() {
- return exception_flag;
- }
- public void setException_flag(String exception_flag) {
- this .exception_flag = exception_flag;
- }
- public String getException_line() {
- return exception_line;
- }
- public void setException_line(String exception_line) {
- this .exception_line = exception_line;
- }
- public String getGetTime() {
- return getTime;
- }
- public void setGetTime(String getTime) {
- this .getTime = getTime;
- }
- public Long getId() {
- return id;
- }
- public void setId(Long id) {
- this .id = id;
- }
- public PlanTaskInfo getTaskInfo() {
- return taskInfo;
- }
- public void setTaskInfo(PlanTaskInfo taskInfo) {
- this .taskInfo = taskInfo;
- }
- public Clob getResult() {
- return result;
- }
- public void setResult(Clob result) {
- this .result = result;
- }
- public String getStrResult() {
- return strResult;
- }
- public void setStrResult(String strResult) {
- this .strResult = strResult;
- }
- }
其中result是个clob类型的字段,在hibernate的配置文件中result字段是这样定义的。
- <property name= "result" type= "java.sql.Clob" update= "true" insert= "true" >
- <column name="RESULT" ></column>
- </property>
这样写感觉没问题啊,怎么会Software caused connection abort: socket write
error,后来发现保存进clob的结果有时候String长度非常大,length都有几十万,算起来有100多K,这种情况下就会出现异常,但中小
量的数据是能够正常插入的。但是clob不是号称能保存4G的数据吗。我决定死马当活马医了,参照网上别的clob保存方式来重构代码。下面是第一版重构
代码:
- Session session = null ;
- Transaction tx = null ;
- java.io.Writer writer = null ;
- Reader reader = null ;
- try {
- session = this .getSessionFactory().openSession();
- planResultInfo.setResult(Hibernate.createClob(" " ));
- tx = session.beginTransaction();
- // 保存维护作业计划结果
- session.save(planResultInfo);
- session.flush();
- // 刷新
- session.refresh(planResultInfo, LockMode.UPGRADE);
- tx.commit();
- LOGGER.infoT("END TASK " + planResultInfo.getTaskInfo().getId());} catch (Exception e) {
- LOGGER.exception(e);
- // 回滚事务
- session.getTransaction().rollback();
- } finally {
- if (session != null ) {
- if (session.isOpen()) {
- // 关闭session
- session.close();
- }
- }
- }
很可惜,保存大的clob又出现了新鲜异常:
- org.hibernate.exception.GenericJDBCException: Could not execute JDBC batch update
- at org.hibernate.exception.SQLStateConverter.handledNonSpecificException(SQLStateConverter.java:103 )
- at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:91 )
- at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:43 )
- at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:202 )
- at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:235 )
- at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:139 )
- at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:297 )
- at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:27 )
- at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:985 )
- at com.wri.hy.itmanagerv2.dg.dao.ResultLogDao.saveCollection(ResultLogDao.java:50 )
- at com.wri.hy.itmanagerv2.dg.autotask.sched.ResultLogThread.run(ResultLogThread.java:159 )
- at java.lang.Thread.run(Unknown Source)
- at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(Unknown Source)
- at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
- at java.lang.Thread.run(Unknown Source)
- Caused by: java.sql.BatchUpdateException: 无法从套接字读取更多的数据
- at oracle.jdbc.driver.DatabaseError.throwBatchUpdateException(DatabaseError.java:345 )
- at oracle.jdbc.driver.OraclePreparedStatement.executeBatch(OraclePreparedStatement.java:10844 )
- at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:58 )
- at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:195 )
- ... 11 more
在网上查有关这个异常的问题也无所获,后来又是换ojdbc14.jar,也同样没作用。然后在网上看到有人在博客中写了一种写流的办法存clob,就出了重构第二版:
- Session session = null ;
- Transaction tx = null ;
- java.io.Writer writer = null ;
- try {
- session = this .getSessionFactory().openSession();
- planResultInfo.setResult(Hibernate.createClob(" " ));
- tx = session.beginTransaction();
- // 保存维护作业计划结果
- session.save(planResultInfo);
- session.flush();
- // 刷新
- session.refresh(planResultInfo, LockMode.UPGRADE);
- org.hibernate.lob.SerializableClob cb = (org.hibernate.lob.SerializableClob) planResultInfo
- .getResult();
- java.sql.Clob wrapClob = (java.sql.Clob) cb.getWrappedClob();
- if (wrapClob instanceof oracle.sql.CLOB) {
- oracle.sql.CLOB clob = (oracle.sql.CLOB) wrapClob;
- writer = new BufferedWriter(clob.getCharacterOutputStream());
- String contentStr = planResultInfo.getStrResult();
- LOGGER.infoT("result size : " + contentStr.length());
- writer.write(contentStr);
- writer.flush();
- }
- // 保存维护作业计划日志
- session.save(planLogInfo);
- tx.commit();
- LOGGER.infoT("END TASK " + planResultInfo.getTaskInfo().getId());
- } catch (Exception e) {
- LOGGER.exception(e);
- // 回滚事务
- session.getTransaction().rollback();
- } finally {
- try {
- if ( null != writer)
- writer.close();
- } catch (IOException ioe) {
- LOGGER.exception(ioe);
- }
- if (session != null ) {
- if (session.isOpen()) {
- // 关闭session
- session.close();
- }
- }
- }
这个可是人家博客上写的言之凿凿的啊,可是保存还是出现异常(这个和起初的保存出的异常一模一样)。
- java.io.IOException: Io 异常: Software caused connection abort: socket write error
- at oracle.jdbc.driver.DatabaseError.SQLToIOException(DatabaseError.java:519 )
- at oracle.jdbc.driver.OracleClobWriter.write(OracleClobWriter.java:122 )
- at java.io.Writer.write(Unknown Source)
- at java.io.Writer.write(Unknown Source)
- at com.wri.hy.itmanagerv2.dg.dao.ResultLogDao.saveCollection(ResultLogDao.java:64 )
- at com.wri.hy.itmanagerv2.dg.autotask.sched.ResultLogThread.run(ResultLogThread.java:159 )
- at java.lang.Thread.run(Unknown Source)
- at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(Unknown Source)
- at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
- at java.lang.Thread.run(Unknown Source)
这里很明显看到是java.io.Writer.write出现的异常,可能以前的博客作者没有保存大数据量的clob数据,这样会造成连接中断,最后一次重构终于成功,代码如下:
- Session session = null ;
- Transaction tx = null ;
- java.io.Writer writer = null ;
- Reader reader = null ;
- try {
- session = this .getSessionFactory().openSession();
- planResultInfo.setResult(Hibernate.createClob(" " ));
- tx = session.beginTransaction();
- // 保存维护作业计划结果
- session.save(planResultInfo);
- session.flush();
- // 刷新
- session.refresh(planResultInfo, LockMode.UPGRADE);
- org.hibernate.lob.SerializableClob cb = (org.hibernate.lob.SerializableClob) planResultInfo
- .getResult();
- java.sql.Clob wrapClob = (java.sql.Clob) cb.getWrappedClob();
- if (wrapClob instanceof oracle.sql.CLOB) {
- oracle.sql.CLOB clob = (oracle.sql.CLOB) wrapClob;
- writer = new BufferedWriter(clob.getCharacterOutputStream());
- String contentStr = planResultInfo.getStrResult();
- LOGGER.infoT("result size : " + contentStr.length());
- // // 截取前200000个字符,不然会出现异常
- // if (contentStr.length() > 200000) {
- // contentStr = contentStr.substring(0, 200000);
- // }
- reader = new BufferedReader( new StringReader(contentStr));
- char [] buffer = new char [ 1024 ];
- int length;
- while ((length = reader.read(buffer)) > 0 ) {
- writer.write(buffer, 0 , length);
- writer.write(contentStr);
- writer.flush();
- }
- // writer.write(contentStr);
- // OutputStream outputStream = clob.getAsciiOutputStream();
- //
- // InputStream inputStream = new BufferedInputStream(
- // new ByteArrayInputStream(contentStr.getBytes()));
- // byte[] buff = new byte[10240];
- //
- // int len;
- //
- // while ((len = inputStream.read(buff)) > 0) {
- // outputStream.write(buff, 0, len);
- // outputStream.flush();
- // }
- // inputStream.close();
- // outputStream.close();
- }
- // 保存维护作业计划日志
- session.save(planLogInfo);
- tx.commit();
- LOGGER.infoT("END TASK " + planResultInfo.getTaskInfo().getId());
- } catch (Exception e) {
- LOGGER.exception(e);
- // 回滚事务
- session.getTransaction().rollback();
- } finally {
- try {
- if ( null != reader)
- reader.close();
- if ( null != writer)
- writer.close();
- } catch (IOException ioe) {
- LOGGER.exception(ioe);
- }
- if (session != null ) {
- if (session.isOpen()) {
- // 关闭session
- session.close();
- }
- }
- }
这里要重点说明,对于大数据量的clob写入,必须用缓冲流循环写入字符数组,虽然执行时间长的,但可以执行成功,不会出现异常,
总结:对于hibernate clob保存,如果clob中的数据量较小,普通saveorUpdate即可保存成功,但对于大数据量会导致连接断开,从而导致耗尽连接池中的连接,改 用流来写入,也同样不能直接写入全部String,也会引起connection abort,此时需要按缓冲流读写,降低连接过程中的io效率,这样就能保证插入大数据量的clob信息。
这就是从解决java.sql.SQLException: 关闭的连接最终解决大数据量clob保存的流的保存方式的一个解决方案。希望能给其它解决clob保存的同仁一些参考。
发表评论
-
jcl与jul、log4j1、log4j2、logback的集成原理
2017-12-01 15:59 534jcl与jul、log4j1、log4j2、logbac ... -
slf4j与jul、log4j1、log4j2、logback的集成原理
2017-12-01 15:52 427收藏 jd ... -
预编译分析
2017-11-29 10:26 654一.背景: 用Mybatis+my ... -
预编译
2017-11-29 09:57 710PreparedStatement 在说PreparedS ... -
Spring Boot应用的后台运行配置
2017-11-21 14:26 540Spring Boot应用的后台运行配置 酱油一篇,整 ... -
编码
2017-11-21 14:25 523几种常见的编码格式 为什么要编码 不知道大家有没有想过一 ... -
Spring Boot应用的后台运行配置
2017-11-29 09:58 737Spring Boot应用的后台运行配置 酱油一篇, ... -
spring boot 注解
2017-11-01 10:58 347@EnableAutoConfiguration和@Spr ... -
支付话题
2015-09-09 11:45 1450本文档适用人员:交易领域的产品研发人员 提纲: 银 ... -
深入分析 Java 中的中文编码问题
2014-12-19 10:21 602在 IBM Bluemix 云平台上开发并部署您的下一个应 ... -
hashmap死循环
2014-11-24 10:45 755疫苗:Java HashMap的死循环 在淘宝内 ... -
jquery
2014-02-07 09:03 765当你准备使用jQuery,我强烈建议你遵循下面这些指南: ... -
减轻页面压力
2014-01-26 09:04 611网站快速加载,是提供 ... -
linux 命令
2014-01-23 09:20 402一.linux快捷键 Ctrl+C : 终止当前命令 C ... -
mina
2013-10-15 12:49 1362<!--StartFragment --> ... -
spring 线程池
2013-10-12 14:35 684Spring 线程池使用 Spring ... -
性能监控
2013-07-08 10:34 793spring,真是一个好东西;性能,真是个让人头疼又不 ... -
java ftp
2013-06-24 11:05 880在项目中使用到FTP功能,于是采用类似Spring的各种 ... -
json
2013-06-21 14:01 721JSON小结【json-lib】 j ... -
基于Spring打造简单高效通用的异步任务处理系统
2013-05-21 14:24 3928背景 随着应用系统功 ...
相关推荐
Hibernate存储Clob字段的方式总结涉及了在Java开发中使用Hibernate操作大型文本字段Clob的操作方法。本文主要介绍了两种操作Clob字段的方法,一种是将Clob字段直接映射为String类型,另一种是使用Clob类型进行处理。...
以上就是关于在Hibernate中保存Blob和Clob对象的基本操作。在实际项目中,还需要考虑性能优化、错误处理等问题,例如使用流式处理减少内存占用,以及适当地配置Hibernate的缓存策略等。了解并熟练掌握这些技巧,能...
在保存实体时,需要将String类型的会议内容转换为Clob对象,再设置给实体的summaryClob属性。 4.2.2 更新(Update): 更新时,同样需要将更新后的String内容转换为Clob对象。 4.2.3 读取(Read): 读取时,Hibernate...
Hibernate也支持直接使用Clob对象进行映射,这意味着在实体类中直接使用Clob类型的字段,然后在Hibernate的映射文件中进行相应的配置。这种方式适合处理大数据量的文本,因为它允许直接与数据库的Clob类型进行交互...
接下来介绍如何通过 Hibernate 将 CLOB 数据保存到 Oracle 数据库中。 **3.1 创建会话工厂** 首先,创建 Hibernate 的 `SessionFactory` 对象,这是与数据库交互的基础。 **3.2 创建事务** 在进行数据库操作之前...
在Java的持久化框架Hibernate中,BLOB和CLOB是用来处理大数据对象(Binary Large Object和Character Large Object)的。这两个类型常用于存储图像、视频、大文本等数据,因为它们可以容纳超过数据库标准列大小限制的...
例如,当你调用`session.saveOrUpdate(entity)`或`entityManager.persist(entity)`时,Hibernate会将Clob和Blob的内容正确地保存到数据库中。 当从数据库中检索这些字段时,同样可以通过Hibernate的API获取。例如,...
- 插入数据:在保存实体时,Hibernate会自动处理Clob和Blob对象的序列化。可以通过`getClob()`和`setClob()`方法设置和获取Clob值,对于Blob,使用`getBlob()`和`setBlob()`。 - 更新数据:同样,更新实体时,...
在保存数据时,Blob可以接收FileInputStream或byte[]作为参数,Clob则接受String: ```java // 对于Blob byte[] imageData = ...; // 从文件或网络获取 session.saveOrUpdate(entity); entity.setBinaryData(new ...
在Hibernate,一个流行的Java对象关系映射(ORM)框架中,Blob和Clob也被广泛使用,特别是在处理大型图片、文件或长文本时。 在Hibernate和Microsoft SQL Server的环境中,Blob常用来存储图像、音频、视频等二进制...
通常,要解决超过4000字节的数据,一种做法是将数据写入文件,xml或plain file都可以,数据表中保存文件的路径即可。这种做法不必处理clob(Character Large Object), blob(Binary Large Object)等格式的字段类型,但...
无论是Hibernate还是JDBC,获取到Blob的输入流后,你可能需要将其保存到本地文件或者进行其他处理。这涉及到文件流的读写操作: ```java FileOutputStream outputStream = new FileOutputStream("outputFilePath");...
本文将深入探讨如何模仿Hibernate的功能,动态生成SQL来保存对象,以及与之相关的技术如注解(Annotation)和数据库字段映射。 首先,让我们了解一下Hibernate的核心功能:对象持久化。Hibernate允许开发者将Java...
一个完整的工程,主要功能为:spring+...包括:数据脚本、典型的SSH框架处理,以及spring、hibernate数据批量保存等功能源码、所用到的lib包 数据环境为:oracle 10G 开发:Mycelipse5.1 Spring1.2 Hibernate3.0;
7. **CLOB(字符大对象)**:用于存储大量文本数据,如XML文档,对应的Java类型是`java.sql.Clob`。 8. **自增主键**:例如MySQL的AUTO_INCREMENT,Hibernate通常通过`@GeneratedValue`和`@Id`注解配合`...
- **数据保存**:使用`save()`方法将对象持久化到数据库。 - **数据删除**:使用`delete()`方法删除数据库中的记录。 - **数据修改**:通过`update()`方法更新对象的状态,同步到数据库。 - **数据加载**:`get...
1. **保存/更新大字段**:在保存或更新实体时,使用SessionFactory的`save()`或`update()`方法,Hibernate会自动处理大字段的存储。 ```java Session session = sessionFactory.openSession(); Transaction tx = ...