2017-04-11 1 views
3

Verwenden von JdbcTemplate Ich möchte MERGE SQL-Anweisung aufrufen, die einen neuen Datensatz in die Tabelle einfügen oder aktualisieren, wenn Zeile mit bestimmten Schlüssel bereits vorhanden ist. Der Schlüssel ist, dass eine der Spalten vom Typ Oracle BLOB ist. HierJdbcTemplate - Einfügen oder Aktualisieren von Oracle BLOB mit SQL MERGE

ist, was habe ich versucht, bis jetzt:

1.

SQL-Anweisung Versuchen:

String sql = "" 
      + "MERGE INTO file_thumbnails " 
      + "  USING (SELECT ? as file_c_id, ? as thumbnail_type, ? as thumbnail_image FROM DUAL) tmp " 
      + "  ON (file_thumbnails.file_c_id = tmp.file_c_id AND " 
      + "   file_thumbnails.thumbnail_type = tmp.thumbnail_type) " 
      + "  WHEN MATCHED THEN " 
      + "  UPDATE " 
      + "   SET thumbnail_image = tmp.thumbnail_image " 
      + "    ,thumbnail_date = SYSDATE " 
      + "  WHEN NOT MATCHED THEN " 
      + "  INSERT (c_id, file_c_id, thumbnail_type, thumbnail_image, thumbnail_date) " 
      + "  VALUES (cedar_c_id_seq.nextval, tmp.file_c_id, tmp.thumbnail_type, tmp.thumbnail_image, SYSDATE)"; 

Datenbank Aufruf:

List<Object[]> x = fileList.stream().map(file -> { 
    byte[] thumbnail = file.getThumbnail(); 
    SqlLobValue sqlLobValue = new SqlLobValue(new ByteArrayInputStream(thumbnail), thumbnail.length, new DefaultLobHandler()); 
    return new Object[] { file.getFileCId(), file.getType().toString(), sqlLobValue}; 
}).collect(Collectors.toList()); 
jdbcTemplate.batchUpdate(sql, x, new int[] { OracleTypes.NUMBER, OracleTypes.VARCHAR, OracleTypes.BLOB}); 

Ausnahme:

Caused by: org.springframework.jdbc.UncategorizedSQLException: PreparedStatementCallback; uncategorized SQLException for SQL [MERGE INTO file_thumbnails  USING (SELECT ? as file_c_id, ? as thumbnail_type, ? as thumbnail_image FROM DUAL) tmp   ON (file_thumbnails.file_c_id = tmp.file_c_id AND    file_thumbnails.thumbnail_type = tmp.thumbnail_type)  WHEN MATCHED THEN   UPDATE   SET thumbnail_image = tmp.thumbnail_image    ,thumbnail_date = SYSDATE  WHEN NOT MATCHED THEN   INSERT (c_id, file_c_id, thumbnail_type, thumbnail_image, thumbnail_date)   VALUES (cedar_c_id_seq.nextval, tmp.file_c_id, tmp.thumbnail_type, tmp.thumbnail_image, SYSDATE)]; SQL state [72000]; error code [1461]; ORA-01461: can bind a LONG value only for insert into a LONG column 
; nested exception is java.sql.BatchUpdateException: ORA-01461: can bind a LONG value only for insert into a LONG column 

    at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:84) ~[spring-jdbc-4.3.7.RELEASE.jar:4.3.7.RELEASE] 
    at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:81) ~[spring-jdbc-4.3.7.RELEASE.jar:4.3.7.RELEASE] 
    at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:81) ~[spring-jdbc-4.3.7.RELEASE.jar:4.3.7.RELEASE] 
    at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:649) ~[spring-jdbc-4.3.7.RELEASE.jar:4.3.7.RELEASE] 
    at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:662) ~[spring-jdbc-4.3.7.RELEASE.jar:4.3.7.RELEASE] 
    at org.springframework.jdbc.core.JdbcTemplate.batchUpdate(JdbcTemplate.java:950) ~[spring-jdbc-4.3.7.RELEASE.jar:4.3.7.RELEASE] 
    at org.springframework.jdbc.core.BatchUpdateUtils.executeBatchUpdate(BatchUpdateUtils.java:32) ~[spring-jdbc-4.3.7.RELEASE.jar:4.3.7.RELEASE] 
    at org.springframework.jdbc.core.JdbcTemplate.batchUpdate(JdbcTemplate.java:1000) ~[spring-jdbc-4.3.7.RELEASE.jar:4.3.7.RELEASE] 
    at cern.edms.thumbnails.generator.repository.EdmsFileRepository.saveThumbnails(EdmsFileRepository.java:61) ~[classes/:na] 
    at cern.edms.thumbnails.generator.repository.EdmsFileRepository$$FastClassBySpringCGLIB$$e3d79386.invoke(<generated>) ~[classes/:na] 
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204) ~[spring-core-4.3.7.RELEASE.jar:4.3.7.RELEASE] 
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:721) ~[spring-aop-4.3.7.RELEASE.jar:4.3.7.RELEASE] 
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) ~[spring-aop-4.3.7.RELEASE.jar:4.3.7.RELEASE] 
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136) ~[spring-tx-4.3.7.RELEASE.jar:4.3.7.RELEASE] 
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.7.RELEASE.jar:4.3.7.RELEASE] 
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:656) ~[spring-aop-4.3.7.RELEASE.jar:4.3.7.RELEASE] 
    at cern.edms.thumbnails.generator.repository.EdmsFileRepository$$EnhancerBySpringCGLIB$$70f43ba5.saveThumbnails(<generated>) ~[classes/:na] 
    at cern.edms.thumbnails.generator.Application.run(Application.java:58) [classes/:na] 
    at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:776) [spring-boot-1.5.2.RELEASE.jar:1.5.2.RELEASE] 
    ... 6 common frames omitted 
Caused by: java.sql.BatchUpdateException: ORA-01461: can bind a LONG value only for insert into a LONG column 

    at oracle.jdbc.driver.OraclePreparedStatement.executeBatch(OraclePreparedStatement.java:10401) ~[ojdbc6-11.2.0.3.0.jar:11.2.0.3.0] 
    at oracle.jdbc.driver.OracleStatementWrapper.executeBatch(OracleStatementWrapper.java:230) ~[ojdbc6-11.2.0.3.0.jar:11.2.0.3.0] 
    at org.springframework.jdbc.core.JdbcTemplate$4.doInPreparedStatement(JdbcTemplate.java:966) ~[spring-jdbc-4.3.7.RELEASE.jar:4.3.7.RELEASE] 
    at org.springframework.jdbc.core.JdbcTemplate$4.doInPreparedStatement(JdbcTemplate.java:950) ~[spring-jdbc-4.3.7.RELEASE.jar:4.3.7.RELEASE] 
    at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:633) ~[spring-jdbc-4.3.7.RELEASE.jar:4.3.7.RELEASE] 
    ... 21 common frames omitted 

Versuchen 2.

Sql statement: 
     String sql = "" 
       + "MERGE INTO file_thumbnails " 
       + "  USING (SELECT ? as file_c_id, ? as thumbnail_type FROM DUAL) tmp " 
       + "  ON (file_thumbnails.file_c_id = tmp.file_c_id AND " 
       + "   file_thumbnails.thumbnail_type = tmp.thumbnail_type) " 
       + "  WHEN MATCHED THEN " 
       + "  UPDATE " 
       + "   SET thumbnail_image = ? " 
       + "    ,thumbnail_date = SYSDATE " 
       + "  WHEN NOT MATCHED THEN " 
       + "  INSERT (c_id, file_c_id, thumbnail_type, thumbnail_image, thumbnail_date) " 
       + "  VALUES (cedar_c_id_seq.nextval, tmp.file_c_id, tmp.thumbnail_type, ?, SYSDATE)"; 

Datenbank Aufruf:

List<Object[]> x = fileList.stream().map(file -> { 
    byte[] thumbnail = file.getThumbnail(); 
    SqlLobValue sqlLobValue = new SqlLobValue(new ByteArrayInputStream(thumbnail), thumbnail.length, new DefaultLobHandler()); 
    SqlLobValue sqlLobValue2 = new SqlLobValue(new ByteArrayInputStream(thumbnail), thumbnail.length, new DefaultLobHandler()); 
    return new Object[] { file.getFileCId(), file.getType().toString(), sqlLobValue, sqlLobValue2 }; 
}).collect(Collectors.toList()); 
jdbcTemplate.batchUpdate(sql, x, new int[] { OracleTypes.NUMBER, OracleTypes.VARCHAR, OracleTypes.BLOB, OracleTypes.BLOB }); 

