2015-12-20 157 views
6

我有一個休眠多對多關係的問題:當我從我的設置中刪除一個項目時,它不會在我的數據庫中刪除。我知道有很多類似的問題,但是我沒有通過閱讀來解決我的問題。休眠多對多刪除關係

我已經爲它寫了一個JUnit測試用例。我的聯想是建築物和用戶之間:

@Test 
public void testBuildingManyToMany(){ 
    //Create 2 buildings 
    Building building = createBuilding("b1"); 
    Building building2 = createBuilding("b2"); 
    //Create 1 user 
    User user = createUser("u1"); 

    //Associate the 2 buildings to that user 
    user.getBuildings().add(building); 
    building.getUsers().add(user); 

    user.getBuildings().add(building2); 
    building2.getUsers().add(user); 

    userController.save(user); 
    user = userController.retrieve(user.getId()); 
    Assert.assertEquals(2, user.getBuildings().size());//Test OK 

    //Test 1: remove 1 building from the list 
    user.getBuildings().remove(building); 
    building.getUsers().remove(user); 
    userController.save(user); 

    //Test 2: clear and add 
    //user.getBuildings().clear(); 
    //user.getBuildings().add(building); 
    //userController.save(user); 
    //user = userController.retrieve(user.getId()); 
    //Assert.assertEquals(1, user.getBuildings().size()); 
} 

這是我得到的錯誤:

... 
Hibernate: insert into building_useraccount (userid, buildingid) values (?, ?) 
Hibernate: insert into building_useraccount (userid, buildingid) values (?, ?) 
Hibernate: delete from building_useraccount where userid=? and buildingid=? 
Hibernate: insert into building_useraccount (userid, buildingid) values (?, ?) 
4113 [main] WARN org.hibernate.util.JDBCExceptionReporter - SQL Error: 23505, SQLState: 23505 
4113 [main] ERROR org.hibernate.util.JDBCExceptionReporter - Unique index or primary key violation: "PRIMARY_KEY_23 ON PUBLIC.BUILDING_USERACCOUNT(BUILDINGID, USERID) VALUES (/* key:0 */ 201, 201)"; SQL statement: 
insert into building_useraccount (userid, buildingid) values (?, ?) [23505-176] 

當我評論「測試1」,並取消對「測試2」行,我去下面錯誤:

junit.framework.AssertionFailedError: 
Expected :1 
Actual :2 

這裏是我的hbm.xml類:

<hibernate-mapping default-lazy="true"> 
    <class name="my.model.pojo.Building" table="building"> 
    <cache usage="read-write" /> 
    <id name="id" column="id" type="java.lang.Long"> 
     <generator class="sequence"> 
      <param name="sequence">building_id_sequence</param> 
     </generator> 
    </id> 
    <property name="name" type="java.lang.String" column="name" not-null="true" /> 
    ... 
    <set name="users" cascade="none" lazy="true" inverse="true" table="building_useraccount"> 
     <key column="buildingid" /> 
     <many-to-many class="my.model.pojo.User" column="userid" /> 
    </set> 
</class> 
</hibernate-mapping> 

<hibernate-mapping default-lazy="true"> 
<class name="my.model.pojo.User" table="useraccount"> 
    <cache usage="read-write" /> 
    <id name="id" column="id" type="java.lang.Long"> 
     <generator class="sequence"> 
      <param name="sequence">useraccount_id_sequence</param> 
     </generator> 
    </id> 
    <property name="login" type="java.lang.String" column="login" not-null="true" unique="true" length="40" /> 

    ... 
    <set name="buildings" cascade="none" lazy="false" fetch="join" table="building_useraccount"> 
     <key column="userid" /> 
     <many-to-many class="my.model.pojo.Building" column="buildingid" /> 
    </set> 
</class> 
</hibernate-mapping> 

和類

