jpa添加时数据缺少关联实体ID

511 阅读2分钟

[这是我参与11月更文挑战的第10天,活动详情查看:2021最后一次更文挑战]


今天,在添加一个注册用户的接口到测试环境时,发现通过该接口生成的用户会缺失某个关联实体的ID,最终导致业务执行失败。但是,在开发环境并不存在这个问题

相关环境:

  • jdk8
  • JPA
  • MySQL

1. 是否是sql语句错误

在第一时间,觉得是表结构不一致导致的问题,在重新导入开发环境的数据库后,该问题依旧存在。在开发的机子上通过java -jar运行项目,复现了该问题。所以,这个问题应该是代码的原因。

通过JAVA自带的远程调试启动jar包后,IDEA通过5005端口远程调试jar包。

image.png

可以看到parent字段是非空字段,证明关联表的数据有读取到。

image.png

2. 是否是多sql错乱导致的

jpa执行事务时,sql的执行顺序并不是按照代码的顺序执行的,而是根据INSERT, UPDATE, DELETE的顺序执行。如果查询在插入之后,那么是有可能导致这个问题的。

配置打印sql语句:

spring:
  jpa:
    show-sql: true # 显示sql

查看打印的sql语句后,可以发现该顺序在逻辑上的顺序正确。

3. 是否是jpa的id重复

有一位朋友提出:可能是主键策略设置错误导致的。

JPA主键的生成策略:

  • GenerationType.TABLE
  • GenerationType.AUTO
  • GenerationType.IDENTITY
  • GenerationType.SEQUENCE:根据底层数据库的序列来生成主键,条件是数据库支持序列。

如果策略是SEQUENCE,那会由数据库生成序列;如果注解中还设置了id,那么就会覆盖数据库生成的序列。

例如:

@GeneratedValue(strategy = GenerationType.SEQUENCE) // 数据库的序列来生成主键
@GenericGenerator(name = "uuid2", strategy = "org.hibernate.id.UUIDGenerator") // 注解生成UUID

相关表的ID定义如下:

@Id
@GeneratedValue(generator = "uuid2")
@GenericGenerator(name = "uuid2", strategy = "org.hibernate.id.UUIDGenerator")
@Column(columnDefinition = "VARCHAR(255) comment 'ID' ")
private String id;

可以看到并没有使用SEQUENCE策略,所以这个问题也可以排除。

4. 字段映射错误

在查询表结构后,发现经过jar包运行时会多出一个字段。该字段的@JoinColumn没有定义name属性。通过编程工具执行会生成uparent_id字段;但是通过jar包运行时,会生成wparent_id,最终导致JPA的native query查询失败。

相关字段的定义如下:

@Table(name = "t_folder")
@Entity
@org.hibernate.annotations.Table(appliesTo = "t_folder", comment = "文件夹表")
@DynamicInsert
@DynamicUpdate
public class FolderEntity  extends BaseEntity{
    // 父文件夹
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(referencedColumnName = "id", foreignKey = @ForeignKey,columnDefinition = "varchar(255) comment '父文件夹'")
    @JsonIgnore
    private FolderEntity parent;
    
。。。
}

为该字段设置name属性后,可以解决这个问题。

5. 总结

这次的问题是@JoinColumn自动生成字段名不一致导致的测试环境异常导致的。框架虽好,但坑也不少。