Ausnahme:

Caused by: org.springframework.jdbc.UncategorizedSQLException: PreparedStatementCallback; uncategorized SQLException for SQL [MERGE INTO file_thumbnails  USING (SELECT ? as file_c_id, ? as thumbnail_type FROM DUAL) tmp   ON (file_thumbnails.file_c_id = tmp.file_c_id AND    file_thumbnails.thumbnail_type = tmp.thumbnail_type)  WHEN MATCHED THEN   UPDATE   SET thumbnail_image = ?    ,thumbnail_date = SYSDATE  WHEN NOT MATCHED THEN   INSERT (c_id, file_c_id, thumbnail_type, thumbnail_image, thumbnail_date)   VALUES (cedar_c_id_seq.nextval, tmp.file_c_id, tmp.thumbnail_type, ?, SYSDATE)]; SQL state [63000]; error code [3106]; ORA-03106: fatal two-task communication protocol error 
; nested exception is java.sql.BatchUpdateException: ORA-03106: fatal two-task communication protocol error 

    at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:84) ~[spring-jdbc-4.3.7.RELEASE.jar:4.3.7.RELEASE] 
    at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:81) ~[spring-jdbc-4.3.7.RELEASE.jar:4.3.7.RELEASE] 
    at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:81) ~[spring-jdbc-4.3.7.RELEASE.jar:4.3.7.RELEASE] 
    at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:649) ~[spring-jdbc-4.3.7.RELEASE.jar:4.3.7.RELEASE] 
    at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:662) ~[spring-jdbc-4.3.7.RELEASE.jar:4.3.7.RELEASE] 
    at org.springframework.jdbc.core.JdbcTemplate.batchUpdate(JdbcTemplate.java:950) ~[spring-jdbc-4.3.7.RELEASE.jar:4.3.7.RELEASE] 
    at org.springframework.jdbc.core.BatchUpdateUtils.executeBatchUpdate(BatchUpdateUtils.java:32) ~[spring-jdbc-4.3.7.RELEASE.jar:4.3.7.RELEASE] 
    at org.springframework.jdbc.core.JdbcTemplate.batchUpdate(JdbcTemplate.java:1000) ~[spring-jdbc-4.3.7.RELEASE.jar:4.3.7.RELEASE] 
    at cern.edms.thumbnails.generator.repository.EdmsFileRepository.saveThumbnails(EdmsFileRepository.java:62) ~[classes/:na] 
    at cern.edms.thumbnails.generator.repository.EdmsFileRepository$$FastClassBySpringCGLIB$$e3d79386.invoke(<generated>) ~[classes/:na] 
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204) ~[spring-core-4.3.7.RELEASE.jar:4.3.7.RELEASE] 
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:721) ~[spring-aop-4.3.7.RELEASE.jar:4.3.7.RELEASE] 
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) ~[spring-aop-4.3.7.RELEASE.jar:4.3.7.RELEASE] 
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136) ~[spring-tx-4.3.7.RELEASE.jar:4.3.7.RELEASE] 
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.7.RELEASE.jar:4.3.7.RELEASE] 
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:656) ~[spring-aop-4.3.7.RELEASE.jar:4.3.7.RELEASE] 
    at cern.edms.thumbnails.generator.repository.EdmsFileRepository$$EnhancerBySpringCGLIB$$587b6598.saveThumbnails(<generated>) ~[classes/:na] 
    at cern.edms.thumbnails.generator.Application.run(Application.java:58) [classes/:na] 
    at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:776) [spring-boot-1.5.2.RELEASE.jar:1.5.2.RELEASE] 
    ... 6 common frames omitted 
Caused by: java.sql.BatchUpdateException: ORA-03106: fatal two-task communication protocol error 

    at oracle.jdbc.driver.OraclePreparedStatement.executeBatch(OraclePreparedStatement.java:10401) ~[ojdbc6-11.2.0.3.0.jar:11.2.0.3.0] 
    at oracle.jdbc.driver.OracleStatementWrapper.executeBatch(OracleStatementWrapper.java:230) ~[ojdbc6-11.2.0.3.0.jar:11.2.0.3.0] 
    at org.springframework.jdbc.core.JdbcTemplate$4.doInPreparedStatement(JdbcTemplate.java:966) ~[spring-jdbc-4.3.7.RELEASE.jar:4.3.7.RELEASE] 
    at org.springframework.jdbc.core.JdbcTemplate$4.doInPreparedStatement(JdbcTemplate.java:950) ~[spring-jdbc-4.3.7.RELEASE.jar:4.3.7.RELEASE] 
    at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:633) ~[spring-jdbc-4.3.7.RELEASE.jar:4.3.7.RELEASE] 
    ... 21 common frames omitted 

