5

データベースにいくつかのBITフィールドを持つエンティティがあります。

  • 編集可能
  • ニーズ_レビュー
  • アクティブ

booleanこれらのフィールドは、Hibernate 3.6.9 バージョンを使用して Java クラスのフィールドに対してマップされます。これにより、取得したいエンティティの List ごとにインターフェイス メソッドを作成する必要があります。

List<Entity> listEditables();
List<Entity> listReviewNeeded();
List<Entity> listActives();

または、それらの組み合わせを実現するための一般的なインターフェイス メソッドを記述します。

List<Entity> listEntities(boolean editables, boolean reviewNeeded, boolean actives);

2 番目の選択肢の方が優れているように見えますが、将来別のフィールドを追加する場合は、インターフェイス自体 (およびインターフェイスに結合されたすべてのコード行) を変更する必要があります。

だから私はそれを列挙型として表現できると決めましたSet:

public enum EntityType{
    EDITABLE, REVIEW_NEEDED, ACTIVE
}

//That way there's no need to change interface method's signature
List<Entity> listEntities(Set<EntityType> requiredTypes);

私が達成したいものと一致する列挙であることは理にかなっています。Entity型自体は独自のものを持つべきSet<EntityType>です:

public class Entity{
    Set<EntityType> entityTypes;
}

ただし、その代わりに、論理的に一致するマップされたブール値がありSetます。次に、私の質問は、そのBITSet<EntityType> entityTypesフィールドに基づいて休止状態でマップする方法はありますか、またはそのロジックを自分で管理する必要がありますか?boolean

アップデート

それらを としてマップするSetことは、句を使用してリストを照会する可能性を意味しinます。そうでない場合、コントローラーとモデルコード間の変換のための追加のステップを意味します。

Set<EntityType> typesSet = Sets.newHashSet(EntityType.EDITABLE, EntityType.REVIEW_NEEDED);
//Obtains a list of every single entity which is EDITABLE or REVIEW_NEEDED
session.createCriteria(Entity.class).addRestriction(Restrictions.in("entityTypes",typeSet)).list();
4

3 に答える 3

3

私はあなたのための解決策があると思います。あなたが興味を持っているのは CompositeUserType です。

例として、私が最近書いた InetAddress 複合ユーザー タイプを使用して、128 ビット IPv6 アドレス / IPv4Address オブジェクトをユーザー アカウント エンティティ内の 2 つの 64 ビット長プロパティにマップしてみましょう。

signupIp:InetAddress は、次を使用して 2 つの列にマップされます (列数の制限などはありません)。

    @Columns(columns = {@Column(name = "ip_low", nullable = true), @Column(name = "ip_high", nullable = true)})
    private InetAddress signupIp;

そして、実装の興味深い部分は次のようになります。

public class InetAddressUserType implements CompositeUserType {
@Override
public String[] getPropertyNames() {
    return new String [] {"ipLow", "ipHigh"};
}

@Override
public Type[] getPropertyTypes() {
    return new Type [] { LongType.INSTANCE, LongType.INSTANCE};
}

@Override
public Object getPropertyValue(Object component, int property) throws HibernateException {
    if(component != null)
        return toLong((InetAddress)component)[property];
    else
        return null;
}

@Override
public void nullSafeSet(PreparedStatement st, Object value, int index,
        SessionImplementor session) throws HibernateException, SQLException {

    if(value != null) {
        long [] longs = toLong((InetAddress)value);
        st.setLong(index, longs[0]);
        st.setLong(index + 1, longs[1]);
    }
    else {
        st.setNull(index, LongType.INSTANCE.sqlType());
        st.setNull(index + 1, LongType.INSTANCE.sqlType());
    }
}

@Override
public void setPropertyValue(Object component, int property, Object value)
        throws HibernateException {
    throw new RuntimeException("This object is immutable");
}

@Override
public Class<?> returnedClass() {
    return InetAddress.class;
}

@Override
public boolean equals(Object x, Object y) throws HibernateException {
    return x != null ? x.equals(y) : null == y;
}

@Override
public int hashCode(Object x) throws HibernateException {
    return x.hashCode();
}

@Override
public Object nullSafeGet(ResultSet rs, String[] names,
        SessionImplementor session, Object owner)
        throws HibernateException, SQLException {
    Long ipLow = rs.getLong(names[0]);
    if(!rs.wasNull()) {
        Long ipHigh = rs.getLong(names[1]);

        try {
            return fromLong(new long [] {ipLow, ipHigh});
        } catch (UnknownHostException e) {
            throw new HibernateException("Failed to get InetAddress: ip = " + ipHigh + " + " + ipLow, e);
        }
    }
    else
        return null;
}

@Override
public Object deepCopy(Object value) throws HibernateException {
    if(value != null)
        try {
            return InetAddress.getByAddress(((InetAddress)value).getAddress());
        } catch (UnknownHostException e) {
            throw new RuntimeException("Impossible Exception: " + e.getMessage(), e);
        }
    else
        return null;
}

@Override
public boolean isMutable() {
    return false;
}
    ...
 }

ipLow と ipHigh の値に応じて、Inet4Address インスタンスと Inet6Address インスタンスを柔軟に切り替えることに注意してください。複合は不変としてマークされており、ドキュメントと Hibernate ソース コードの例を確認する必要があります (複合ユーザー タイプのビルド)。

同様の方法で、意味のあるビット プロパティをマップできます。EnumType を参照する単一の Restriction.eq を使用して、これらのビットを照会できます。equals メソッドを使用して、プロパティ オブジェクトを確認できます。また、特別にマップされたビットを参照する必要がある場合は、signupIp.ipLow のようにドット表記を使用して、ipLow プロパティ/列を参照できます。