public class User implements Serializable, Identifiable { 

private static final long serialVersionUID = 1L; 
private int hashCode; 

private Long id; 
private String login; 

private Set<Building> buildings = new HashSet<Building>(); 

public boolean equals(Object value) { 
    if (value == this) 
     return true; 
    if (value == null || !(value instanceof User)) 
     return false; 
    if (getId() != null && getId().equals(((User) value).getId())) 
     return true; 
    return super.equals(value); 
} 

public int hashCode() { 
    if (hashCode == 0) { 
     hashCode = (getId() == null) ? super.hashCode() : new HashCodeBuilder().append(getId()).toHashCode(); 
    } 
    return hashCode; 
} 

/* Getter/Setter ... */ 

public class BuildingBase implements Serializable, Identifiable { 

private static final long serialVersionUID = 1L; 
private int hashCode; 

private Long id; 
private String name; 

private Set<User> users = new HashSet<User>(); 

public boolean equals(Object value) { 
    if (value == this) 
     return true; 
    if (value == null || !(value instanceof Building)) 
     return false; 
    if (getId() != null && getId().equals(((Building) value).getId())) 
     return true; 
    return super.equals(value); 
} 

public int hashCode() { 
    if (hashCode == 0) { 
     hashCode = (getId() == null) ? super.hashCode() : new HashCodeBuilder().append(getId()).toHashCode(); 
    } 
    return hashCode; 
} 

/* Getter/Setter ... */ 

編輯:添加UserController的實施,對交易

@Transactional(readOnly = false, propagation = Propagation.REQUIRED) 
public User save(User user) throws ServiceException { 
    validate(user);//Validation stuffs 
    return userDAO.update(user); 
} 

的userDAO的:

public class UserDAOImpl extends HibernateDAOImpl<User> implements UserDAO { 
} 

而且HibernateDAOImpl:

public class HibernateDAOImpl<T> implements DAO<T> { 

    public T update(T entity) { 
     return executeAndCreateSessionIfNeeded(new HibernateAction<T>() { 
      @Override 
      public T execute(Session session) { 
       return (T) session.merge(entity); 
      } 
     }); 
    } 

    protected <E> E executeAndCreateSessionIfNeeded(HibernateAction<E> action) { 
     Session session = null; 
     try { 
      session = sessionFactory.getCurrentSession(); 
      return executeAction(action, session); 
     } finally { 
      if (session != null) { 
       session.close(); 
      } 
     } 
    } 

} 
+0

請張貼userController.save'方法'實現。另外,交易界限是什麼? –

+0

我添加了一些實現。該交易工作得很好,因爲它在代碼中的任何地方都能成功使用。還要注意清除建築物(使用user.getBuildings()。clear())也可以工作並清空我的多對多數據庫表!只是刪除是奇怪的不工作... – Asterius

回答

0

更改級聯屬性沒有固定我的問題。我最終決定通過爲中間表創建一個對象來處理多對多關係,並由我自己來管理它。這是更多的代碼,但爲我想實現的目標提供了一致的行爲。

1

爲什麼cascade="none"

您應該使用cascade="detached,merge,refresh,persist"(而不是刪除!)來更新集合中的刪除。

1

更換cascade='none'作者cascade='all'關於buildings關係的定義在用戶應該修復問題。

由於您要保存用戶,爲了還要更新數據庫中的多對多數據,您需要級聯對用戶關係的更改。

10

CascadeType.REMOVEdoesn't have sense for many-to-many associations因爲在雙方都設置時,它可能觸發父母和子女之間的鏈刪除並且回到父母。如果您只將其設置在家長一方,您可能會遇到問題,即刪除的孩子仍被其他家長引用。

引述Hibernate docs

It does not usually make sense to enable cascade on a many-to-one or many-to-many association. In fact the @ManyToOne and @ManyToMany don't even offer a orphanRemoval attribute. Cascading is often useful for one-to-one and one-to-many associations.

0

恐怕你在做什麼是不是真的與Hibernate是一個好主意,即使它是更常見的任務,你將與一個關係呢之一。實現你想要的就是使用級聯,但正如Vlad Mihalcea所說,這最終可能會刪除關係中的一個或另一個端點,而不僅僅是關係本身。

作爲一個適當的迴應,我會告訴你一個老師會說什麼?你真的有一個N:M的關係?你確定它本身沒有實體嗎? N:M關係很難找到,通常意味着建模是錯誤的。即使情況並非如此,你實際上有一個n:m,這應該留在模型中,不要忘記你正在使用ORM將ACTUAL模型鏈接到你的java模型,所以你實際上可以在Java中擁有一個實體1:n關係,並將其存儲在關係表中。

此致敬禮!