Java 在JPA中使用可嵌入Id持久化OneToMany
授权服务基于(遗憾的是,它没有提供注册示例) 我拥有以下实体和服务: User.javaJava 在JPA中使用可嵌入Id持久化OneToMany,java,database,spring,hibernate,jpa,Java,Database,Spring,Hibernate,Jpa,授权服务基于(遗憾的是,它没有提供注册示例) 我拥有以下实体和服务: User.java package com.test.entity; import javax.persistence.*; import java.io.Serializable; import java.sql.Timestamp; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; @Entity @Ta
package com.test.entity;
import javax.persistence.*;
import java.io.Serializable;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
@Entity
@Table(name = "user")
public class User implements Serializable {
private static final long serialVersionUID = 1322120000551624359L;
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "username")
private String username;
@Column(name = "password")
private String password;
@Column(name = "first_name")
private String firstName;
@Column(name = "last_name")
private String lastName;
@Column(name = "activated")
private Boolean activated;
@Column(name = "activation_token")
private String activationToken;
@Column(name = "activation_token_exp")
private Timestamp activationTokenExpirationDate;
@Column(name = "reset_token")
private String resetToken;
@Column(name = "reset_token_exp")
private Timestamp resetTokenExpirationDate;
@Column(name = "created")
private LocalDateTime created;
@Column(name = "updated")
private LocalDateTime updated;
@OneToMany(cascade = CascadeType.ALL)
@JoinColumn(name = "user_id", referencedColumnName = "id")
private List<UserRole> roles = new ArrayList<>(0);
public User() { }
// getters and setters
}
@Entity
@Table(name = "user_role")
public class UserRole implements Serializable {
@Embeddable
public static class Id implements Serializable {
private static final long serialVersionUID = 1322120000551624359L;
@Column(name = "user_id")
protected Long userId;
@Enumerated(EnumType.STRING)
@Column(name = "role")
protected Role role;
public Id() { }
public Id(Long userId, Role role) {
this.userId = userId;
this.role = role;
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
Id id = (Id) o;
if (! userId.equals(id.userId))
return false;
return role == id.role;
}
@Override
public int hashCode() {
int result = userId.hashCode();
result = 31 * result + role.hashCode();
return result;
}
}
@EmbeddedId
Id id = new Id();
@Enumerated(EnumType.STRING)
@Column(name = "role", insertable = false, updatable = false)
protected Role role;
public UserRole() {
}
public UserRole(Role role) {
this.role = role;
}
public Role getRole() {
return role;
}
public void setRole(Role role) {
this.role = role;
}
}
@Override
public User registerUser(UserDTO userDto) {
Optional<User> existingUser = this.getByUsername(userDto.getUsername());
if (existingUser.isPresent()) {
throw new RegistrationException("User is already taken");
}
User newUser = new User();
newUser.setUsername(userDto.getUsername());
newUser.setPassword(encoder.encode(userDto.getPassword()));
newUser.setFirstName(userDto.getFirstName());
newUser.setLastName(userDto.getLastName());
newUser.setActivated(Boolean.FALSE);
newUser.setActivationToken(RandomUtil.generateActivationKey());
newUser.setActivationTokenExpirationDate(Timestamp.valueOf(LocalDateTime.now().plusSeconds(ACTIVATION_TOKEN_TTL)));
newUser.setCreated(LocalDateTime.now());
newUser.addRole(new UserRole(Role.MEMBER));
return userRepository.save(newUser);
}
UserService.java
package com.test.entity;
import javax.persistence.*;
import java.io.Serializable;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
@Entity
@Table(name = "user")
public class User implements Serializable {
private static final long serialVersionUID = 1322120000551624359L;
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "username")
private String username;
@Column(name = "password")
private String password;
@Column(name = "first_name")
private String firstName;
@Column(name = "last_name")
private String lastName;
@Column(name = "activated")
private Boolean activated;
@Column(name = "activation_token")
private String activationToken;
@Column(name = "activation_token_exp")
private Timestamp activationTokenExpirationDate;
@Column(name = "reset_token")
private String resetToken;
@Column(name = "reset_token_exp")
private Timestamp resetTokenExpirationDate;
@Column(name = "created")
private LocalDateTime created;
@Column(name = "updated")
private LocalDateTime updated;
@OneToMany(cascade = CascadeType.ALL)
@JoinColumn(name = "user_id", referencedColumnName = "id")
private List<UserRole> roles = new ArrayList<>(0);
public User() { }
// getters and setters
}
@Entity
@Table(name = "user_role")
public class UserRole implements Serializable {
@Embeddable
public static class Id implements Serializable {
private static final long serialVersionUID = 1322120000551624359L;
@Column(name = "user_id")
protected Long userId;
@Enumerated(EnumType.STRING)
@Column(name = "role")
protected Role role;
public Id() { }
public Id(Long userId, Role role) {
this.userId = userId;
this.role = role;
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
Id id = (Id) o;
if (! userId.equals(id.userId))
return false;
return role == id.role;
}
@Override
public int hashCode() {
int result = userId.hashCode();
result = 31 * result + role.hashCode();
return result;
}
}
@EmbeddedId
Id id = new Id();
@Enumerated(EnumType.STRING)
@Column(name = "role", insertable = false, updatable = false)
protected Role role;
public UserRole() {
}
public UserRole(Role role) {
this.role = role;
}
public Role getRole() {
return role;
}
public void setRole(Role role) {
this.role = role;
}
}
@Override
public User registerUser(UserDTO userDto) {
Optional<User> existingUser = this.getByUsername(userDto.getUsername());
if (existingUser.isPresent()) {
throw new RegistrationException("User is already taken");
}
User newUser = new User();
newUser.setUsername(userDto.getUsername());
newUser.setPassword(encoder.encode(userDto.getPassword()));
newUser.setFirstName(userDto.getFirstName());
newUser.setLastName(userDto.getLastName());
newUser.setActivated(Boolean.FALSE);
newUser.setActivationToken(RandomUtil.generateActivationKey());
newUser.setActivationTokenExpirationDate(Timestamp.valueOf(LocalDateTime.now().plusSeconds(ACTIVATION_TOKEN_TTL)));
newUser.setCreated(LocalDateTime.now());
newUser.addRole(new UserRole(Role.MEMBER));
return userRepository.save(newUser);
}
这是在将复合主键作为可嵌入主键的同时保持这种关系的正确方法吗
如果我避免使用UserRole设置realation,那么用户将正确持久化(没有角色)
DB
CREATE TABLE `user` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL,
`password` varchar(64) NOT NULL,
`first_name` varchar(20) DEFAULT NULL,
`last_name` varchar(20) DEFAULT NULL,
`activated` tinyint(1) NOT NULL DEFAULT '0',
`activation_token` varchar(50) DEFAULT NULL,
`activation_token_exp` timestamp NULL DEFAULT NULL,
`reset_token` varchar(50) DEFAULT NULL,
`reset_token_exp` timestamp NULL DEFAULT NULL,
`created` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=27 DEFAULT CHARSET=utf8;
CREATE TABLE `user_role` (
`user_id` bigint(20) unsigned NOT NULL,
`role` varchar(50) NOT NULL DEFAULT '',
PRIMARY KEY (`user_id`,`role`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
在
UserRole
中,角色映射两次:一次作为简单属性
@Enumerated(EnumType.STRING)
@Column(name = "role", insertable = false, updatable = false)
protected Role role;
再一次在嵌入的id中:
@Enumerated(EnumType.STRING)
@Column(name = "role")
protected Role role;
在调用userRepository.save(newUser)
时,您仅将简单属性UserRole.role
设置为指向非空角色。但是,由于简单属性被标记为insertable=false
,因此在INSERT
语句中会忽略它UserRole.id.role
依次设置为null
,这是INSERT
语句考虑的值。由于您已经为角色
列创建了非空约束,插入
语句失败
(请注意,DEFAULT'
仅在INSERT
子句的字段列表中不存在该列时才使用,此处不是这种情况)
简单地说,解决方案是在设置
User.role
时更新UserRole.id.role
的值 在UserRole
中,角色映射两次:一次作为简单属性
@Enumerated(EnumType.STRING)
@Column(name = "role", insertable = false, updatable = false)
protected Role role;
再一次在嵌入的id中:
@Enumerated(EnumType.STRING)
@Column(name = "role")
protected Role role;
在调用userRepository.save(newUser)
时,您仅将简单属性UserRole.role
设置为指向非空角色。但是,由于简单属性被标记为insertable=false
,因此在INSERT
语句中会忽略它UserRole.id.role
依次设置为null
,这是INSERT
语句考虑的值。由于您已经为角色
列创建了非空约束,插入
语句失败
(请注意,DEFAULT'
仅在INSERT
子句的字段列表中不存在该列时才使用,此处不是这种情况)
简单地说,解决方案是在设置
User.role
时更新UserRole.id.role
的值 您的UserRole
类映射不正确。您已经映射了两次Role
,一次通过EmbeddedId
映射,一次直接映射到UserRole
。您必须删除第二个UserRole
,该类将如下所示
@Entity
@Table(name = "user_role")
public class UserRole implements Serializable {
@Embeddable
public static class Id implements Serializable {
private static final long serialVersionUID = 1322120000551624359L;
@Column(name = "user_id")
protected Long userId;
@Enumerated(EnumType.STRING)
@Column(name = "role")
protected Role role;
public Id() {
}
public Id(Long userId, Role role) {
this.userId = userId;
this.role = role;
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
Id id = (Id) o;
if (!userId.equals(id.userId))
return false;
return role == id.role;
}
@Override
public int hashCode() {
int result = userId.hashCode();
result = 31 * result + role.hashCode();
return result;
}
}
@EmbeddedId
Id id;
public UserRole(Id id) {
super();
this.id = id;
}
public Id getId() {
return id;
}
public void setId(Id id) {
this.id = id;
}
}
另外,请注意,Id
不再初始化。请将注册表服务器更改为:
public User registerUser(String userName) {
User newUser = new User();
newUser.setUsername(userName);
newUser.setPassword("password");
newUser.setFirstName("name");
//set other fields.
userRepository.save(newUser);
newUser.addRole(new UserRole(new UserRole.Id(newUser.getId(), Role.MEMBER)))
userRepository.save(newUser);
return newUser;
}
当您为
user\u role
创建复合主键时,您只提供role而不提供user\u id,JPA会将user\u id抱怨为null。我们需要两者兼备 您的UserRole
类映射不正确。您已经映射了两次Role
,一次通过EmbeddedId
映射,一次直接映射到UserRole
。您必须删除第二个UserRole
,该类将如下所示
@Entity
@Table(name = "user_role")
public class UserRole implements Serializable {
@Embeddable
public static class Id implements Serializable {
private static final long serialVersionUID = 1322120000551624359L;
@Column(name = "user_id")
protected Long userId;
@Enumerated(EnumType.STRING)
@Column(name = "role")
protected Role role;
public Id() {
}
public Id(Long userId, Role role) {
this.userId = userId;
this.role = role;
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
Id id = (Id) o;
if (!userId.equals(id.userId))
return false;
return role == id.role;
}
@Override
public int hashCode() {
int result = userId.hashCode();
result = 31 * result + role.hashCode();
return result;
}
}
@EmbeddedId
Id id;
public UserRole(Id id) {
super();
this.id = id;
}
public Id getId() {
return id;
}
public void setId(Id id) {
this.id = id;
}
}
另外,请注意,Id
不再初始化。请将注册表服务器更改为:
public User registerUser(String userName) {
User newUser = new User();
newUser.setUsername(userName);
newUser.setPassword("password");
newUser.setFirstName("name");
//set other fields.
userRepository.save(newUser);
newUser.addRole(new UserRole(new UserRole.Id(newUser.getId(), Role.MEMBER)))
userRepository.save(newUser);
return newUser;
}
当您为user\u role
创建复合主键时,您只提供role而不提供user\u id,JPA会将user\u id抱怨为null。我们需要两者兼备