[英]Hibernate many-to-many remove relation
我有一個休眠多對多關系的問題:當我從我的集合中刪除一個項目時,它並沒有從我的數據庫中刪除。 我知道有很多類似的問題,但我沒有通過閱讀來成功解決我的問題。
我已經為它編寫了一個 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);
}
用戶道:
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
對於many-to-many
關聯沒有意義,因為在兩側設置時,它可能會觸發父子之間的鏈刪除並返回父子。 如果你只在父端設置它,當刪除的孩子仍然被其他一些父母引用時,你可能會遇到問題。
引用Hibernate 文檔:
在多對一或多對多關聯上啟用級聯通常沒有意義。 事實上,@ManyToOne 和@ManyToMany 甚至不提供 orphanRemoval 屬性。 級聯通常對一對一和一對多關聯很有用。
為什么cascade="none"
?
您應該使用cascade="detached,merge,refresh,persist"
(不是刪除。)來更新集合中的刪除。
在用戶定義的buildings
關系上用cascade='all'
替換cascade='none'
應該可以解決問題。
由於您正在保存用戶,為了也更新數據庫中的多對多,您需要級聯用戶關系的更改。
恐怕你正在做的事情對於休眠來說並不是一個好主意,即使這是你對一段關系所做的更常見的任務之一。 實現你想要的方法是使用級聯,但正如 Vlad Mihalcea 所說,這最終可能會刪除關系的一端或另一端,而不僅僅是關系本身。
作為一個適當的回應,我會告訴你老師會說什么......你真的有一個:m關系嗎? 你確定它本身沒有實體嗎? N:M 關系很難找到,通常意味着建模是錯誤的。 即使情況並非如此,並且您實際上有一個 n:m,它也應該保留在模型中,永遠不要忘記您正在使用 ORM 將 ACTUAL 模型鏈接到您的 Java 模型,這樣您實際上可以在 Java 中擁有一個實體每一端的 1:n 關系並將其存儲在關系表中。
最好的祝福!
更改級聯屬性並沒有解決我的問題。 我最終決定通過為中間表創建一個對象來自己處理多對多關系,並自行管理它。 它的代碼有點多,但為我想要實現的目標提供了一致的行為。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.