8

時間がない場合は、例を見てください

一時ユーザーと永続ユーザーの 2 種類のユーザーがいます。

一時的なユーザーはシステムをゲストとして使用し、名前を入力して使用しますが、システムはそれらを追跡する必要があります。

永久ユーザーとは、登録済みの永久ユーザーです。

ユーザーが自分の永久記録を作成したら、ユーザーがゲストであった間に追跡されたすべての情報を永久記録にコピーする必要があります。

クラスは次のとおりです。

@Entity
public class PermUser{
    @Id
    @GeneratedValue
    private long id;

    @OneToMany
    private List Favorites favorites;    
    ....

}

@Entity
public class Favorites {
    @Id
    @GeneratedValue
    private long id;

    @OneToMany (cascade = CascadeType.ALL)
    @LazyCollection(LazyCollectionOption.FALSE)
    private List <FavoriteItems> items;

    ...
 }

 @Entity
   public class FavoriteItems {
     @Id
     @GeneratedValue
     private long id;

     private int quantity;

     @ManyToOne
     private Ball ball;
     ..
   }


@Entity
public class TempUser extends PermUser{
    private String date;
    ....
}

問題は次のとおりです。

tempUser オブジェクトのクローンを作成すると、id パラメータもコピーされるため、perm ユーザー オブジェクトを保存すると、「キーのエントリ '10' が重複しています ...」のようなメッセージが表示されます。最初に tempUser を削除してから保存することはできません。 permUser permUser の保存に失敗したかのように、データが失われます。アイテムの ID なしでお気に入りのアイテムの各ボールを個別にコピーしようとすると、効率的な方法ではありません。

例 ( 1 つの文での質問:示されているように、ユーザーは複数の TempUser レコードと 1 つの PermUser レコードしか持たない可能性があるため、すべての TempUser レコードの情報をその 1 つの PermUser レコードに追加する必要があります。)

  Type of record    | name      | favorites         | date 
                    |           |                   |
1)TempUser          | Jack      | 2 items           | 1/1/2013
2)TempUser          | Jack      | 3 items           | 1/4/2013
  ---------------------------------------------------------------------------
  PermUser          | Jack      | 5 items ( 2 + 3 items from his temp records)

*注意してください、私は解決策を見つける必要があり、オブジェクトを複製するのではなく、新しい解決策を試すかどうかは気にしません.

私が 2 つの異なるクラスを持っている理由は、tempUser追加の属性がほとんどないtempUsersためpermUserです。また、前述のように、ユーザーは関連のない多くの異なる一時レコードを持つ場合があります

4

9 に答える 9

4

何かが足りない場合は申し訳ありませんが、それは別のクラスであるべきだ TempUserとは思いません。extends 、これは「is-a」関係です。明らかに、一時ユーザーは永久ユーザーの一種ではありません。あなたの質問は、それらを異なるものにすることを正当化するのに十分な情報を提供していません-おそらくそれらは同じクラスであり、その違いはいくつかの新しい属性として表現できますか? 例えば:PermUserTempUserPermUser

@Entity
public class User{
    @OneToMany(cascade = CascadeType.ALL)
    private List Favorites favorites;
    private boolean isTemporary;
    ....
}

一時ユーザーから永続ユーザーへの「移行」isTemporary = falseは、永続ユーザーの他のプロパティが適切に設定されていることを確認して、一部のコントローラーで処理できます。これにより、クローン作成の問題が完全に回避され、データベースでの作業がはるかに簡単になります。

于 2013-10-09T04:54:10.827 に答える
2

可能であればこれらのエンティティを再評価する必要があるという以前のコメントに同意しますが、それが不可能な場合は、データベースから一般的なユーザーを返し、そのユーザーを PermUser または TempUser のいずれかにキャストすることをお勧めします。特定の基準の存在に基づくユーザー。

于 2013-10-10T16:37:31.663 に答える
1

手動でディープクローンを作成する必要があると思います。複数の tempUsers から単一の permUsers にデータをマージする必要があるため、正確にはクローンではありません。リフレクションとオプションで注釈を使用して、情報のコピーを自動化できます。

