2011-09-15 31 views
21

Tôi đã thiết lập một tài khoản quan hệ nhiều-nhiều đơn giản: vai trò với Hibernate nhưng khi tôi cố gắng lưu một tài khoản trong một bài kiểm tra đơn vị sau khi nó có đã thêm vai trò của mình, tôi nhận được UnsupportedOperationException:UnsupportedOperationException hợp nhất tiết kiệm quan hệ nhiều-nhiều với hibernate và 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) 

Có gì xảy ra ở đây? Là thiết lập thực thể của tôi bị lỗi hoặc là một giới hạn ngủ đông hoặc JPA buộc tôi phải chia tách mối quan hệ m: m của tôi thành mối quan hệ 3: n mô hình hóa bảng quan hệ m: n (mà tôi muốn tránh vì nó không có bất kỳ thông tin thêm). Tôi đã mô hình hóa các thực thể 1: n khác trong nguyên mẫu của mình và dường như hoạt động tốt cho đến nay ...

Đây là thiết lập của tôi, bất kỳ suy nghĩ nào có thể bị lỗi.

Đối tượng (giản thể):

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

Unit Test:

@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 (tương tự cho 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; 
    } 

} 

Và cuối cùng các nhà cung cấp:

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

} 

nơi JPAProvider chỉ kết thúc tốt đẹp rất nhiều phương pháp JPARepository (ít nhất là như xa như điều quan trọng là trong trường hợp này):

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

Bất kỳ ý tưởng về lý do tiết kiệm có thể là một UnsupportedOperation?

Trả lời

42

Đó là vì

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

này tạo ra một danh sách unmodifiable (trên thực tế, một danh sách không thay đổi kích thước) của bạn. Hibernate dường như mong đợi một danh sách có thể sửa đổi. Hãy thử sử dụng này để thay thế:

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

Giải pháp này sẽ không giải thích lý do tại sao chính xác bạn có những ngoại lệ khác (có thể được ghi lại trong các tài liệu Hibernate), nhưng nó có thể là một cách giải quyết hợp lệ.

+0

Xin chào, cảm ơn bạn đã trả lời ngay! Tôi đang đập đầu vào bàn ngay lúc này! Tôi đã có nó chỉ như thế khi đồng nghiệp của tôi nhìn qua của tôi nên nói "tại sao bạn không làm điều đó như thế ..?" ;) Ah well .. làm việc một lần nữa, hạnh phúc cho tôi! – Pete

0

Biến thể liên tục của Hibernate của Collection trong câu hỏi cố gắng ủy nhiệm cho lớp cơ sở trừu tượng (PersistenceBag) không triển khai phương thức add.

Các vấn đề liên quan