@SQLInsert(sql = "insert ignore into tb_vip_code (code, duration) value (?, ?)")
public class VipCode extends BaseModel {
private static final long serialVersionUID = -4697221755301869573L;
private String code;
private Integer duration;
private Integer status;
private Long userId;
// 构造函数
}
如上实体类定义,@SQLInsert 注解的本意是在批量插入数据遇到唯一性约束时忽略,继续插入不重复的数据,但在调用 repository 的 save 方法插入数据是,总是报参数越界错误,有朋友遇到过吗?
Caused by: java.sql.SQLException: Parameter index out of range (3 > number of parameters, which is 2).
单元测试代码如下:
@Test
public void addOne() throws Exception {
VipCode vipCode = new VipCode("123456", 1);
service.addOne(vipCode);
}
第一个问题解决了,SQL 里要把除自增主键外的所有字段写全,而且顺序要跟实体类属性定义的顺序保持一致,父类的属性在最前面,后面接着是子类的属性,如下:
insert ignore into tb_vip_code (create_at, update_at, code, duration, status, user_id) values (?, ?, ?, ?, ?, ?)
但,又出现了另外一个问题,插入不重复记录的时候,可以正常插入数据,插入重复记录报异常:
org.springframework.orm.jpa.JpaSystemException: The database returned no natively generated identity value
欢迎大家继续讨论。
1
reid2017 OP 自己 up 一下
|
2
onnfee 2018-11-14 18:41:17 +08:00
你先找到 Hibernate 生成的 insert 语句,在此基础上 diy。
|
3
johnniang 2018-11-14 19:49:43 +08:00
这个错误似乎是因为参数的问题。本来只需要两个参数,而 https://raymondhlee.wordpress.com/2012/01/07/using-sqlinsert-to-insert-entity-in-hibernate-with-custom-sql/
|
5
Aidenboss 2018-11-14 20:07:26 +08:00
要不还是写在 VipCodeRepository 里面吧?
``` interface VipCodeRepository { @Modify @Query(sql = "..." , nativeSql = true) @Transcational public void save(SString code, int duration); } ``` |
6
sutra 2018-11-14 20:35:26 +08:00
status 和 userId 字段是怎么定义的?还有 BaseModel 里面是不是有 id 字段的定义,怎么没有在 @SQLInsert 里出现呢?
|
7
TommyLemon 2018-11-14 23:45:53 +08:00
@SQLInsert(sql = "insert ignore into tb_vip_code (code, duration) value (?, ?)")
首先 value 得改成 values 如果还不行,可能就是如楼上所说 BaseModel 里有额外的字段,很可能还是用基本类型有默认值的。 如果是基本类型,改成对应的封装类型再试。 另外你代码都没发全,都不知道构造函数里是否给其它字段 set 了值,难以判断。 |
8
TommyLemon 2018-11-14 23:46:17 +08:00
@TommyLemon 默认值得去掉,或者改成 null。
|
9
TommyLemon 2018-11-14 23:51:47 +08:00
@TommyLemon
如果以上试过都不行,或许是 serialVersionUID 有个对应的 getSerialVersionUID 方法(自动生成时错误勾选), 导致序列化时多转了一个字段。 某些 JSON 库可以通过 @JSONField(serialize = false) 这种注解等方式来忽略要序列化的变量或方法。 |
10
reid2017 OP @sutra
status 数据库里有设置默认值,userid 可空,BaseModel 时有 id 主键自增,这些应该都不影响吧 |
11
reid2017 OP @TommyLemon 把 serialVersionUID 注释掉也不行,构造函数里没有对其它字段设置值
``` public VipCode(String code, Integer duration) { this.code = code; this.duration = duration; } ``` |
14
TommyLemon 2018-11-15 09:47:34 +08:00
@reid2017 也有可能这个库默认加了一个 主键 或 创建时间 之类的字段,
最好还是断点调试下下它最终到 JDBC 时 set 进去的值,这样最容易看出问题所在。 |
15
sutra 2018-11-15 10:05:34 +08:00
|
16
passerbytiny 2018-11-15 10:20:42 +08:00
把 ID 的 @GeneratedValue 去掉试试,你用了自定义 SQL,GeneratedValue 很有可能被判定成由程序或序列自动生成(而不是数据库自增长)。
|
17
ZiLong 2018-11-15 10:47:25 +08:00
加日志看下最终执行的 sql 和对应的参数
|
18
reid2017 OP @passerbytiny
去掉这注解就需要自己手动给主键赋值了 |
19
reid2017 OP |
20
passerbytiny 2018-11-15 11:23:17 +08:00
差不多知道问题了,跟 ID 没关系,而是你的实体有 4 个非自动属性,SQl 语句只有两个字段。Hibernate 不会在手工 SQL 处理上放太多重心,这里可能只是简单做了实体属性跟 SQL 字段的映射,但是没有自动判断参数数量。
你的 SQL 语句要把所有字段写全,另外除了“类属性——表字段”的自动映射外,不要期望 Hibernate 帮你做其它的自动处理。 你现在这个是批量、仅部分字段、遇重复就忽略的操作,已经是一个很复杂的数据处理了,建议在 Dao/Repository,甚至 Service 中处理,并且全盘交给 SQL,不要再实体定义上处理。 |
21
reid2017 OP 主键 ID 策略如下:
@Id @GeneratedValue(strategy = GenerationType.IDENTITY) //@GeneratedValue(strategy = GenerationType.AUTO) private Long id; 两个策略都尝试过,一样的异常。 |
22
sutra 2018-11-15 12:20:32 +08:00
把日志打开吧,然后就知道生成的 SQL 语句到底什么样子了:
<Logger name="org.hibernate.SQL" level="DEBUG" includeLocation="true" additivity="false"> <AppenderRef ref="file-hibernate" /> </Logger> <Logger name="org.hibernate.type" level="TRACE" includeLocation="true" additivity="false"> <AppenderRef ref="file-hibernate" /> </Logger> |