2011-09-15 146 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方法。