在Sprinboot/JPA中,我定义了一个具有一对五关联的实体,如下所示:
@Entity
@Table(name = "useraccount", catalog = "useraccount")
public class UserAccount implements Serializable
{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
//other stuff...
@OneToMany(mappedBy ="tokenId.user", cascade = {CascadeType.REMOVE, CascadeType.MERGE, CascadeType.REFRESH}, orphanRemoval =true, fetch=FetchType.LAZY)
private Set<SecureToken> tokens = new HashSet<>();
public Set<SecureToken> getTokens()
{
return this.tokens;
}
//other getter and setter
}
安全令牌实体:
@Entity
@Table(name = "secureToken", catalog = "useraccount")
public class SecureToken implements Serializable
{
@EmbeddedId
public SecureTokenId tokenId= new SecureTokenId();
@Column(unique = true)
private String token;
private Timestamp isConsumed;
@CreationTimestamp
@Column(updatable = false)
private Timestamp timestamp;
@Column(updatable = false)
@Basic(optional = false)
private Timestamp expireAt;
@MapsId("user_id")
@JoinColumn(name = "user_id", referencedColumnName ="id")
@ManyToOne
private UserAccount user;
public SecureToken(UserAccount user, String token, String tokenType, Timestamp timestamp, Timestamp expire)
{
super();
this.token=token;
this.tokenId.setTokenType(tokenType);
this.tokenId.setUser(user);
this.timestamp=timestamp;
this.expireAt=expire;
this.isExpired=false;
}
}
安全令牌ID:
@Embeddable
public class SecureTokenId implements Serializable
{
@Column(name="tokenType")
private String tokenType;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private UserAccount user;
public SecureTokenId()
{
super();
}
public SecureTokenId(String tokenType)
{
//this.user_id=user_id;
this.tokenType=tokenType;
}
@Override
public boolean equals(Object o)
{
if (o == null || getClass() != o.getClass())
return false;
SecureTokenId that = (SecureTokenId) o;
return Objects.equals(this.tokenType, that.tokenType) &&
Objects.equals(this.user.getId(), that.user.getId());
}
@Override
public int hashCode() {
return Objects.hash(tokenType, this.user.getId());
}
public void setTokenType(String tokenType)
{
this.tokenType=tokenType;
}
public String getTokenType()
{
return this.tokenType;
}
public void setUser(UserAccount user)
{
this.user=user;
}
public UserAccount getUser()
{
return this.user;
}
public Long getTokenId()
{
return this.user.getId();
}
}
但是调用实体UserAccount的方法getToken()会得到著名的“LazyInitializationException”。我通常使用Hibernate.initialize,但是使用这种配置我无法摆脱这个问题。
这就是我如何在一个@Service类中创建一个标记的SecureTokenService。
@Override
@Transactional
public SecureToken generateToken(UserAccount user, String tokenType)
{
byte[] random = new byte[64];
new SecureRandom().nextBytes(random);
Timestamp timestamp = java.sql.Timestamp.valueOf(LocalDateTime.now());
LocalDateTime expire= LocalDateTime.now().plusHours(12);
SecureToken token = new SecureToken(new SecureTokenId(user, tokenType),Base64.encodeBase64URLSafeString(random),
timestamp, Timestamp.valueOf(expire));
return token;
}
然后在UserService类中(标注为@Service),我尝试创建一个令牌:
SecureToken token = secureTokenService.generateToken(user, type);
secureTokenService.save(token);
user.addSecureToken(token); //Error
this.save(user)
当我试图将令牌与用户关联时,抛出了错误。如果没有该语句,应用程序看起来可以工作,但即使在www.example.com中使用“spring.jpa.open-in-view = false”application.properties调用user.getTokens(),也会引发延迟初始化错误。
1条答案
按热度按时间2fjabf4q1#
在父子关系中,您没有从子端声明任何父引用。
在父端(
UserAccount
)中,您声明如下这意味着您的子端(
SecureToken
)没有名为user
的属性。为了摆脱这种状况,
首先,你需要在
SecureToken
/SecureTokenId
中声明user
。根据你的定义,你在SecureTokenId
中声明了user_id
,而不是在SecureTokenId
中声明了user
。然后在
UserAccount
中声明@OneToMany
,如下所示