我已经建立了一个简单的多对多关系帐户:Hibernate角色,但是当我在添加角色后尝试在单元测试中保存帐户时,出现UnsupportedOperationException异常:
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)
这是怎么了 我的实体设置有问题吗?或者这是hibernate或JPA限制,迫使我将m:m关系分解为3:n关系,也为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的任何想法?
是因为你
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文档中进行记录),但是这可能是一个有效的解决方法。