JPA 问题请教

2018 年 11 月 14 日
 reid2017
@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);
    }
4136 次点击
所在节点    Java
22 条回复
reid2017
2018 年 11 月 14 日
自己 up 一下
uuau
2018 年 11 月 14 日
你先找到 Hibernate 生成的 insert 语句,在此基础上 diy。
johnniang
2018 年 11 月 14 日
这个错误似乎是因为参数的问题。本来只需要两个参数,而 https://raymondhlee.wordpress.com/2012/01/07/using-sqlinsert-to-insert-entity-in-hibernate-with-custom-sql/
johnniang
2018 年 11 月 14 日
忽略 link 前面的文字 @reid2017
Aidenboss
2018 年 11 月 14 日
要不还是写在 VipCodeRepository 里面吧?
```
interface VipCodeRepository {
@Modify
@Query(sql = "..." , nativeSql = true)
@Transcational
public void save(SString code, int duration);
}
```
sutra
2018 年 11 月 14 日
status 和 userId 字段是怎么定义的?还有 BaseModel 里面是不是有 id 字段的定义,怎么没有在 @SQLInsert 里出现呢?
TommyLemon
2018 年 11 月 14 日
@SQLInsert(sql = "insert ignore into tb_vip_code (code, duration) value (?, ?)")
首先 value 得改成 values
如果还不行,可能就是如楼上所说 BaseModel 里有额外的字段,很可能还是用基本类型有默认值的。
如果是基本类型,改成对应的封装类型再试。

另外你代码都没发全,都不知道构造函数里是否给其它字段 set 了值,难以判断。
TommyLemon
2018 年 11 月 14 日
@TommyLemon 默认值得去掉,或者改成 null。
TommyLemon
2018 年 11 月 14 日
@TommyLemon
如果以上试过都不行,或许是 serialVersionUID 有个对应的 getSerialVersionUID 方法(自动生成时错误勾选),
导致序列化时多转了一个字段。
某些 JSON 库可以通过 @JSONField(serialize = false) 这种注解等方式来忽略要序列化的变量或方法。
reid2017
2018 年 11 月 15 日
@sutra
status 数据库里有设置默认值,userid 可空,BaseModel 时有 id 主键自增,这些应该都不影响吧
reid2017
2018 年 11 月 15 日
@TommyLemon 把 serialVersionUID 注释掉也不行,构造函数里没有对其它字段设置值

```
public VipCode(String code, Integer duration) {
this.code = code;
this.duration = duration;
}
```
reid2017
2018 年 11 月 15 日
@johnniang 跟文章描述的场景并不一样
reid2017
2018 年 11 月 15 日
@Aidenboss 就是不想这样写啰,看到有这个注解应该试下
TommyLemon
2018 年 11 月 15 日
@reid2017 也有可能这个库默认加了一个 主键 或 创建时间 之类的字段,
最好还是断点调试下下它最终到 JDBC 时 set 进去的值,这样最容易看出问题所在。
sutra
2018 年 11 月 15 日
改成这样看看,换句话说就是把所有 @Column 都写全:
@SQLInsert(sql = "insert ignore into tb_vip_code (id, userId, code, duration) value (?, ?, ?, ?)")
passerbytiny
2018 年 11 月 15 日
把 ID 的 @GeneratedValue 去掉试试,你用了自定义 SQL,GeneratedValue 很有可能被判定成由程序或序列自动生成(而不是数据库自增长)。
ZiLong
2018 年 11 月 15 日
加日志看下最终执行的 sql 和对应的参数
reid2017
2018 年 11 月 15 日
@passerbytiny
去掉这注解就需要自己手动给主键赋值了
reid2017
2018 年 11 月 15 日
@ZiLong
Hibernate: insert ignore into tb_vip_code (code, duration) values (?, ?)
参数没打印出来
passerbytiny
2018 年 11 月 15 日
差不多知道问题了,跟 ID 没关系,而是你的实体有 4 个非自动属性,SQl 语句只有两个字段。Hibernate 不会在手工 SQL 处理上放太多重心,这里可能只是简单做了实体属性跟 SQL 字段的映射,但是没有自动判断参数数量。

你的 SQL 语句要把所有字段写全,另外除了“类属性——表字段”的自动映射外,不要期望 Hibernate 帮你做其它的自动处理。

你现在这个是批量、仅部分字段、遇重复就忽略的操作,已经是一个很复杂的数据处理了,建议在 Dao/Repository,甚至 Service 中处理,并且全盘交给 SQL,不要再实体定义上处理。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/507859

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX