當我嘗試將用戶物體保存到資料庫時
org.hibernate.PersistentObjectException: detached entity passed to persist: kpi.diploma.ovcharenko.entity.user.AppUser
出現此錯誤一些更多資訊此錯誤出現在哪里
at kpi.diploma.ovcharenko.service.user.LibraryUserService.createPasswordResetTokenForUser(LibraryUserService.java:165) ~[classes/:na]
at kpi.diploma.ovcharenko.service.user.LibraryUserService$$FastClassBySpringCGLIB$$ca63bf4b.invoke(<generated>) ~[classes/:na]
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.3.10.jar:5.3.10]
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:688) ~[spring-aop-5.3.10.jar:5.3.10]
at kpi.diploma.ovcharenko.service.user.LibraryUserService$$EnhancerBySpringCGLIB$$6160822e.createVerificationTokenForUser(<generated>) ~[classes/:na]
at kpi.diploma.ovcharenko.service.activation.RegistrationListener.confirmRegistration(RegistrationListener.java:39) ~[classes/:na]
at kpi.diploma.ovcharenko.service.activation.RegistrationListener.onApplicationEvent(RegistrationListener.java:32) ~[classes/:na]
at kpi.diploma.ovcharenko.service.activation.RegistrationListener.onApplicationEvent(RegistrationListener.java:16) ~[classes/:na]
at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:176) ~[spring-context-5.3.10.jar:5.3.10]
at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:169) ~[spring-context-5.3.10.jar:5.3.10]
at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:143) ~[spring-context-5.3.10.jar:5.3.10]
at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:421) ~[spring-context-5.3.10.jar:5.3.10]
at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:378) ~[spring-context-5.3.10.jar:5.3.10]
at kpi.diploma.ovcharenko.controller.UserController.registerUserAccount(UserController.java:96) ~[classes/:na]
如您所見,此方法中出現錯誤。在這種方法中,我為用戶創建了一個驗證令牌
@Override
@Transactional
public void createVerificationTokenForUser(final AppUser user, final String token) {
final VerificationToken myToken = new VerificationToken(token, user);
verificationTokenRepository.save(myToken);
}
我在我的 RegistrationListener 中呼叫此方法
private void confirmRegistration(OnRegistrationCompleteEvent event) {
AppUser user = event.getUser();
log.info(user.toString());
String token = UUID.randomUUID().toString();
userService.createVerificationTokenForUser(user, token);
final SimpleMailMessage email = constructEmailMessage(event, user, token);
mailSender.send(email);
}
以及我如何在 RegistrationListener 中呼叫我的 confirmRegistration 方法
@Override
public void onApplicationEvent(OnRegistrationCompleteEvent event) {
this.confirmRegistration(event);
}
我在控制器中使用的這個 RegistrationListener 像這樣
@PostMapping("/registration")
public String registerUserAccount(@ModelAttribute("user") @Valid UserModel userModel, BindingResult result, HttpServletRequest request) {
AppUser existing = userService.findByEmail(userModel.getEmail());
if (existing != null) {
result.rejectValue("email", null, "There is already an account registered with that email");
}
if (result.hasErrors()) {
return "registration";
}
AppUser registeredUser = userService.save(userModel);
log.info(registeredUser.toString());
String appUrl = request.getContextPath();
eventPublisher.publishEvent(new OnRegistrationCompleteEvent(registeredUser, request.getLocale(), appUrl));
return "redirect:/regSuccessfully";
}
如您所見,我在控制器和偵聽器中有 log.info(),這是因為我認為問題可能是因為用戶 ID 有問題,但是,當我的日志顯示用戶和用戶 ID 是一切時行
2022-05-16 21:33:27.522 INFO 65143 --- [nio-8080-exec-7] k.d.o.controller.UserController : AppUser{id=42, firstName='[email protected]', lastName='[email protected]', email='[email protected]', telephoneNumber='', password='$2a$10$4ao20SYR2QJsQ.Fj50Jek.ZBRG0R0g9N8t3iaksUx2.byIb0fj6Y6', registrationDate=2022-05-16 21:33:27.492, enabled=false, roles=[kpi.diploma.ovcharenko.entity.user.UserRole@bbe3026f], bookCards=[]}
2022-05-16 21:33:27.523 INFO 65143 --- [nio-8080-exec-7] k.d.o.s.activation.RegistrationListener : AppUser{id=42, firstName='[email protected]', lastName='[email protected]', email='[email protected]', telephoneNumber='', password='$2a$10$4ao20SYR2QJsQ.Fj50Jek.ZBRG0R0g9N8t3iaksUx2.byIb0fj6Y6', registrationDate=2022-05-16 21:33:27.492, enabled=false, roles=[kpi.diploma.ovcharenko.entity.user.UserRole@bbe3026f], bookCards=[]}
還有一點是,在我嘗試注冊用戶后,我收到了上述錯誤,但我的用戶已成功保存到資料庫中 [
這是我的 AppUser 類和 VerificationToken 類
@Getter
@Setter
@Entity
@NoArgsConstructor
@AllArgsConstructor
@Builder(toBuilder = true)
@Table(uniqueConstraints = @UniqueConstraint(columnNames = "email"), name = "library_user")
public class AppUser {
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@NotEmpty
@Column(name = "first_name")
private String firstName;
@NotEmpty
@Column(name = "last_name")
private String lastName;
@NotEmpty
@Email
@Column(name = "email")
private String email;
@Column(name = "number")
private String telephoneNumber;
@NotEmpty
@Column(name = "password")
private String password;
@CreationTimestamp
@Column(name = "create_time")
private Timestamp registrationDate;
@Column(name = "enabled")
private boolean enabled;
@ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
@JoinTable(
name = "user_roles",
joinColumns = @JoinColumn(name = "user_id"),
inverseJoinColumns = @JoinColumn(name = "role_id")
)
private Collection<UserRole> roles = new HashSet<>();
@EqualsAndHashCode.Exclude
@OnDelete(action = OnDeleteAction.CASCADE)
@OneToMany(mappedBy = "book", fetch = FetchType.EAGER)
private Set<BookCard> bookCards = new HashSet<>();
public void addBookCard(BookCard bookCard) {
bookCards.add(bookCard);
bookCard.setUser(this);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
AppUser user = (AppUser) o;
return Objects.equals(id, user.id) &&
Objects.equals(firstName, user.firstName) &&
Objects.equals(lastName, user.lastName) &&
Objects.equals(email, user.email) &&
Objects.equals(password, user.password);
}
@Override
public int hashCode() {
return Objects.hash(id, firstName, lastName, email, password);
}
public void setRoles(Collection<UserRole> roles) {
this.roles = roles;
}
@Override
public String toString() {
return "AppUser{"
"id=" id
", firstName='" firstName '\''
", lastName='" lastName '\''
", email='" email '\''
", telephoneNumber='" telephoneNumber '\''
", password='" password '\''
", registrationDate=" registrationDate
", enabled=" enabled
", roles=" roles
", bookCards=" bookCards
'}';
}
}
@Getter
@Setter
@Entity
@NoArgsConstructor
@AllArgsConstructor
@Builder(toBuilder = true)
@Table(name = "verification_token")
public class VerificationToken {
private static final int EXPIRATION = 60 * 24;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String token;
@OneToOne
@JoinColumn(nullable = false, name = "user_id", foreignKey = @ForeignKey(name = "FK_VERIFY_USER"))
private AppUser user;
private Date expiryDate;
public VerificationToken(final String token, final AppUser user) {
super();
this.token = token;
this.user = user;
this.expiryDate = calculateExpiryDate();
}
public Long getId() {
return id;
}
public String getToken() {
return token;
}
public void setToken(final String token) {
this.token = token;
}
public AppUser getUser() {
return user;
}
public void setUser(final AppUser user) {
this.user = user;
}
public Date getExpiryDate() {
return expiryDate;
}
public void setExpiryDate(final Date expiryDate) {
this.expiryDate = expiryDate;
}
private Date calculateExpiryDate() {
final Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(new Date().getTime());
cal.add(Calendar.MINUTE, VerificationToken.EXPIRATION);
return new Date(cal.getTime().getTime());
}
public void updateToken(final String token) {
this.token = token;
this.expiryDate = calculateExpiryDate();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
VerificationToken that = (VerificationToken) o;
return Objects.equals(token, that.token) && Objects.equals(user, that.user) && Objects.equals(expiryDate, that.expiryDate);
}
@Override
public int hashCode() {
return Objects.hash(token, user, expiryDate);
}
}
我嘗試了cascade type的不同變體,例如 (cascade = CascadeType.ALL, cascade = CascadeType.MERGE)。此外,正如您在 VerificationToken 類中看到的那樣,我嘗試洗掉任何型別的級聯,但這對我沒有幫助。我不知道如何解決這個問題
uj5u.com熱心網友回復:
您可能會在事務中加載用戶,然后創建并發布您設定用戶的事件。在您創建和發布事件的事務方法結束后,事務也結束并且AppUser物體與持久性背景關系分離。為了像您剛剛從存盤庫或 中獲得的物體一樣進一步使用它EntityManager,您需要將其“重新附加”到背景關系中。您可以在Hibernate 檔案或此Baldung 文章中閱讀有關 Hibernate 物體生命周期的更多資訊。
或者,您可以AppUser在要保留VerificationToken. 例如,您可以將方法更改為:
private final AppUserRepository appUserRepository;
@Override
@Transactional
public void createVerificationTokenForUser(final String userId, final String token) {
final AppUser user = appUserRepository.findById(userId).orElseThrow();
final VerificationToken myToken = new VerificationToken(token, user);
verificationTokenRepository.save(myToken);
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/478682.html
標籤:春天 PostgreSQL 弹簧靴 休眠 一对一
