使用自动生成的 ID 插入一条新记录
Insert a new record with autogenerated id
我正在尝试使用 MyBatis 向一个简单的数据库 table 中插入一条新记录,但我遇到了一个奇怪的异常。可能跟我没用POJO有关
MyBatis 版本:3.4.5
我的table:
CREATE TABLE IF NOT EXISTS image
(
id BIGINT PRIMARY KEY,
content BYTEA
) WITHOUT OIDS;
MyBatis 映射器:
@Insert("INSERT INTO image (id, content) VALUES (#{id}, #{content})")
@SelectKey(statement = "SELECT NEXTVAL('image_seq')", keyProperty = "id", before = true, resultType = long.class)
long insertImage(byte[] content);
我尝试使用它的方式:
byte[] fileContent = IOUtils.toByteArray(inputStream);
long id = imageDao.insertImage(fileContent);
我得到的异常:
java.lang.ClassCastException: java.lang.Long cannot be cast to [B
at org.apache.ibatis.type.ByteArrayTypeHandler.setNonNullParameter(ByteArrayTypeHandler.java:26)
at org.apache.ibatis.type.BaseTypeHandler.setParameter(BaseTypeHandler.java:53)
at org.apache.ibatis.scripting.defaults.DefaultParameterHandler.setParameters(DefaultParameterHandler.java:87)
at org.apache.ibatis.executor.statement.PreparedStatementHandler.parameterize(PreparedStatementHandler.java:93)
at org.apache.ibatis.executor.statement.RoutingStatementHandler.parameterize(RoutingStatementHandler.java:64)
at org.apache.ibatis.executor.SimpleExecutor.prepareStatement(SimpleExecutor.java:86)
at org.apache.ibatis.executor.SimpleExecutor.doUpdate(SimpleExecutor.java:49)
at org.apache.ibatis.executor.BaseExecutor.update(BaseExecutor.java:117)
at org.apache.ibatis.executor.CachingExecutor.update(CachingExecutor.java:76)
at org.apache.ibatis.session.defaults.DefaultSqlSession.update(DefaultSqlSession.java:198)
at org.apache.ibatis.session.defaults.DefaultSqlSession.insert(DefaultSqlSession.java:185)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
...
我不想用 getter/setter 方法为这个 "content" 参数创建 POJO class,但我认为这个问题与缺少 POJO 有关。
解决方法是什么?
编辑
我正在尝试调试 mybatis 代码,我在 parameterTypes
中找到了“[B
”:
java.lang.Long cannot be cast to [B
这表示您正在尝试将 long
转换为 byte[]
正在查看 org.apache.ibatis.type.ByteArrayTypeHandler
的来源:
public void setNonNullParameter(PreparedStatement ps, int i, byte[] parameter, JdbcType jdbcType) throws SQLException {
ps.setBytes(i, parameter);
}
我认为您需要从插入注释中删除 {id}
(因为该值是自动生成的)。
@Insert("INSERT INTO image (content) VALUES (#{content})")
否则参数移动一位。
@SelectKey
当你想在代码中更远的地方重用生成的值时很有用,但你似乎不会。
那为什么不把所有东西都放在 SQL:
INSERT INTO image (id, content) VALUES ((SELECT NEXTVAL('image_seq')), #{content})
关于参数的例外情况,参数必须用@Param
注释命名
int insertImage(@Param("content") byte[] content);
或
int insertImage(@Param("id) Long id, @Param("content") byte[] content)
请注意,INSERT 以及 UPDATE 和 DELETE 语句 returns 类型 int 是 inserted/updated/deleted 行的数量,[...]
编辑: 除非你认为在幕后, java 8 PreparedStatement.executeLargeUpdate
返回 long 被执行。
[...] 而不是建议的生成密钥。然后看起来您最终想要获得键值,这意味着回到原点 @SelectKey
并且需要一个 POJO 和一个目标 属性 作为生成的值。它甚至可以与带有生成键的批量插入一起使用。
我最近发现如果按照 settings section of the documentation:
中的说明,可以使用实际参数名称(然后您的代码将按原样工作)
useActualParamName
Allow referencing statement parameters by their
actual names declared in the method signature. To use this feature,
your project must be compiled in Java 8 with -parameters option.
(Since: 3.4.1) valid values: true
| false
default: true
我正在尝试使用 MyBatis 向一个简单的数据库 table 中插入一条新记录,但我遇到了一个奇怪的异常。可能跟我没用POJO有关
MyBatis 版本:3.4.5
我的table:
CREATE TABLE IF NOT EXISTS image
(
id BIGINT PRIMARY KEY,
content BYTEA
) WITHOUT OIDS;
MyBatis 映射器:
@Insert("INSERT INTO image (id, content) VALUES (#{id}, #{content})")
@SelectKey(statement = "SELECT NEXTVAL('image_seq')", keyProperty = "id", before = true, resultType = long.class)
long insertImage(byte[] content);
我尝试使用它的方式:
byte[] fileContent = IOUtils.toByteArray(inputStream);
long id = imageDao.insertImage(fileContent);
我得到的异常:
java.lang.ClassCastException: java.lang.Long cannot be cast to [B
at org.apache.ibatis.type.ByteArrayTypeHandler.setNonNullParameter(ByteArrayTypeHandler.java:26)
at org.apache.ibatis.type.BaseTypeHandler.setParameter(BaseTypeHandler.java:53)
at org.apache.ibatis.scripting.defaults.DefaultParameterHandler.setParameters(DefaultParameterHandler.java:87)
at org.apache.ibatis.executor.statement.PreparedStatementHandler.parameterize(PreparedStatementHandler.java:93)
at org.apache.ibatis.executor.statement.RoutingStatementHandler.parameterize(RoutingStatementHandler.java:64)
at org.apache.ibatis.executor.SimpleExecutor.prepareStatement(SimpleExecutor.java:86)
at org.apache.ibatis.executor.SimpleExecutor.doUpdate(SimpleExecutor.java:49)
at org.apache.ibatis.executor.BaseExecutor.update(BaseExecutor.java:117)
at org.apache.ibatis.executor.CachingExecutor.update(CachingExecutor.java:76)
at org.apache.ibatis.session.defaults.DefaultSqlSession.update(DefaultSqlSession.java:198)
at org.apache.ibatis.session.defaults.DefaultSqlSession.insert(DefaultSqlSession.java:185)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
...
我不想用 getter/setter 方法为这个 "content" 参数创建 POJO class,但我认为这个问题与缺少 POJO 有关。
解决方法是什么?
编辑
我正在尝试调试 mybatis 代码,我在 parameterTypes
中找到了“[B
”:
java.lang.Long cannot be cast to [B
这表示您正在尝试将 long
转换为 byte[]
正在查看 org.apache.ibatis.type.ByteArrayTypeHandler
的来源:
public void setNonNullParameter(PreparedStatement ps, int i, byte[] parameter, JdbcType jdbcType) throws SQLException {
ps.setBytes(i, parameter);
}
我认为您需要从插入注释中删除 {id}
(因为该值是自动生成的)。
@Insert("INSERT INTO image (content) VALUES (#{content})")
否则参数移动一位。
@SelectKey
当你想在代码中更远的地方重用生成的值时很有用,但你似乎不会。
那为什么不把所有东西都放在 SQL:
INSERT INTO image (id, content) VALUES ((SELECT NEXTVAL('image_seq')), #{content})
关于参数的例外情况,参数必须用@Param
注释命名
int insertImage(@Param("content") byte[] content);
或
int insertImage(@Param("id) Long id, @Param("content") byte[] content)
请注意,INSERT 以及 UPDATE 和 DELETE 语句 returns 类型 int 是 inserted/updated/deleted 行的数量,[...]
编辑: 除非你认为在幕后, java 8 PreparedStatement.executeLargeUpdate
返回 long 被执行。
[...] 而不是建议的生成密钥。然后看起来您最终想要获得键值,这意味着回到原点 @SelectKey
并且需要一个 POJO 和一个目标 属性 作为生成的值。它甚至可以与带有生成键的批量插入一起使用。
我最近发现如果按照 settings section of the documentation:
中的说明,可以使用实际参数名称(然后您的代码将按原样工作)
useActualParamName
Allow referencing statement parameters by their actual names declared in the method signature. To use this feature, your project must be compiled in Java 8 with -parameters option. (Since: 3.4.1) valid values:true
|false
default:true