[这是我参与11月更文挑战的第10天,活动详情查看:2021最后一次更文挑战]
今天,在添加一个注册用户的接口到测试环境时,发现通过该接口生成的用户会缺失某个关联实体的ID,最终导致业务执行失败。但是,在开发环境并不存在这个问题。
相关环境:
- jdk8
- JPA
- MySQL
1. 是否是sql语句错误
在第一时间,觉得是表结构不一致导致的问题,在重新导入开发环境的数据库后,该问题依旧存在。在开发的机子上通过java -jar
运行项目,复现了该问题。所以,这个问题应该是代码的原因。
通过JAVA自带的远程调试启动jar包后,IDEA通过5005端口远程调试jar包。
可以看到parent
字段是非空字段,证明关联表的数据有读取到。
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
自动生成字段名不一致导致的测试环境异常导致的。框架虽好,但坑也不少。