我有一个hibernate多对多关系的问题:当我从我的集合中删除一个项目时,它不会在我的数据库中删除.我知道有很多类似的问题,但我没有通过阅读来修复我的问题.
我已经为它编写了一个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,?) Hibernate: delete from building_useraccount where userid=? and buildingid=? Hibernate: insert into building_useraccount (userid,?) 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,?) [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(); } } } }
解决方法
CascadeType.REMOVE
doesn’t have sense for many-to-many associations因为,当设置在两侧时,它可以触发父母和孩子之间以及父母之间的链删除.如果您只在父级设置它,当一些其他父级仍引用删除子级时,您可能会遇到问题.
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.