2011-09-15 190 views
21

我已經設置了一個簡單的多對多關係帳戶:與Hibernate角色,但是當我嘗試在單元測試中保存帳戶之後有其角色增加我得到一個UnsupportedOperationException:UnsupportedOperationException合併保存與休眠和JPA的多對多關係

java.lang.UnsupportedOperationException 
    at java.util.AbstractList.remove(AbstractList.java:144) 
    at java.util.AbstractList$Itr.remove(AbstractList.java:360) 
    at java.util.AbstractList.removeRange(AbstractList.java:559) 
    at java.util.AbstractList.clear(AbstractList.java:217) 
    at org.hibernate.type.CollectionType.replaceElements(CollectionType.java:502) 
    at org.hibernate.type.CollectionType.replace(CollectionType.java:582) 
    at org.hibernate.type.TypeHelper.replace(TypeHelper.java:178) 
    at org.hibernate.event.def.DefaultMergeEventListener.copyValues(DefaultMergeEventListener.java:563) 
    at org.hibernate.event.def.DefaultMergeEventListener.entityIsPersistent(DefaultMergeEventListener.java:288) 
    at org.hibernate.event.def.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:261) 
    at org.hibernate.event.def.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:84) 
    at org.hibernate.impl.SessionImpl.fireMerge(SessionImpl.java:867) 
    at org.hibernate.impl.SessionImpl.merge(SessionImpl.java:851) 
    at org.hibernate.impl.SessionImpl.merge(SessionImpl.java:855) 
    at org.hibernate.ejb.AbstractEntityManagerImpl.merge(AbstractEntityManagerImpl.java:686) 
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) 
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) 
    at java.lang.reflect.Method.invoke(Method.java:597) 
    at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:240) 
    at $Proxy33.merge(Unknown Source) 
    at org.springframework.data.jpa.repository.support.SimpleJpaRepository.save(SimpleJpaRepository.java:360) 
    at ....JpaProvider.save(JpaProvider.java:161) 
    at ....DataModelTest.testAccountRole(DataModelTest.java:47) 
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) 
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) 
    at java.lang.reflect.Method.invoke(Method.java:597) 
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44) 
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15) 
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41) 
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20) 
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28) 
    at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:74) 
    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:82) 
    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72) 
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:240) 
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:49) 
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193) 
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52) 
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191) 
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42) 
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184) 
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61) 
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70) 
    at org.junit.runners.ParentRunner.run(ParentRunner.java:236) 
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:180) 
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50) 
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38) 
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467) 
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683) 
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390) 
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197) 

這是怎麼回事?我的實體設置是否有問題,或者這是休眠或JPA限制,迫使我將m:m關係拆分爲3:1關係建模m:n關係表(我想避免它,因爲它沒有任何關係附加信息)。我已經在我的原型中建模了其他1:n實體,並且目前看起來效果很好......

這是我的設置,任何想法可能是有缺陷的都會感激。

實體(簡體):

@Entity 
@Table(name="account") 
public class Account extends AbstractPersistable<Long> { 

    private static final long serialVersionUID = 627519641892468240L; 

    private String username; 


    @ManyToMany 
    @JoinTable(name = "account_roles", 
       joinColumns = { @JoinColumn(name = "account_id")}, 
       inverseJoinColumns={@JoinColumn(name="role_id")}) 
    private List<Role> roles; 


    public List<Role> getRoles() { 
     return roles; 
    } 
    public void setRoles(List<Role> roles) { 
     this.roles = roles; 
    } 



    @Entity 
    @Table(name="role") 
    public class Role extends AbstractPersistable<Long> { 

     private static final long serialVersionUID = 8127092070228048914L; 

     private String name; 

     @ManyToMany 
     @JoinTable(name = "account_roles", 
        joinColumns={@JoinColumn(name="role_id")}, 
        inverseJoinColumns={@JoinColumn(name="account_id")}) 
     private List<Account> accounts; 


     public List<Account> getAccounts() { 
      return accounts; 
     } 

     public void setAccounts(List<Account> accounts) { 
      this.accounts = accounts; 
     } 

單元測試:

@TransactionConfiguration 
@ContextConfiguration({"classpath:dw-security-context-test.xml"}) 
@Transactional 
@RunWith(SpringJUnit4ClassRunner.class) 
public class DataModelTest { 

    @Inject 
    private AccountProvider accountProvider;  

    @Inject 
    private RoleProvider roleProvider; 

    @Before 
    public void mockAccountRolePermission(){ 
     Account account = MockAccount.getSavedInstance(accountProvider); 
     Role role = MockRole.getSavedInstance(roleProvider); 
    } 

    @Test 
    public void testAccountRole(){  
     Account returnedAccount = accountProvider.findAll().get(0); 
     returnedAccount.setRoles(Arrays.asList(roleProvider.findAll().get(0))); 
     accountProvider.save(returnedAccount); 

    } 
} 

MockAccount(同爲MockRole):

public class MockAccount { 

    public static Account getInstance(){ 
     Account account = new Account(); 
     account.setUsername(RandomData.rndStr("userName-", 5)); 
     return account; 
    } 

    public static Account getSavedInstance(AccountProvider accountProvider){ 
     Account account = getInstance(); 
     accountProvider.save(account); 
     return account; 
    } 

} 

最後提供者:

@Repository 
public class AccountProvider extends JpaProvider<Account, Long> { 

} 

其中JPAProvider只是包裝了很多JPARepository方法(至少就因爲它是重要的,這種情況下):

public abstract class JpaProvider<T extends Object, ID extends Serializable> implements JpaRepository<T, ID>, JpaSpecificationExecutor<T>, QueryDslPredicateExecutor<T> { 
... 
} 

爲什麼保存可能是一個UnsupportedOperation什麼想法?

回答

42

這是因爲你的

Arrays.asList(roleProvider.findAll().get(0)) 

這將創建一個不可修改的列表(事實上,不可調整大小的列表)。 Hibernate似乎期待一個可修改的列表。嘗試使用這個:

public void testAccountRole(){  
    Account returnedAccount = accountProvider.findAll().get(0); 

    List<Role> list = new ArrayList<Role>(); 
    list.add(roleProvider.findAll().get(0));  
    returnedAccount.setRoles(list); 

    accountProvider.save(returnedAccount); 
} 

該解決方案將無法解釋到底爲什麼你得到了其他異常(可能在Hibernate文檔記錄在案),但它可能是一個有效的解決方法。

+0

嘿,感謝您的即時回覆!這一刻我正在把我的頭撞在桌子上!當我的同事看着我應該說「你爲什麼不這樣做......」? ;)啊,再次工作,讓我開心! – Pete

0

Hibernate的持久變體Collection試圖委託給一個抽象基類(PersistenceBag),該類沒有實現add方法。