2015-12-20 18 views
6

休止状態の多対多リレーションシップに問題があります。セットから1つのアイテムを削除すると、データベース内で削除されません。私は似たような問題がたくさんあることを知っていますが、私はそれらを読むことで私の問題を解決することに成功しませんでした。休止状態の多対多リレーションシップを削除

私は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()); 
} 

は、私が得たエラーです:私は「テスト1」をコメントし、「テスト2」の行のコメントを解除する場合

... 
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] 

、私は次のように行きますエラー:ここ

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 ... */ 

EDIT:は、トランザクションのために、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='all'buildingsの関係に置き換えて、ユーザーが定義した問題を解決する必要があります。

ユーザーを保存するので、DBの多対多も更新するには、ユーザーからの関係の変更をカスケードする必要があります。

10

CascadeType.REMOVEdoesn'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.

0

私は何をやっていることは、それはあなたが関係をどう考え、より一般的なタスクの一つであっても、本当にHibernateで良いアイデアではないことを恐れています:Hibernate docsを引用する

。あなたが望むものを達成する方法はカスケードを使用していますが、Vlad Mihalceaの説明によると、これによって関係の一端または他端が削除され、関係そのものだけが削除される可能性があります。

私は教師の言うことを教えてくれるでしょう...あなたは本当にn:mの関係を持っていますか?それ自体がエンティティを持っていないと確信していますか? N:Mの関係は非常に稀であり、通常、モデリングが間違っていることを意味します。これが当てはまらず、実際にn:mを持っていても、これはモデルに残っているはずです.ORMを使用してACTUALモデルをJavaモデルにリンクしているので、実際にJavaでエンティティを持つことができます各エンドで1:nの関係を作成し、関係テーブルに格納します。

よろしくお願いいたします。

関連する問題