フィールドを既存のオブジェクトから新しいオブジェクトに自動的にコピーするには、次の例に従います。これはディープ クローンではありませんが、出発点として役立つ可能性があります。

クラス「c」が参照として使用されます。src と dest は、'c' のインスタンスまたは 'c' のサブクラスのインスタンスでなければなりません。このメソッドは、「c」で定義された属性と「c」のスーパークラスをコピーします。

public static <E>  E copyObject(E dest, E src, Class<?> c) throws IllegalArgumentException, IllegalAccessException{
  // TODO: You may want to create new instance of 'dest' here instead of receiving one as parameter
    if (!c.isAssignableFrom(src.getClass())) 
    {
        throw new IllegalArgumentException("Incompatible classes: " + src.getClass() + " - " + c);
    }
    if (!c.isAssignableFrom(dest.getClass())) 
    {
        throw new IllegalArgumentException("Incompatible classes: " + src.getClass() + " - " + c);
    }
    while (c != null && c != Object.class) 
    {
        for (Field aField: c.getDeclaredFields()) 
        {                   
            // We skip static and final
            int modifiers = aField.getModifiers();
            if ( Modifier.isStatic(modifiers) || Modifier.isFinal(modifiers)) 
            {
                continue;
            }

            // We skip the fields annotated with @Generated and @GeneratedValue
            if (aField.getAnnotation(GeneratedValue.class) == null && 
                aField.getAnnotation(Generated.class) == null) 
            {

                aField.setAccessible(true);
                Object value = aField.get(src);
                if (aField.getType().isPrimitive() ||
                    String.class == aField.getType()    || 
                    Number.class.isAssignableFrom(aField.getType()) ||
                    Boolean.class == aField.getType()   ||
                    Enum.class.isAssignableFrom(aField.getType()))
                {
                    try
                    {
                        // TODO: You may want to recursive copy value too
                        aField.set(dest, value);
                    }
                    catch(Exception e)
                    {
                        e.printStackTrace();
                    }
                }
            }   
        }
        c = c.getSuperclass();  
    }

    return dest;
}
于 2013-10-18T09:09:06.610 に答える
1

問題のパート 2 の場合:

関係に使用CascadeType.ALLしています。favoritesこれには が含まれます。これはCascadeType.REMOVE、ユーザーに対する削除操作がそのエンティティにカスケードされることを意味します。CascadeTypeを含まない値の配列を指定してCascadeType.REMOVEください。http://webarch.kuzeko.com/2011/11/hibernate-understanding-cascade-types/を参照してください。

于 2013-10-09T23:40:44.110 に答える
1

私が提案しようとしているのは、OO ではないかもしれませんが、効果があることを願っています。私は、PermUserTempUserを分離して拡張せず、is-a 関係にバインドしないことを嬉しく思います。したがって、 TempUser用とPermUser用のデータベースに 2 つの別個のテーブルを作成し、2 つの別個のエンティティとして扱います。多くの人はそれが冗長であることに気付くでしょう..しかし読んでください...私たちは皆知っています..時には冗長性が良い..だから今...

1) TempUserがいつPermUserになりたいのかわかりません。したがって、すべてのTempUsersを常に別のテーブルに配置します。

2) ユーザーが常にTempUserになりたい場合はどうすればよいでしょうか ..? 参照する別のTempUserテーブルがまだあります..

3) TempUser がPermUserになりたい場合、彼の TempUser 名を読み取って、のレコードを TempUser として取得していると想定ています。

だから今、あなたの仕事は簡単です。したがって、TempUser が PermUserなりたい場合は、 TempUserオブジェクトをコピーし、必要な属性を設定して、それを使用して新しいPermUserオブジェクトを作成するだけです。その後、必要に応じてTempUserレコードを保持するか、削除することができます.. :)

TempUsersまた、それを保持すると、実際にどれだけの数が永続的になるかの履歴があり、 a が永続的TempUserになる平均時間もわかります。

于 2013-10-16T07:58:24.723 に答える
1

一部の人がすでに示唆しているように、継承+浅いコピー(参照を共有するため)または自動生成されたIDを除外/操作できるライブラリを使用したディープクローン(アイテムを複製する場合)を使用して、この問題に取り組むことを提案しました。

