@OneToMany JPA注释会导致不必要的sql更新,并且不会级联

时间:2014-04-16 11:51:49

标签: java spring hibernate jpa cascade

我正在将我的应用从基于xml的映射迁移到基于JPA注释的映射。但这只是在一个简单的情况下失败。例如,我有以下3个实体:

@Entitiy
public class UserAccount {

     @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
     @JoinColumn(name = "user_account_id", nullable = false, 
                 insertable = true, updatable = false)
     @NotNull
     private Set<Authority> authorities;

     @OneToOne(cascade = CascadeType.ALL)
     @JoinColumn(name="USER_ID", nullable = false)

     ... get and setter
}

@Entity
public class Authority {

    @Column
    private String authority;

    ... get and setter
}

@Entity
public class User {
    ... some properties
}

如果我创建一个新的UserAccount实例并尝试保存它,我得到一个例外,因为没有保存Authority实例,这意味着它没有像预期的那样级联。

org.springframework.dao.InvalidDataAccessApiUsageException: 
org.hibernate.TransientObjectException: object references an unsaved transient 
instance - save the transient instance before flushing: Authority ...

这种情况发生了,因为hibernate尝试更新权限行(之前没有插入):

Hibernate: 
    insert into users ...

Hibernate: 
    insert into user_accounts ...

Hibernate: 
    update authorities set user_account_id=? where id=?

我使用以下服务来保存UserAccount实例。

@Service
public class UserAccountService {

    @Resource
    private UserAccountRepository userAccountRepository;

    @Transactional(propagation = Propagation.REQUIRED, readOnly = false)
    public void save(UserAccount userAccount) {

       userAccountRepository.save(userAccount);
    }

}

奇怪的是,User实例将被保存,因此OneToOne映射级联(如果我删除了UserAccount.authorities属性)。但这适用于基于xml的映射(并且没有JPA)。我最终调用了一个单独的方法来存储Authoriy实例。

@Service
public class UserAccountService {

    @Resource
    private UserAccountRepository userAccountRepository;
    @Resource
    private AuthorityRepository authorityRepository;

    @Transactional(propagation = Propagation.REQUIRED, readOnly = false)
    public void save(UserAccount userAccount) {

        userAccountRepository.save(userAccount);
        authorityRepository.save(userAccount.getAuthoritiesSet());
    }

} 

这意味着,我手动级联...但是,查看已执行的SQL语句,我可以看到,hibernate插入了权限实例,之后,它更新此行以设置UserAccount引用:

Hibernate: 
    insert into users ...

Hibernate: 
    insert into user_accounts ...

Hibernate: 
    insert into authorities (authority) values (?)

Hibernate: 
    update authorities set user_account_id=? where id=?

对我而言,这是一个性能问题,因为有许多权限,我不希望有20个插入,之后有20个更新。但我无法“删除”更新并告诉hibernate将user_account_id与权限一起设置。

因为我有许多其他具有OneToMany关系的对象,所以我对解决这个问题非常感兴趣并感谢您的帮助和想法。

Hibernate:4.3.4.Final, spring data jpa:1.5.1.RELEASE

祝你好运, 丹尼尔

3 个答案:

答案 0 :(得分:0)

...对象引用未保存的瞬态... ”表示您已将父对象映射到子对象,并且在保存父对象之前尚未保存子对象。

在您保存权限之前,您要保存父 UserAccount 以及子实体权限

您可以使用适当的Cascade为权限创建OneToMany关系。

 @OneToMany(cascade = CascadeType.ALL)
 private Set<Authority> authorities;

如果你有适当的级联,你不需要像下面那样单独保存权限。你可以删除它。

authorityRepository.save(userAccount.getAuthoritiesSet());

答案 1 :(得分:0)

我通过将hibernate版本降级到4.2.11.Final解决了这两个问题。我不知道,为什么这些问题出现在4.3.4.Final。也许这是所有4.3.x版本中的一个问题!

答案 2 :(得分:0)

我遇到了与ManyToOne映射非常相似的问题。 通过向版本字段添加默认值解决了问题。

@Version
@Column(name = "VERSION")
private Integer version = 1;