2010-05-05 3 views
14

Из документации Spring JDBC, я знаю, как insert a blob using JdbcTemplateSpring JdbcTemplate - Вставить блоб и вернуть сгенерированный ключ

final File blobIn = new File("spring2004.jpg"); 
final InputStream blobIs = new FileInputStream(blobIn); 
jdbcTemplate.execute(
    "INSERT INTO lob_table (id, a_blob) VALUES (?, ?)", 
    new AbstractLobCreatingPreparedStatementCallback(lobhandler) {       
     protected void setValues(PreparedStatement ps, LobCreator lobCreator) 
      throws SQLException { 
     ps.setLong(1, 1L); 
     lobCreator.setBlobAsBinaryStream(ps, 2, blobIs, (int)blobIn.length());   
     } 
    } 
); 
blobIs.close(); 

А также как retrieve the generated key of a newly inserted row:

KeyHolder keyHolder = new GeneratedKeyHolder(); 
jdbcTemplate.update(
    new PreparedStatementCreator() { 
     public PreparedStatement createPreparedStatement(Connection connection) throws SQLException { 
      PreparedStatement ps = 
       connection.prepareStatement(INSERT_SQL, new String[] {"id"}); 
      ps.setString(1, name); 
      return ps; 
     } 
    }, 
    keyHolder); 

// keyHolder.getKey() now contains the generated key 

Есть ли способ, которым я мог бы объединить два?

ответ

14

Я пришел сюда в поисках тот же ответ, но не был удовлетворен тем, что было принято. Так что я сделал немного покопаться вокруг и пришел с этим решением, которое я испытывал в Oracle 10g и Spring 3,0

public Long save(final byte[] blob) { 
    KeyHolder keyHolder = new GeneratedKeyHolder(); 
    String sql = "insert into blobtest (myblob) values (?)"; //requires auto increment column based on triggers 
    getSimpleJdbcTemplate().getJdbcOperations().update(new AbstractLobPreparedStatementCreator(lobHandler, sql, "ID") { 
    @Override 
    protected void setValues(PreparedStatement ps, LobCreator lobCreator) throws SQLException, DataAccessException { 
     lobCreator.setBlobAsBytes(ps, 1, blob); 
    } 
    }, keyHolder); 

    Long newId = keyHolder.getKey().longValue(); 
    return newId; 
} 

это также требует следующего абстрактного класса, частично на основе Spring, AbstractLobCreatingPreparedStatementCallback

public abstract class AbstractLobPreparedStatementCreator implements PreparedStatementCreator { 
    private final LobHandler lobHandler; 
    private final String sql; 
    private final String keyColumn; 
    public AbstractLobPreparedStatementCreator(LobHandler lobHandler, String sql, String keyColumn) { 
    this.lobHandler = lobHandler; 
    this.sql = sql; 
    this.keyColumn = keyColumn; 
    } 
    public PreparedStatement createPreparedStatement(Connection con) throws SQLException { 
    PreparedStatement ps = con.prepareStatement(sql, new String[] { keyColumn }); 
    LobCreator lobCreator = this.lobHandler.getLobCreator(); 
    setValues(ps, lobCreator); 
    return ps; 
    } 
    protected abstract void setValues(PreparedStatement ps, LobCreator lobCreator) throws SQLException, DataAccessException; 
} 

Кроме того, таблица, которую вы создаете в Oracle, должна иметь автоинкрементный столбец для идентификатора, используя последовательность и триггер. Триггер необходим, потому что в противном случае вам придется использовать функцию NamedParameterJdbcOperations от Spring (для выполнения последовательности.nextval в вашем SQL), которая, похоже, не поддерживает KeyHolder (которую я использую для получения идентификатора авто-gen). Смотрите этот блог (не мой блог) для получения дополнительной информации: http://www.lifeaftercoffee.com/2006/02/17/how-to-create-auto-increment-columns-in-oracle/

create table blobtest (
id number primary key, 
myblob blob); 

create sequence blobseq start with 1 increment by 1; 

CREATE OR REPLACE TRIGGER blob_trigger 
BEFORE INSERT 
ON blobtest 
REFERENCING NEW AS NEW 
FOR EACH ROW 
BEGIN 
SELECT blobseq.nextval INTO :NEW.ID FROM dual; 
end; 
/
+1

Принимая этот ответ из-за всех upvotes. Вы должны заменить 'getSimpleJdbcTemplate(). GetJdbcOperations()' с 'getJdbcTemplate()', поскольку SimpleJdbcTemplate теперь устарел. – itsadok

2

В итоге я просто выполнил два запроса: один для создания строки и один для обновления blob.

int id = insertRow(); 
updateBlob(id, blob); 

Глядя на исходный код Spring и извлечения необходимых деталей, я пришел с этим:

final KeyHolder generatedKeyHolder = new GeneratedKeyHolder(); 
getJdbcTemplate().execute(
    "INSERT INTO lob_table (blob) VALUES (?)", 
    new PreparedStatementCallback() { 
     public Object doInPreparedStatement(PreparedStatement ps) throws SQLException { 
      LobCreator lobCreator = lobHandler.getLobCreator(); 
      lobCreator.setBlobAsBinaryStream(ps, 2, blobIs, (int)blobIn.length()); 

      int rows = ps.executeUpdate(); 
      List generatedKeys = generatedKeyHolder.getKeyList(); 
      generatedKeys.clear(); 
      ResultSet keys = ps.getGeneratedKeys(); 
      if (keys != null) { 
       try { 
        RowMapper rowMapper = new ColumnMapRowMapper(); 
        RowMapperResultSetExtractor rse = new RowMapperResultSetExtractor(rowMapper, 1); 
        generatedKeys.addAll((List) rse.extractData(keys)); 
       } 
       finally { 
        JdbcUtils.closeResultSet(keys); 
       } 
      } 
      if (logger.isDebugEnabled()) { 
       logger.debug("SQL update affected " + rows + " rows and returned " + generatedKeys.size() + " keys"); 
      } 
      return new Integer(rows); 
     } 
    } 
); 

Я не могу сказать, что я полностью понимаю, что здесь происходит. Я не уверен, что сложный метод для извлечения сгенерированного ключа необходим в этом простом случае, и я не совсем понимаю, что даже использовать JdbcTemplate, когда код получает этот волосатый.

В любом случае, я протестировал вышеуказанный код, и он работает. В моем случае я решил, что это слишком усложнит мой код.

0

Если ваша базовая база данных - это mysql, вы можете автогенерировать свой первичный ключ. Затем вставить запись в вашу БД, вы можете использовать следующий синтаксис для вставки:

INSERT INTO lob_table (a_blob) VALUES (?) 
-1

Может быть, некоторые так:

public class JdbcActorDao implements ActorDao { 
private SimpleJdbcTemplate simpleJdbcTemplate; 
private SimpleJdbcInsert insertActor; 

public void setDataSource(DataSource dataSource) { 
    this.simpleJdbcTemplate = new SimpleJdbcTemplate(dataSource); 
    this.insertActor = 
      new SimpleJdbcInsert(dataSource) 
        .withTableName("t_actor") 
        .usingGeneratedKeyColumns("id"); 
} 

public void add(Actor actor) { 
    Map<String, Object> parameters = new HashMap<String, Object>(2); 
    parameters.put("first_name", actor.getFirstName()); 
    parameters.put("last_name", actor.getLastName()); 
    Number newId = insertActor.executeAndReturnKey(parameters); 
    actor.setId(newId.longValue()); 
} 

// ... additional methods 
} 
+0

Где находится капля? – itsadok

+0

Does actor.getLastName() не может быть блобом? – iMysak

0

В 2012 году SimpleJdbcTemplate является устаревшим. Это то, что я сделал:

KeyHolder keyHolder = new GeneratedKeyHolder(); 

List<SqlParameter> declaredParams = new ArrayList<>(); 

declaredParams.add(new SqlParameter(Types.VARCHAR)); 
declaredParams.add(new SqlParameter(Types.BLOB)); 
declaredParams.add(new SqlParameter(Types.VARCHAR)); 
declaredParams.add(new SqlParameter(Types.INTEGER)); 
declaredParams.add(new SqlParameter(Types.INTEGER)); 

PreparedStatementCreatorFactory pscFactory = 
    new PreparedStatementCreatorFactory(SQL_CREATE_IMAGE, declaredParams); 

pscFactory.setReturnGeneratedKeys(true); 

getJdbcTemplate().update(
    pscFactory.newPreparedStatementCreator(
     new Object[] { 
      image.getName(), 
      image.getBytes(), 
      image.getMimeType(), 
      image.getHeight(), 
      image.getWidth() 
     }), keyHolder); 

image.setId(keyHolder.getKey().intValue()); 

SQL, выглядит следующим образом:

INSERT INTO image (name, image_bytes, mime_type, height, width) VALUES (?, ?, ?, ?, ?) 
1
package com.technicalkeeda.dao; 

import java.io.File; 
import java.io.FileInputStream; 
import java.io.FileNotFoundException; 
import java.io.InputStream; 
import java.sql.Types; 

import javax.sql.DataSource; 

import org.springframework.dao.DataAccessException; 
import org.springframework.jdbc.core.JdbcTemplate; 
import org.springframework.jdbc.core.support.SqlLobValue; 
import org.springframework.jdbc.support.lob.DefaultLobHandler; 
import org.springframework.jdbc.support.lob.LobHandler; 

public class ImageDaoImpl implements ImageDao { 

    private DataSource dataSource; 

    private JdbcTemplate jdbcTemplate; 

    public void setDataSource(DataSource dataSource) { 
     this.dataSource = dataSource; 
     this.jdbcTemplate = new JdbcTemplate(this.dataSource); 
    } 