これがあなたが探しているものだと思います。

アップデート:

最終的には、プロパティの正しい順序を定義することになります。Hibernate は常に整数のインデックス値を使用して各プロパティにアクセスします。

//immutable for simplicity
class Status {
     private final boolean editable;
     private final boolean needsReview;
     private final boolean active;
     //... constructor + isEditable etc..
}

StatusCompositeType クラスで:

public String[] getPropertyNames() {
  return new String [] {"editable", "needsReview", "active"};
}

public Type[] getPropertyTypes() {
  return new Type [] { BooleanType.INSTANCE, LongType.INSTANCE};
}

public Object getPropertyValue(Object component, int property) throws HibernateException {
if(component != null) {
   Status status = (Status)component;
   switch(property) {
   case 1: return status.isEditable();
   case 2: return status.isReviewNeeded();
   case 3: return status.isActive();
   default: throw new IllegalArgumentException();
   }
}
else
    return null; //all columns can be set to null if you allow a entity to have a null status.
}


public void nullSafeSet(PreparedStatement st, Object value, int index,
    SessionImplementor session) throws HibernateException, SQLException {

  if(value != null) {
    Status status = (Status)value;
    st.setBoolean(index, status.isEditable());
    st.setBoolean(index + 1, status.isReviewNeeded());
    st.setBoolean(index + 2, status.isActive());
  }
  else {
    st.setNull(index, BooleanType.INSTANCE.sqlType());
    st.setNull(index + 1, BooleanType.INSTANCE.sqlType());
    st.setNull(index + 2, BooleanType.INSTANCE.sqlType());
  }
}

public Object nullSafeGet(ResultSet rs, String[] names,
    SessionImplementor session, Object owner)
    throws HibernateException, SQLException {
  Boolean isEditable = rs.getBoolean(names[0]);
  if(!rs.wasNull()) {
    Boolean isReviewNeeded = rs.getBoolean(names[1]);
    Boolean isActive = rs.getBoolean(names[2]);

    return new Status(isEditable, isReviewNeeded, isActive);
  }
  else
    return null;
}

残りは簡単です。sessionFactory を作成する前に、ユーザー タイプに equals と hashcode を実装し、設定にタイプを追加することを忘れないでください。

すべてが整ったら、条件検索を作成して使用できます。

//search for any elements that have a status of editable, no reviewNeeded and is not active (true false false).
criteria.add(Restrictions.eq("status", new Status(true, false, false));

これで、listEntities メソッドは次のいずれかになりlistEntities(Status status)ますlistEntities(boolean editable, boolean reviewNeeded, boolean isActive)

追加情報が必要な場合は、Hibernate が独自のソースコード内で提供する CompositeType および BasicType の実装を確認してください (CompositeType および BasicType の実装者を探してください)。これらを理解すると、Hibernate の中級レベルの知識を使用して学習するのに大いに役立ちます。

于 2013-09-27T19:58:33.507 に答える
1

いくつかのブレインストーミングの後、Hibernate でブール値の列挙型をマップすることは不可能であると考える回避策に行きました。これが私のEntityクラスの外観です。

public class Entity{

    private boolean editable;

    private boolean needsReview;

    private boolean active;

    //getters and setters

}

私のリスト方法は次のように実装されています:

public List<Entity> listEntities(Set<EntityType> requiredTypes){
    Criteria cri = session.createCriteria(Entity.class);
    if (requiredTypes.contains(EntityType.EDITABLE)){
        cri.addRestriction(Restrictions.eq("editable",true));
    }
    if (requiredTypes.contains(EntityType.NEEDS_REVIEW)){
        cri.addRestriction(Restrictions.eq("needsReview",true));
    }
    if (requiredTypes.contains(EntityType.ACTIVE)){
        cri.addRestriction(Restrictions.eq("active",true));
    }
    return cri.list();
}

悪くはありませんが、それが唯一の方法かどうかはわかりません!

于 2013-09-04T17:25:07.563 に答える
1

休止状態が、あなたが説明している方法でマッピングを管理する方法を提供するとは思いません。UserType独自のもの( https://community.jboss.org/wiki/Java5EnumUserType )を作成できますが、新しい列挙値を追加するたびにUserType、新しいフィールドをマップするためにロジックを変更する必要があります。

別の方法は、これを 1 対多の関係に変換することです。基本的に、フィールドを追加したい場合は、署名を変更する必要があるlistEntitiesだけでなく、テーブルを変更する必要があるということです。

したがって、代わりに、エンティティ タイプを含み、エンティティから @OneToMany` 関係を持つテーブルを作成できます。例えば:

必要に応じてフラグを定義します。

public enum Flags {
    EDITABLE, REVIEW_NEEDED, ACTIVE
}

との 1 対多の関係を作成しますEntityType

@Entity
@Table( name="entity" )
public class Entity implements Serializable {

    @OneToMany(mappedBy = "entity")
public Set<EntityType> getEntityTypes() {
    return entityTypes;
}

そして多対一からEntity:

@Entity
@Table( name="entityType" )
public class EntityType implements Serializable {

    @Id
    private Integer id;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "ENTITY_ID")
    private Entity entity;

    @Enumerated(EnumType.STRING)
    private Flag entityType;

    ...
}

PD: コードは単なる例であり、完全ではなく、テストもされていないことに注意してください。

于 2013-09-04T13:24:12.650 に答える