Java Hibernate对象引用具有一对多关系的未保存的临时实例
我有两个域用户和权限。这两个域使用第三个表具有一对多单向关系 用户域:Java Hibernate对象引用具有一对多关系的未保存的临时实例,java,spring,hibernate,spring-data-jpa,Java,Spring,Hibernate,Spring Data Jpa,我有两个域用户和权限。这两个域使用第三个表具有一对多单向关系 用户域: @Entity @Table(name = "USER") @ToString @EqualsAndHashCode(callSuper=false) @Data public class User extends AbstractEntity implements UserDetails { private static final long serialVersionUID = 2507177602107639
@Entity
@Table(name = "USER")
@ToString
@EqualsAndHashCode(callSuper=false)
@Data
public class User extends AbstractEntity implements UserDetails {
private static final long serialVersionUID = 2507177602107639240L;
@OneToMany(fetch = FetchType.EAGER)
@JoinTable
List<Authority> authorities;
@Column(nullable = false)
String password;
@Column(nullable = false, unique = true)
String username;
boolean accountNonExpired = true;
boolean accountNonLocked = true;
boolean credentialsNonExpired = true;
boolean enabled = false;
@OneToOne(cascade = CascadeType.ALL)
UserDetail userDetail;
public User(String username, String password) {
this.username = username;
this.password = password;
}
public User() {
}
}
@Entity
@EqualsAndHashCode(callSuper=false)
@ToString
@Data
public class Authority extends AbstractEntity implements GrantedAuthority {
private static final long serialVersionUID = -3506805573570762491L;
@Setter(AccessLevel.NONE)
@Column(unique = true, nullable = false)
String authority;
@SuppressWarnings("unused")
private Authority(){}
public Authority(String authority) {
this.authority = authority;
}
public Authority(Role role) {
this(role.toString());
}
}
org.springframework.dao.InvalidDataAccessApiUsageException: org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: com.legacybuy.model.Authority; nested exception is java.lang.IllegalStateException: org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: com.legacybuy.model.Authority
at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:381) ~[spring-orm-4.3.6.RELEASE.jar:4.3.6.RELEASE]
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:246) ~[spring-orm-4.3.6.RELEASE.jar:4.3.6.RELEASE]
at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:521) ~[spring-orm-4.3.6.RELEASE.jar:4.3.6.RELEASE]
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:761) ~[spring-tx-4.3.6.RELEASE.jar:4.3.6.RELEASE]
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:730) ~[spring-tx-4.3.6.RELEASE.jar:4.3.6.RELEASE]
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:504) ~[spring-tx-4.3.6.RELEASE.jar:4.3.6.RELEASE]
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:292) ~[spring-tx-4.3.6.RELEASE.jar:4.3.6.RELEASE]
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96) ~[spring-tx-4.3.6.RELEASE.jar:4.3.6.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.6.RELEASE.jar:4.3.6.RELEASE]
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136) ~[spring-tx-4.3.6.RELEASE.jar:4.3.6.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.6.RELEASE.jar:4.3.6.RELEASE]
at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:133) ~[spring-data-jpa-1.11.0.RELEASE.jar:na]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.6.RELEASE.jar:4.3.6.RELEASE]
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92) ~[spring-aop-4.3.6.RELEASE.jar:4.3.6.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.6.RELEASE.jar:4.3.6.RELEASE]
at org.springframework.data.repository.core.support.SurroundingTransactionDetectorMethodInterceptor.invoke(SurroundingTransactionDetectorMethodInterceptor.java:57) ~[spring-data-commons-1.13.0.RELEASE.jar:na]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.6.RELEASE.jar:4.3.6.RELEASE]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213) ~[spring-aop-4.3.6.RELEASE.jar:4.3.6.RELEASE]
at com.sun.proxy.$Proxy99.save(Unknown Source) ~[na:na]
at com.legacybuy.config.BootStrap.createAdminUser(BootStrap.java:79) ~[classes/:na]
at com.legacybuy.config.BootStrap.onApplicationEvent(BootStrap.java:38) ~[classes/:na]
at com.legacybuy.config.BootStrap.onApplicationEvent(BootStrap.java:1) ~[classes/:na]
at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:167) ~[spring-context-4.3.6.RELEASE.jar:4.3.6.RELEASE]
at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:139) ~[spring-context-4.3.6.RELEASE.jar:4.3.6.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:383) ~[spring-context-4.3.6.RELEASE.jar:4.3.6.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:337) ~[spring-context-4.3.6.RELEASE.jar:4.3.6.RELEASE]
at org.springframework.boot.context.event.EventPublishingRunListener.finished(EventPublishingRunListener.java:100) ~[spring-boot-1.5.1.RELEASE.jar:1.5.1.RELEASE]
at org.springframework.boot.SpringApplicationRunListeners.callFinishedListener(SpringApplicationRunListeners.java:79) ~[spring-boot-1.5.1.RELEASE.jar:1.5.1.RELEASE]
at org.springframework.boot.SpringApplicationRunListeners.finished(SpringApplicationRunListeners.java:72) ~[spring-boot-1.5.1.RELEASE.jar:1.5.1.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:316) [spring-boot-1.5.1.RELEASE.jar:1.5.1.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1162) [spring-boot-1.5.1.RELEASE.jar:1.5.1.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1151) [spring-boot-1.5.1.RELEASE.jar:1.5.1.RELEASE]
at com.legacybuy.Application.main(Application.java:12) [classes/:na]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_92]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_92]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_92]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_92]
at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49) [spring-boot-devtools-1.5.1.RELEASE.jar:1.5.1.RELEASE]
Caused by: java.lang.IllegalStateException: org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: com.legacybuy.model.Authority
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1689) ~[hibernate-entitymanager-5.0.11.Final.jar:5.0.11.Final]
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1602) ~[hibernate-entitymanager-5.0.11.Final.jar:5.0.11.Final]
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1608) ~[hibernate-entitymanager-5.0.11.Final.jar:5.0.11.Final]
at org.hibernate.jpa.internal.EntityManagerImpl$CallbackExceptionMapperImpl.mapManagedFlushFailure(EntityManagerImpl.java:235) ~[hibernate-entitymanager-5.0.11.Final.jar:5.0.11.Final]
at org.hibernate.internal.SessionImpl.flushBeforeTransactionCompletion(SessionImpl.java:2967) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final]
at org.hibernate.internal.SessionImpl.beforeTransactionCompletion(SessionImpl.java:2339) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final]
at org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl.beforeTransactionCompletion(JdbcCoordinatorImpl.java:485) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final]
at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.beforeCompletionCallback(JdbcResourceLocalTransactionCoordinatorImpl.java:147) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final]
at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.access$100(JdbcResourceLocalTransactionCoordinatorImpl.java:38) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final]
at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl$TransactionDriverControlImpl.commit(JdbcResourceLocalTransactionCoordinatorImpl.java:231) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final]
at org.hibernate.engine.transaction.internal.TransactionImpl.commit(TransactionImpl.java:65) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final]
at org.hibernate.jpa.internal.TransactionImpl.commit(TransactionImpl.java:61) ~[hibernate-entitymanager-5.0.11.Final.jar:5.0.11.Final]
at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:517) ~[spring-orm-4.3.6.RELEASE.jar:4.3.6.RELEASE]
... 35 common frames omitted
Caused by: org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: com.legacybuy.model.Authority
at org.hibernate.engine.internal.ForeignKeys.getEntityIdentifierIfNotUnsaved(ForeignKeys.java:279) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final]
at org.hibernate.type.EntityType.getIdentifier(EntityType.java:462) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final]
at org.hibernate.type.ManyToOneType.nullSafeSet(ManyToOneType.java:144) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final]
at org.hibernate.persister.collection.AbstractCollectionPersister.writeElement(AbstractCollectionPersister.java:894) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final]
at org.hibernate.persister.collection.AbstractCollectionPersister.recreate(AbstractCollectionPersister.java:1313) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final]
at org.hibernate.action.internal.CollectionRecreateAction.execute(CollectionRecreateAction.java:50) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final]
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:582) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final]
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:456) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final]
at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:337) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final]
at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:39) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final]
at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1282) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final]
at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:465) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final]
at org.hibernate.internal.SessionImpl.flushBeforeTransactionCompletion(SessionImpl.java:2963) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final]
... 43 common frames omitted
在应用程序启动时,我将所有可用权限保存在db中
mysql> select * from authority;
+----+------------+--------------+--------------+------------+------------------+
| id | created_by | date_created | last_updated | updated_by | authority |
+----+------------+--------------+--------------+------------+------------------+
| 1 | NULL | NULL | NULL | NULL | ROLE_CUSTOMER |
| 2 | NULL | NULL | NULL | NULL | ROLE_ADMIN_READ |
| 3 | NULL | NULL | NULL | NULL | ROLE_ADMIN_WRITE |
+----+------------+--------------+--------------+------------+------------------+
3 rows in set (0.00 sec)
现在我正试图用权限保存用户
@Transactional
private void createAdminUser(){
String username = "admin";
User user = userRepository.findByUsername(username);
if(user == null){
List<Authority> authorities = authorityRepository.findAllByAuthority(Role.ROLE_ADMIN_WRITE.toString());
user = new User(username, "admin");
user.setAuthorities(authorities);
userRepository.save(user);
}
}
这可能是因为您的
findbyaauthority
调用参与了它自己的事务,返回了一个分离的/暂时的Authority
实例,并且您稍后将其委托给用户的存储库,但它失败了,因为Authority
是暂时的
处理此问题的最佳方法是确保您的事务边界发生在方法调用堆栈中的较早位置,以便两个存储库操作都发生在相同的事务上下文中
@Transactional
public void createAdminUser(String userName) {
// your logic here
}
在这种情况下,另一个黑客解决方案是更改列表
属性的映射,以便将操作从用户
级联到权限
@OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@JoinTable
private List<Authority> authorities;
@OneToMany(fetch=FetchType.EAGER,cascade=CascadeType.ALL)
@可接合
私人名单管理机构;
显然,最好的方法是确保所有存储库调用都参与到更广泛的事务范围中,这样就不会有实体作为业务用例的一部分分离。我的猜测是,在用户构造函数中,您调用的是AuthorityUtil,它会返回分离的权限实体列表
public User(String username, String password, Authority authority) {
this.username = username;
this.password = password;
this.authorities = AuthorityUtil.getAsList(authority);
}
请确保方法getAsList没有实例化新的权限类。我添加了@Transactional Annotion,但它不起作用。cascade=CascadeType。由于角色\u ADMIN\u WRITE已经存在,所有这些都不适用于我。我能够重现错误。见下面我的答案。在代码中找不到n-m关系。@PeterRader我想这是\@OneToMany(fetch=FetchType.EAGER)\@JoinTable列表权限@DeepakAgrawal啊,好吧,但它是一个1-n而不是一个n-m像标题说的那样。正确的
多对多
没有关系表的实体。这一点很重要,因为一些特殊规则仅适用于关系表,如组合唯一主键、不为null、仅两个字段、仅foregin键、一侧删除级联、一侧更新-cascade@PeterRader刚刚更新了这个问题。我已经删除了AuthorityUtil,但它仍然无法工作。看看更新的createAdminUser()方法。在这种情况下,我同意@Naros。这与在另一个会话中获得的权限实体有关,即分离的对象。仅仅因为使用事务性注释服务方法,并不意味着每次调用都获得相同的会话,除非会话上下文范围是JTA。你的课程范围是什么?您正在设置hibernate.current\u session\u context\u类吗?你在用JTA吗?