2015-12-20 156 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关系,并将其存储在关系表中。

此致敬礼!