我想我有一个解决方案给你。您感兴趣的是 CompositeUserType。
例如,让我们使用我最近编写的 InetAddress 复合用户类型将 128 位 IPv6 地址/IPv4Address 对象映射到用户帐户实体内的两个 64 位长属性。
signupIp:InetAddress 使用以下方法映射到两列(没有列数限制等):
@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;
}
其余的都是直截了当的。请记住为用户类型实现 equals 和 hashcode,并在创建 sessionFactory 之前将类型添加到配置中。
一切就绪后,您可以创建条件搜索并使用:
//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 的中级知识。