プログラムで生成された主キーで Spring の KeyHolder を使用する
-
26-09-2019 - |
質問
Spring の NamedParameterJdbcTemplate を使用してテーブルへの挿入を実行しています。テーブルはシーケンスで NEXTVAL を使用して主キーを取得します。次に、この生成された ID を私に返してもらいたいと考えています。Spring の KeyHolder 実装を次のように使用しています。
KeyHolder key = new GeneratedKeyHolder();
jdbcTemplate.update(Constants.INSERT_ORDER_STATEMENT, params, key);
ただし、このステートメントを実行すると、次の結果が得られます。
org.springframework.dao.DataRetrievalFailureException: The generated key is not of a supported numeric type. Unable to cast [oracle.sql.ROWID] to [java.lang.Number]
at org.springframework.jdbc.support.GeneratedKeyHolder.getKey(GeneratedKeyHolder.java:73)
私に何が欠けているのか何かアイデアはありますか?
解決
私はあなたがJdbcTemplate
に間違った方法を使っていると思います。あなたのコードのフラグメントと一致するように思われるupdate
方法の一つだけです。
int update(String sql, Object... args)
その場合は2つの要素が配列をvargsとして、あなたはparams
とkey
を渡している、とJdbcTemplate
は通常のバインド・パラメータとしてkey
を処理し、それを誤解釈されます。
はupdate
かかりJdbcTemplate
にのみパブリックKeyHolder
方法がある
int update(PreparedStatementCreator psc, KeyHolder generatedKeyHolder)
あなたがそれを使用するようにコードを修正してくださいする必要がありますので。
他のヒント
同様の問題を解決しました - Oracle では別の方法を使用する必要があります ( NamedParameterJdbcOperations
) -
int update(String sql,
SqlParameterSource paramSource,
KeyHolder generatedKeyHolder,
String[] keyColumnNames)
throws DataAccessException
keyColumnNames には自動生成された列が含まれています。私の場合は ["Id"] だけです。それ以外の場合は、ROWID のみが取得されます。見る 春のドキュメント 詳細については。
を実行する必要があります。 JdbcTemplate.update(PreparedStatementCreator p, KeyHolder k)
.
データベースから返されたキーは、 KeyHolder
パラメータオブジェクト。
例:
final String INSERT_ORDER_STATEMENT
= "insert into order (product_id, quantity) values(?, ?)";
KeyHolder keyHolder = new GeneratedKeyHolder();
jdbcTemplate.update(new PreparedStatementCreator() {
public PreparedStatement createPreparedStatement(
Connection connection) throws SQLException {
PreparedStatement ps = connection.prepareStatement(
INSERT_ORDER_STATEMENT, new String[] { "id" });
ps.setInt(1, order.getProductId());
ps.setInt(2, order.getQuantity());
return ps;
}
}, keyHolder);
さらに詳しい情報が見つかります ここ 参照ドキュメントに記載されています。
はありません@konstantin答えに手の込んだ:ここでは、完全に動作する例です。 データベースは、店舗イドを生成し、Oracleおよび列名であると仮定すると(任意の名前にすることができます)「GENERATED_ID」です。 注:私が使用NamedParameterJdbcTemplate.update(....)この例では春の、NOT JdbcTemplateクラス
public Integer insertRecordReturnGeneratedId(final MyObject obj)
{
final String INSERT_QUERY = "INSERT INTO MY_TABLE VALUES(GENERATED_ID_SEQ.NEXTVAL, :param1, :param2)";
try
{
MapSqlParameterSource parameters = new MapSqlParameterSource().addValue( "param1", obj.getField1() ).addValue( "param2", obj.getField1() ) ;
final KeyHolder holder = new GeneratedKeyHolder();
this.namedParameterJdbcTemplate.update( INSERT_QUERY, parameters, holder, new String[] {"GENERATED_ID" } );
Number generatedId = holder.getKey();
// Note: USING holder.getKey("GENERATED_ID") IS ok TOO.
return generatedId.intValue();
}
catch( DataAccessException dataAccessException )
{
}
}
でMySQL
CREATE TABLE `vets` (
`id` int(4) unsigned NOT NULL AUTO_INCREMENT,
`first_name` varchar(30) DEFAULT NULL,
`last_name` varchar(30) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `last_name` (`last_name`)
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8;
public @Data class Vet {
private int id;
private String firstname;
private String lastname;
}
@Repository
public class VetDaoImpl implements VetDao {
/** Logger. */
private static final Logger LOGGER = LoggerFactory.getLogger(VetDaoImpl.class);
private static final String INSERT_VET = "INSERT INTO vets (first_name, last_name) VALUES (:first_name, :last_name)";
@Autowired
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
@Override
public Number insertVet(final Vet vet) {
MapSqlParameterSource paramSource = new MapSqlParameterSource();
paramSource.addValue("first_name", vet.getFirstname());
paramSource.addValue("last_name", vet.getLastname());
KeyHolder keyHolder = new GeneratedKeyHolder();
int nbRecord = namedParameterJdbcTemplate.update(INSERT_VET, paramSource, keyHolder, new String[] {"id" });
LOGGER.info("insertVet: id ["+keyHolder.getKey()+"]");
return nbRecord;
}
}