英文:
Spring JPA instead of update performs insert
问题
我在Spring Data JPA中遇到了这个问题,我从数据库中获取了一个现有的实体,然后更改了一些值并执行了save()操作,但每次都会在数据库中创建新的条目。
我已经创建了一个用于测试这种行为的示例REST控制器,控制器如下:
@GetMapping("/user")
public void test() {
User byEmail = userRepository.findByEmail("test@test.com").orElse(null);
if (byEmail != null) {
userRepository.save(byEmail);
} else {
User user = new User();
user.setEmail("test@test.com");
userRepository.save(user);
}
}
每次触发此端点时,都会创建一个新的Users表记录,尽管findByEmail返回一个具有ID和所有其他数据的现有记录。
可能的原因是什么?
实体类如下:
@Getter
@Setter
@Entity
@Builder
@AllArgsConstructor
@NoArgsConstructor
@ToString
@Table(name = "forum_user")
public class User extends BaseEntityAudit {
@Id
@GeneratedValue(generator = "uuid")
@GenericGenerator(name = "uuid", strategy = "uuid")
private String id;
@Column(name = "first_name")
private String firstName;
@Column(name = "last_name")
private String lastName;
@Email
@Column(name = "email", unique = true)
private String email;
@Column(name = "external_id")
private String externalId;
@Column(name = "picture_url")
private String pictureUrl;
@OneToMany(fetch = FetchType.EAGER)
@JoinColumn(name = "user_id")
@JsonIgnore
private List<Post> posts;
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(
name = "user_group",
joinColumns = @JoinColumn(name = "user_id"),
inverseJoinColumns = @JoinColumn(name = "group_id")
)
@JsonIgnore
private List<Group> userGroups;
@ManyToOne
@JoinColumn(name = "role_id", referencedColumnName = "id", nullable = false)
private Role role;
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
@JoinColumn(name = "author_id")
@JsonIgnore
private List<NameTag> nameTags;
}
我使用查询方法通过电子邮件查找用户:
Optional<User> findByEmail(String email);
用户表的DDL如下:
create table C##USER2.FORUM_USER
(
ID VARCHAR2(32) not null
constraint SYS_C008383
primary key,
FIRST_NAME VARCHAR2(45),
LAST_NAME VARCHAR2(45),
EMAIL VARCHAR2(255),
EXTERNAL_ID VARCHAR2(45),
PICTURE_URL CLOB,
ROLE_ID VARCHAR2(32),
CREATED_AT TIMESTAMP(6),
CREATED_BY VARCHAR2(32),
UPDATED_AT TIMESTAMP(6),
UPDATED_BY VARCHAR2(32)
)
/
GROUP表的DDL如下(这里一切正常):
create table C##USER2.FORUM_GROUP
(
ID VARCHAR2(32) not null
constraint FORUM_GROUP_PK
primary key,
NAME VARCHAR2(45),
SHORT_NAME VARCHAR2(3),
TITLE VARCHAR2(45),
DESCRIPTION CLOB,
LATEST_POST TIMESTAMP(6),
GROUP_COLOR VARCHAR2(10),
LOGO_URL CLOB,
CREATED_AT TIMESTAMP(6),
NUMBER_OF_MEMBERS LONG,
CREATED_BY VARCHAR2(32),
UPDATED_AT TIMESTAMP(6),
UPDATED_BY VARCHAR2(32),
ALLOW_CREATING_MESSAGES NUMBER(1) default 1,
ALLOW_REPLAYING_TO_POST NUMBER(1) default 1,
ALLOW_EDIT_DELETE_POSTS NUMBER(1) default 1,
ALLOW_URL_LINK NUMBER(1) default 1,
ALLOW_FLAGGING NUMBER(1) default 1
)
/
英文:
I have this issue with Spring Data JPA, I fetch an existing entity from DB, I change some values and I perform save() on it, and instead of updating new entry gets created every time in the DB.
I have created a sample REST controller to test this behaviour, contoller looks like:
@GetMapping("/user")
public void test() {
User byEmail = userRepository.findByEmail("test@test.com").orElse(null);
if (byEmail != null) {
userRepository.save(byEmail);
} else {
User user = new User();
user.setEmail("test@test.com");
userRepository.save(user);
}
}
Each time I trigger this endpoint, a new record in the Users table is created even though findByEmail returns an existing one with ID and all the other data.
What could be the reason for this?
Entity class is:
@Getter
@Setter
@Entity
@Builder
@AllArgsConstructor
@NoArgsConstructor
@ToString
@Table(name = "forum_user")
public class User extends BaseEntityAudit {
@Id
@GeneratedValue(generator = "uuid")
@GenericGenerator(name = "uuid", strategy = "uuid")
private String id;
@Column(name = "first_name")
private String firstName;
@Column(name = "last_name")
private String lastName;
@Email
@Column(name = "email", unique = true)
private String email;
@Column(name = "external_id")
private String externalId;
@Column(name = "picture_url")
private String pictureUrl;
@OneToMany(fetch = FetchType.EAGER)
@JoinColumn(name = "user_id")
@JsonIgnore
private List<Post> posts;
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(
name = "user_group",
joinColumns = @JoinColumn(name = "user_id"),
inverseJoinColumns = @JoinColumn(name = "group_id")
)
@JsonIgnore
private List<Group> userGroups;
@ManyToOne
@JoinColumn(name = "role_id", referencedColumnName = "id", nullable = false)
private Role role;
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
@JoinColumn(name = "author_id")
@JsonIgnore
private List<NameTag> nameTags;
}
I use query method to fetch user by email:
Optional<User> findByEmail(String email);
DDL for user table is:
create table C##USER2.FORUM_USER
(
ID VARCHAR2(32) not null
constraint SYS_C008383
primary key,
FIRST_NAME VARCHAR2(45),
LAST_NAME VARCHAR2(45),
EMAIL VARCHAR2(255),
EXTERNAL_ID VARCHAR2(45),
PICTURE_URL CLOB,
ROLE_ID VARCHAR2(32),
CREATED_AT TIMESTAMP(6),
CREATED_BY VARCHAR2(32),
UPDATED_AT TIMESTAMP(6),
UPDATED_BY VARCHAR2(32)
)
/
And DDL for GROUP table (here everything works fine) is:
create table C##USER2.FORUM_GROUP
(
ID VARCHAR2(32) not null
constraint FORUM_GROUP_PK
primary key,
NAME VARCHAR2(45),
SHORT_NAME VARCHAR2(3),
TITLE VARCHAR2(45),
DESCRIPTION CLOB,
LATEST_POST TIMESTAMP(6),
GROUP_COLOR VARCHAR2(10),
LOGO_URL CLOB,
CREATED_AT TIMESTAMP(6),
NUMBER_OF_MEMBERS LONG,
CREATED_BY VARCHAR2(32),
UPDATED_AT TIMESTAMP(6),
UPDATED_BY VARCHAR2(32),
ALLOW_CREATING_MESSAGES NUMBER(1) default 1,
ALLOW_REPLAYING_TO_POST NUMBER(1) default 1,
ALLOW_EDIT_DELETE_POSTS NUMBER(1) default 1,
ALLOW_URL_LINK NUMBER(1) default 1,
ALLOW_FLAGGING NUMBER(1) default 1
)
/
答案1
得分: 1
我怀疑这是因为以下注释之一引起的:
@GeneratedValue(generator = "uuid")
@GenericGenerator(name = "uuid", strategy = "uuid")
你能否移除它们,然后通过默认值添加ID?
private String id = UUID.random().toString();
另外,你可以检查一下是否用以下方式替代保存操作:
log.info("Before: " + byEmail.getId());
var result = userRepository.save(byEmail);
log.info("After result: " + result.getId());
log.info("After byEmail: " + byEmail.getId());
并且在这里检查ID。如果 "result" 有不同的ID,那么就是一个ID的问题。还要检查在执行保存方法之后,"byEmail" 是否有不同的ID。
另一种解决方法是使用 @PrePersist 来在缺少ID时动态添加ID。基本上,你正在自己编写ID生成器:
@PrePersist
void idGenerator(){
if(this.id == null){
this.id = UUID.random().toString();
}
}
英文:
I suspect this happens because of (one of) these annotations:
@GeneratedValue(generator = "uuid")
@GenericGenerator(name = "uuid", strategy = "uuid")
Can you remove them and just add the ID via default value?
private String id = UUID.random().toString();
Also what you can check is if you replace the saving with this:
log.info("Before: " + byEmail.getId());
var result = userRepository.save(byEmail);
log.info("After result: " + result.getId());
log.info("After byEmail: " + byEmail.getId());
and introspect the IDs here. If "result" has a different ID, then its an ID issue. Also check if "byEmail" AFTER executing the save method has a different ID then before.
A solution which i also use is to use @PrePersist to add the ID "dynamically" incase its missing. You are basically writing the id generator yourself:
@PrePersist
void idGenerator(){
if(this.id == null){
this.id = UUID.random().toString();
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论