2017-02-18 16 views
0

我有兩個域的用戶和權限。這兩個域使用第三個表具有一對多單向關係。Hibernate的對象引用一個未保存的瞬態的實例很多關係

用戶域:

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

在應用程序開始我堅持所有可用當局分貝。

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

現在我越來越喜歡例外權限不保留。

錯誤日誌:

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 
+0

我能夠重現錯誤。請參閱下面的答案。 – raminr

+0

無法找到代碼中的N-M的關係。 –

+0

@PeterRader我認爲這是 \ @ OneToMany(fetch = FetchType.EAGER) \ @JoinTable List authorities; –

回答

0

這可能是因爲你的findByAuthority電話在自己的事務參與,返回分離/短暫Authority實例,你以後委託給用戶的存儲庫,它失敗,因爲Authority是短暫的。

處理此問題的最佳方法是確保您的事務邊界在方法調用堆棧中較早發生,以便兩個存儲庫操作都在相同的事務上下文中進行。

@Transactional 
public void createAdminUser(String userName) { 
    // your logic here 
} 

另一個這裏砍解決方案在這種情況下會改變映射爲您List<Authority>屬性,使之級聯操作從UserAuthority

@OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL) 
@JoinTable 
private List<Authority> authorities; 

顯然,最好的辦法是確保所有的庫調用參與更廣泛的交易範圍,使任何單位變得作爲業務用例的一部分分離。

+0

我已經添加@Transactional annototion,但它不起作用。 –

+0

級聯= CascadeType.ALL不適用於我,因爲ROLE_ADMIN_WRITE已經存在。 –

0

我的猜測是,在您的用戶構造,您呼叫AuthorityUtil並返回超脫權威機構的名單。

public User(String username, String password, Authority authority) { 
    this.username = username; 
    this.password = password; 
    this.authorities = AuthorityUtil.getAsList(authority); 
} 

確保getAsList方法沒有實例化新的Authority類。

+0

我已經刪除了AuthorityUtil,但它仍然不起作用。看看更新的一個createAdminUser()方法。 –

+0

在這種情況下,我同意@Naros。這與另一場會議中獲得的管理局實體,即分離的對象有關。僅僅因爲你用Transactional註釋了服務方法,並不意味着你對每個調用都會得到相同的會話,除非會話上下文範圍是JTA。你的會議的範圍是什麼?你在設置hibernate.current_session_context_class嗎?你在使用JTA嗎? – raminr

相關問題