@OneToMany JPA annotation causes unnecessary sql update and doesn't cascade

冷暖自知 提交于 2019-12-11 09:33:50

问题


I am migrating my app from xml based mapping to JPA annotation based mapping. But this fails only in a simple case. For example, I've the following 3 entities:

@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
}

If I create a new UserAccount instance and try to save it, I got an exception, because the Authority instance wasn't saved, which means, it doesn't cascade as it supposed to do.

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

This happens, because hibernate tries to update the authority row (which wasn't inserted before):

Hibernate: 
    insert into users ...

Hibernate: 
    insert into user_accounts ...

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

I used the following service to save the UserAccount instance.

@Service
public class UserAccountService {

    @Resource
    private UserAccountRepository userAccountRepository;

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

       userAccountRepository.save(userAccount);
    }

}

The strange thing is, that the User instance will be saved, so the OneToOne mapping cascades (if I remove the UserAccount.authorities property). But this worked with the xml based mapping (and without JPA). I ended up in calling a separate method to store the Authoriy instances.

@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());
    }

} 

Which means, I cascade manually... However, looking into the executed SQL statements, I can see, that hibernate inserts the authority instance and after that, it updates this row to set the UserAccount reference:

Hibernate: 
    insert into users ...

Hibernate: 
    insert into user_accounts ...

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

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

And for me this is a performance issue, because there are many authorities and I do not want to have 20 inserts and after that 20 updates. But I was not able to "remove" the updates and tell hibernate to set the user_account_id together with the authority.

Because I've many other objects having OneToMany relations, I am very interested in resolving this problem and appreciate your help and thoughts.

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

Best regards, Daniel


回答1:


"...object references an unsaved transient..." means you have mapped a child object with a parent, and the child is not yet saved before you save a parent.

In your case you are saving the parent UserAccount along with child entity Authorities before saving Authorities.

You could create a OneToMany relation for authorities, with appropriate Cascade.

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

And if you have proper cascade, you need not have to save the authorities separately like below line. You could remove it.

authorityRepository.save(userAccount.getAuthoritiesSet());



回答2:


I solved both problems by downgrading the hibernate version to 4.2.11.Final. I don't know, why these problems occur in 4.3.4.Final. Maybe that's a problem in all 4.3.x versions!




回答3:


I had very similar issue with ManyToOne mapping. Solved problem by adding default value to version field.

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


来源:https://stackoverflow.com/questions/23108644/onetomany-jpa-annotation-causes-unnecessary-sql-update-and-doesnt-cascade

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!