私はあなたのための解決策があると思います。あなたが興味を持っているのは 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 の中級レベルの知識を使用して学習するのに大いに役立ちます。