spring-data-jpa JPA延迟初始化错误,具有@OneToMany @EmbeddedId

t30tvxxf  于 2022-11-10  发布在  Spring
关注(0)|答案(1)|浏览(118)

在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(),也会引发延迟初始化错误。

2fjabf4q

2fjabf4q1#

在父子关系中,您没有从子端声明任何父引用。
在父端(UserAccount)中,您声明如下

@OneToMany(mappedBy ="user"....

这意味着您的子端(SecureToken)没有名为user的属性。
为了摆脱这种状况,
首先,你需要在SecureToken/SecureTokenId中声明user。根据你的定义,你在SecureTokenId中声明了user_id,而不是在SecureTokenId中声明了user

...
public class SecureTokenId ... {

    ... 

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "user_id")
    private UserAccount user;

    ...
}

然后在UserAccount中声明@OneToMany,如下所示

@OneToMany(mappedBy ="tokenId.user"...
private Set<SecureToken> tokens;

相关问题