Zusätzliche Hinweise.

  1. Im zweiten Versuch in der Datenbank Anruf, den ich nicht zweimal das gleiche SqlLobValue Objekt verwenden kann, weil ich eine Ausnahme bin immer:

    Verursacht durch: java.sql.SQLException: Doppelte Stream-Parameter: 4

  2. Wenn ich mit dem zweiten Versuch gehen werde, aber den BLOB-Eingabeparameter nur einmal setzen (zum Beispiel nur im INSERT-Teil der MERGE-Anweisung), funktioniert es korrekt. Aber natürlich löst es mein Problem nicht.

Könnten Sie bitte helfen?

Antwort

3

Ich löste das Problem dank @ Gvenzi Antwort, aber beschlossen, meine eigene Antwort zu veröffentlichen, wie ich einige zusätzliche Kommentare habe.

Also, ja, OracleLobHandler löst das Problem. Aber wir sind nicht gezwungen, die veraltete Klasse zu verwenden. Im OracleLobHandler documentation fand ich

Deprecated. in favor of DefaultLobHandler for the Oracle 10g driver and higher. Consider using the 10g/11g driver even against an Oracle 9i database! DefaultLobHandler.setCreateTemporaryLob(boolean) is the direct equivalent of this OracleLobHandler's implementation strategy, just using standard JDBC 4.0 API. That said, in most cases, regular DefaultLobHandler setup will work fine as well.

ich es getestet und es funktioniert.

Aber ich hatte ein anderes Problem SqlLobValue zusammen mit OracleTypes.BLOB in PreparedStatementSetter mit (es ClassCastException: SqlLobValue cannot be cast to oracle.sql.BLOB using PreparedStatementSetter hier beschrieben wird)

wie folgt

Mein letzter Code Arbeits ist:

public void saveThumbnails(List<Thumbnail> fileList) throws SQLException, IOException { 

    BatchPreparedStatementSetter b = new BatchPreparedStatementSetter() { 
     @Override 
     public void setValues(PreparedStatement ps, int i) throws SQLException { 
      Thumbnail thumbnail = fileList.get(i); 
      byte[] thumbnailBytes = thumbnail.getThumbnail(); 
      ps.setObject(1, thumbnail.getFileCId(), OracleTypes.NUMBER); 
      ps.setObject(2, thumbnail.getType().toString(), OracleTypes.VARCHAR); 
      DefaultLobHandler lobHandler = new DefaultLobHandler(); 
      lobHandler.setCreateTemporaryLob(true); 
      lobHandler.getLobCreator().setBlobAsBytes(ps, 3, thumbnailBytes); 
     } 

     @Override 
     public int getBatchSize() { 
      return fileList.size(); 
     } 
    }; 
    jdbcTemplate.batchUpdate(getSaveThumbnailSql(), b); 
} 

private String getSaveThumbnailSql() { 
    // @formatter:off 
    String sql = "" 
      + "MERGE INTO file_thumbnails " 
      + "  USING (SELECT ? as file_c_id, ? as thumbnail_type, ? AS thumbnail_image FROM DUAL) tmp " 
      + "  ON (file_thumbnails.file_c_id = tmp.file_c_id AND " 
      + "   file_thumbnails.thumbnail_type = tmp.thumbnail_type) " 
      + "  WHEN MATCHED THEN " 
      + "  UPDATE " 
      + "   SET thumbnail_image = tmp.thumbnail_image" 
      + "    ,thumbnail_date = SYSDATE " 
      + "  WHEN NOT MATCHED THEN " 
      + "  INSERT (c_id, file_c_id, thumbnail_type, thumbnail_image, thumbnail_date) " 
      + "  VALUES (cedar_c_id_seq.nextval, tmp.file_c_id, tmp.thumbnail_type, tmp.thumbnail_image , SYSDATE)"; 
    //@formatter:on 
    return sql; 
} 
1

