Spring JdbcTemplate - вставить BLOB и вернуть сгенерированный ключ
Из документации Spring JDBC я знаю, как вставить blob с помощью 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();
А также как получить сгенерированный ключ недавно вставленной строки:
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
Есть ли способ объединить эти два?
Ответы
Ответ 1
Я пришел сюда, ища тот же ответ, но не был удовлетворен принятым. Поэтому я немного погубил и придумал это решение, которое я тестировал в 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, должна иметь столбец с автоинкрементами для идентификатора с использованием последовательности и триггера. Триггер необходим, потому что в противном случае вам нужно будет использовать Spring NamedParameterJdbcOperations (чтобы выполнить последовательность .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;
/
Ответ 2
Все это казалось мне слишком сложным. Это работает и прост. Он использует 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();
}
}
Ответ 3
В итоге я просто выполнил два запроса: один для создания строки и один для обновления 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, когда код получает этот волосатый.
В любом случае, я тестировал вышеуказанный код, и он работает. В моем случае я решил, что это слишком усложнит мой код.
Ответ 4
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();
}
}
}
Ответ 5
В 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 (?, ?, ?, ?, ?)
Ответ 6
Если ваша базовая база данных - это mysql, вы можете автогенерировать свой первичный ключ. Затем, чтобы вставить запись в свой db, вы можете использовать следующий синтаксис для вставки:
INSERT INTO lob_table (a_blob) VALUES (?)
Ответ 7
Это тестируется только на 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());
Ответ 8
Другое решение с лямбда (которое не требуется):
jdbcTemplate.update(dbcon -> {
PreparedStatement ps = dbcon.prepareStatement("INSERT INTO ...");
ps.setString(1, yourfieldValue);
ps.setBinaryStream(2, yourInputStream, yourInputStreamSizeAsInt));
return ps;
});
NB. Извините, это не включает KeyGenerator.
Ответ 9
Я получил ту же проблему для обновления данных BLOB-объектов - необходимо обновить изображение в базе данных. чем я нахожу какое-то решение, как показано ниже. для более подробной информации обновите изображение в базе данных
LobHandler lobHandler = new DefaultLobHandler();
statusRes = jdbcTemplate.update("update USERS set FILE_CONTENT = ?, FILE_NAME = ? WHERE lower(USER_ID) = ?",
new Object[] {new SqlLobValue(image, lobHandler),fileName,userIdLower},
new int[] {Types.BLOB,Types.VARCHAR,Types.VARCHAR});
Ответ 10
Возможно, это примерно так:
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
}
Ответ 11
Пожалуйста, используйте:
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);
}