データベース モデルをあまり曲げたくないので、共通の属性を持つマップされたスーパークラスから始めます。これは、データベースにはまったく反映されません。できれば、モデルに近い単一テーブル継承を使用します(ただし、データベースレイヤーでいくつかの調整が必要になる場合があります)。

@MappedSuperclass
public abstract class User {
    @Id
    @GeneratedValue
    private long id;
    // Common properties and relationships...

次に、両方PermUserを持ち、 からTempUser継承しUserて、多くの共通の状態を持つようにします。

@Entity
@Table(name="USER")
public class PermUser extends User {
  // Specific properties
}

現在、いくつかの可能なアプローチがあります。クラスに多くの状態がない場合は、たとえば、PermUserリストの収集データを構築するコンストラクターを作成できますTempUsers

モックコード:

@Entity
@Table(name="PERMANENT_USER")
public class PermUser extends User {
  public PermUser() {} // default constructor
  public PermUser(List<TempUser> userData) {
     final Set<Favorites> f = new LinkedHashSet<>();

     // don't set the id
     for(TempUser u : userData) {
        this.name = u.getName();
        // Shallow copy that guarants uniqueness and insertion order
        // Favorite must override equals and hashCode
        f.addAll(u.getFavorites());
     } 
     this.favorites = new ArrayList<>(f);
     // Logic to conciliate dates
  }
}

永続PermUser化すると、新しい ID が生成され、カスケードされた一方向の関係が正常に機能するはずです。

一方、クラスに多くの属性と関係があり、実際にオブジェクトを複製する必要がある状況が多い場合は、Dozerなどの Bean マッピング ライブラリを使用できます(ただし、オブジェクトの複製には注意してください)。はコードのにおいです)。

Mapper mapper = new DozerBeanMapper();
mapper.map(tempUser.getFavorites(), user.getFavorites());

dozer を使用すると、注釈API、またはXMLを介してマッピングを構成し、フィールドの除外、型キャストなどを行うことができます。

モック マッピング:

<mapping>
  <class-a>my.object.package.TempUser</class-a>
  <class-b>my.object.package.PermUser</class-b>

  <!-- common fields with the same name will be copied by convention-->

  <!-- exclude ids and fields exclusive to temp
  <field-exclude> 
    <a>fieldToExclude</a> 
    <b>fieldToExclude</b> 
  </field-exclude>           

</mapping> 

たとえば、ID を除外したり、複製permUser.idされたすべての双方向の関係をユーザー (存在する場合) にコピーしたりできます。

また、コレクションの複製は、デフォルトでは累積的な操作であることに注意してください。

ドーザーのドキュメントから:

すでに初期化されているクラスにマッピングしている場合、Dozer はオブジェクトをリストに「追加」または「更新」します。List または Set に既にオブジェクトが含まれている場合、dozer はマップされた List、Set、または Array をチェックし、contains() メソッドを呼び出して、「追加」または「更新」が必要かどうかを判断します。

私はいくつかのプロジェクトで Dozer を使用しました。たとえば、あるプロジェクトでは、JPA モデル レイヤーにマップする必要がある JAXB レイヤーがありました。彼らは十分に接近していましたが、残念ながら私はどちらも曲げることができませんでした. Dozer は非常にうまく機能し、習得も簡単で、退屈なコードの 70% を書く必要がありませんでした。深くできるクローン個人的な経験からこのライブラリをお勧めします。

于 2013-10-21T01:02:43.200 に答える
-1

これをモデル化する良い方法は、 has-aとhas-aのようなUserDataクラスのようなものを作成することです。has-aを作成することもできますが、それはあまり明確ではありません。アプリケーションがそれらを交換可能に使用する必要がある場合 (使用していた継承で得られるもの)、両方のクラスは(または 2 番目のオプションで、それ自体を返す) を返すインターフェイスを実装できます。 TempUserUserDataPermUserUserDataTempUserPermUserUserDatagetPermUserPermUser

本当に継承を使用したい場合は、「クラス階層ごとのテーブル」を使用してマッピングし、ストレート JDBC を使用して識別子列を直接更新するのが最も簡単です。

于 2013-10-04T04:05:23.653 に答える