Ich bin nicht wirklich ein Spring-Framework-Experte, aber ich könnte Ihr Problem reproduzieren und etwas debuggen. Es hat mit der DefaultLobHandler zu tun, dass Sie eine, die scheinbar als LONG Datentyp statt einer BLOB durch einen Fehler gebunden scheint.Hier

ist ein vereinfachtes Testfall des obigen Aufrufs mit einer Losgröße Eins:

String sql = "MERGE INTO file_thumbnails " 
     + "  USING (SELECT ? as file_c_id, ? as thumbnail_type, ? as thumbnail_image FROM DUAL) tmp " 
     + "  ON (file_thumbnails.file_c_id = tmp.file_c_id AND " 
     + "   file_thumbnails.thumbnail_type = tmp.thumbnail_type) " 
     + "  WHEN MATCHED THEN " 
     + "  UPDATE " 
     + "   SET thumbnail_image = tmp.thumbnail_image " 
     + "    ,thumbnail_date = SYSDATE " 
     + "  WHEN NOT MATCHED THEN " 
     + "  INSERT (file_c_id, thumbnail_type, thumbnail_image, thumbnail_date) " 
     + "  VALUES (tmp.file_c_id, tmp.thumbnail_type, tmp.thumbnail_image, SYSDATE)"; 

byte[] content = Files.readAllBytes(Paths.get("/Users/gvenzl/Downloads/image1.JPG")); 
ByteArrayInputStream bin = new ByteArrayInputStream(content); 
SqlLobValue sqlLobValue = new SqlLobValue(bin, content.length, new DefaultLobHandler()); 
List<Object []> x = new ArrayList<Object []>(); 
x.add(new Object [] { 1, "Test", sqlLobValue}); 

jdbcTemplate.batchUpdate(sql, x, new int[] { OracleTypes.NUMBER, OracleTypes.VARCHAR, OracleTypes.BLOB}); 

System.out.print("Successful!"); 

ich ein Bild liest, dann erstellen Sie ein einzelnes Element Array und auszuführen, dass die gleiche Art und Weise haben Sie und Fehler:

Exception in thread "main" org.springframework.jdbc.UncategorizedSQLException: PreparedStatementCallback; uncategorized SQLException for SQL [MERGE INTO file_thumbnails  USING (SELECT ? as file_c_id, ? as thumbnail_type, ? as thumbnail_image FROM DUAL) tmp   ON (file_thumbnails.file_c_id = tmp.file_c_id AND    file_thumbnails.thumbnail_type = tmp.thumbnail_type)  WHEN MATCHED THEN   UPDATE   SET thumbnail_image = tmp.thumbnail_image    ,thumbnail_date = SYSDATE  WHEN NOT MATCHED THEN   INSERT (file_c_id, thumbnail_type, thumbnail_image, thumbnail_date)   VALUES (tmp.file_c_id, tmp.thumbnail_type, tmp.thumbnail_image, SYSDATE)]; SQL state [72000]; error code [1461]; ORA-01461: can bind a LONG value only for insert into a LONG column 
; nested exception is java.sql.BatchUpdateException: ORA-01461: can bind a LONG value only for insert into a LONG column 

Jetzt bin ich zu ändern, die LOB-Handler von DefaultLobHandler zu der veralteten OracleLobHandler:

byte[] content = Files.readAllBytes(Paths.get("/Users/gvenzl/Downloads/image1.JPG")); 
ByteArrayInputStream bin = new ByteArrayInputStream(content); 
SqlLobValue sqlLobValue = new SqlLobValue(bin, content.length, new OracleLobHandler()); 
List<Object []> x = new ArrayList<Object []>(); 
x.add(new Object [] { 1, "Test", sqlLobValue}); 

jdbcTemplate.batchUpdate(sql, x, new int[] { OracleTypes.NUMBER, OracleTypes.VARCHAR, OracleTypes.BLOB}); 

System.out.print("Successful!"); 

Und mein out ist:

Successful! 

Debugging durch, ist der Unterschied, dass ich sehen konnte, dass die OracleLobHandler die ps.setBlob() Methode verwendet, während die DefaultLobHandlerps.setBinaryStream() verwendet, die in die Variable, als LONG gebunden zu führen scheint, eher als ein BLOB. Hoffe das hilft!

+0

Thank you! Es löst das Problem, aber bitte überprüfen Sie auch meine Antwort mit einigen zusätzlichen Kommentaren. – kpater87

Verwandte Themen