    @Override 
    public void insertImage() { 
     System.out.println("insertImage" + jdbcTemplate); 

     try { 
      final File image = new File("C:\\puppy.jpg"); 
      final InputStream imageIs = new FileInputStream(image); 

      LobHandler lobHandler = new DefaultLobHandler(); 

      jdbcTemplate.update(
        "INSERT INTO trn_imgs (img_title, img_data) VALUES (?, ?)", 
        new Object[] { 
         "Puppy", 
         new SqlLobValue(imageIs, (int)image.length(), lobHandler), 
        }, 
        new int[] {Types.VARCHAR, Types.BLOB}); 


     } catch (DataAccessException e) { 
      e.printStackTrace(); 
     } catch (FileNotFoundException e) { 
      e.printStackTrace(); 
     } 

    } 
} 
+1

Вы вставили BLOB, но не получили сгенерированный ключ. – itsadok

8

Все это казалось слишком сложным для меня. Это работает и прост.Он использует org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate

import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; 
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; 
import org.springframework.jdbc.core.support.SqlLobValue; 
import org.springframework.jdbc.support.lob.DefaultLobHandler; 


    public void setBlob(Long id, byte[] bytes) { 
     try { 
      jdbcTemplate = new NamedParameterJdbcTemplate(dataSource); 
      MapSqlParameterSource parameters = new MapSqlParameterSource(); 
      parameters.addValue("id", id); 
      parameters.addValue("blob_field", new SqlLobValue(new ByteArrayInputStream(bytes), bytes.length, new DefaultLobHandler()), OracleTypes.BLOB); 
      jdbcTemplate.update("update blob_table set blob_field=:blob_field where id=:id", parameters); 
     } catch(Exception e) { 
      e.printStackTrace(); 
     } 
    } 
+2

Вопрос был о ** вставке ** ** новой ** строки с блобом и получении вновь сгенерированного ключа. Ваш код ** обновляет ** ** существующую ** строку с помощью blob. Могу ли я предположить, что вы хотели просто вставить строку без blob в качестве первого шага, а затем сделать это? – itsadok

0

Это проверяется только на MySql, и я только вставить соответствующую часть. после запуска моего тестового класса, результат показан ниже: "запись добавлена ​​через template.update (PSC, кк): 1 добавлено и получил ключ 36"

final byte[] bytes = "My Binary Content".getBytes(); 
final ByteArrayInputStream bais = new ByteArrayInputStream(bytes);   
PreparedStatementCreator psc = new PreparedStatementCreator() { 
     PreparedStatement ps = null; 
     public PreparedStatement createPreparedStatement(
       Connection connection) throws SQLException { 
      dummy.setStringCode("dummy_jdbc_spring_createPS_withKey_lob"); 
      ps = connection 
        .prepareStatement(
          "INSERT INTO DUMMY (dummy_code, dummy_double, dummy_date, dummy_binary) VALUES (?, ?, ?,?)", 
          Statement.RETURN_GENERATED_KEYS); 
      ps.setString(1, dummy.getStringCode()); 
      ps.setDouble(2, dummy.getDoubleNumber()); 
      ps.setDate(3, dummy.getDate()); 
      new DefaultLobHandler().getLobCreator().setBlobAsBinaryStream(
        ps, 4, bais, bytes.length); 

      return ps; 
     } 
    }; 
KeyHolder holder = new GeneratedKeyHolder(); 
System.out.println("record added via template.update(psc,kh): " 
      + template.update(psc, holder)+" added and got key " + holder.getKey()); 
-1

Пожалуйста, используйте:

addValue("p_file", noDataDmrDTO.getFile_data(), Types.BINARY) 

noDataDmrDTO.getFile_data() is byte array. 


{ 
simpleJdbcCall = 
      new SimpleJdbcCall(jdbcTemplate).withProcedureName("insert_uploaded_files").withCatalogName("wct_mydeq_stg_upld_pkg") 
       .withSchemaName("WCT_SCHEMA"); 

SqlParameterSource sqlParms = 
     new MapSqlParameterSource().addValue("p_upload_idno", Integer.parseInt("143")) 
      .addValue("p_file_type_idno", Integer.parseInt(noDataDmrDTO.getFile_type_idno())).addValue("p_file_name", noDataDmrDTO.getFile_name()) 
      .addValue("p_file", noDataDmrDTO.getFile_data(), Types.BINARY).addValue("p_comments", noDataDmrDTO.getComments()) 
      .addValue("p_userid", noDataDmrDTO.getUserid()); 


    simpleJdbcCallResult = simpleJdbcCall.execute(sqlParms); 

} 
+0

Ум объясняет ваш ответ? – CinCout

0

Другое решение с лямбда (который не требуется):

jdbcTemplate.update(dbcon -> { 
    PreparedStatement ps = dbcon.prepareStatement("INSERT INTO ..."); 
    ps.setString(1, yourfieldValue); 
    ps.setBinaryStream(2, yourInputStream, yourInputStreamSizeAsInt)); 
    return ps; 
}); 

NB. К сожалению, это не включает KeyGenerator.