Spring JdbcTemplate – 插入blob并返回生成的密钥

从Spring JDBC文档中,我知道如何使用JdbcTemplate插入blob

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(); 

以及如何检索新插入行的生成密钥 :

 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 

有没有办法把两者结合起来?

我来到这里寻找相同的答案,但对接受的内容并不满意。 所以我做了一些挖掘,并提出了我在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中创建的表应使用序列和触发器为id创建一个自动递增的列。 触发器是必要的,因为否则你必须使用Spring的NamedParameterJdbcOperations(在SQL中执行sequence.nextval),它似乎不支持KeyHolder(我用它来检索自动生成id)。 有关详细信息,请参阅此博客文章(而非我的博客): 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; / 

所有这些对我来说似乎太复杂了。 这很有效,也很简单。 它使用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(); } } 

我最后只执行了两个查询,一个用于创建行,另一个用于更新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的好处。

无论如何,我测试了上面的代码,它的工作原理。 对于我的情况,我认为它会使我的代码太复杂化。

 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(); } } } 

如果底层数据库是mysql,您可以自动生成主键。 然后,要将记录插入数据库,可以使用以下语法进行插入:

 INSERT INTO lob_table (a_blob) VALUES (?) 

2012年,不推荐使用SimpleJdbcTemplate 。 这就是我做的:

 KeyHolder keyHolder = new GeneratedKeyHolder(); List 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 (?, ?, ?, ?, ?) 

这仅在MySql上测试,我只粘贴相关部分。 运行我的测试类之后,结果显示如下:“通过template.update(psc,kh)添加记录: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()); 

使用lambda的另一种解决方案(不是必需的):

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

NB。 对不起,这不包括KeyGenerator。

也许有些像这样:

 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 parameters = new HashMap(2); parameters.put("first_name", actor.getFirstName()); parameters.put("last_name", actor.getLastName()); Number newId = insertActor.executeAndReturnKey(parameters); actor.setId(newId.longValue()); } // ... additional methods } 

请用:

 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); }