我有一个实体,它在数据库中有一些 BIT 字段:
这些字段使用Hibernate 3.6.9版本映射到其Java类中的boolean
字段。这迫使我为每个我想要的实体列表编写一个接口方法:
List<Entity> listEditables();
List<Entity> listReviewNeeded();
List<Entity> listActives();
或者编写通用接口方法来实现它们的组合:
List<Entity> listEntities(boolean editables, boolean reviewNeeded, boolean actives);
第二个选择看起来更大,但是如果我将来添加另一个字段,则需要修改接口本身(以及与之耦合的每一行代码)。
所以我决定将其表达为枚举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
逻辑匹配的映射布尔值。然后我的问题是,有没有办法在hibernate中基于 BIT 字段映射Set<EntityType> entityTypes
,还是我必须自己管理那些逻辑boolean
?
更新
将它们映射为Set
意味着可以使用in
子句查询List,如果不是,则意味着我的控制器和模型代码之间转换的额外步骤。
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();
答案 0 :(得分:3)
我想我有一个解决方案。你感兴趣的是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源代码中的文档和示例(构建复合用户类型)。
以类似的方式,您可以映射有意义的位属性。您可以使用单个Restriction.eq查询这些位,引用您的EnumType。您可以使用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的这种中级知识。
答案 1 :(得分:1)
我不认为hibernate提供了一种以您描述的方式管理映射的方法。您可以创建自己的UserType
(https://community.jboss.org/wiki/Java5EnumUserType),但每次添加新的枚举值时,您都必须更改UserType
中的逻辑以映射新字段。
替代方案是将其转换为一对多的关系。您的观点基本上是,如果您想要添加更多字段,则必须更改listEntities
的签名,但您还必须修改表格。
因此,您可以创建一个包含实体类型的表,并与您的实体建立@ OneToMany`关系。例如:
根据需要定义标志:
public enum Flags {
EDITABLE, REVIEW_NEEDED, ACTIVE
}
创建与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:请注意,代码只是一个示例,并未完整或经过测试。
答案 2 :(得分: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();
}
不错,但不知道这是否是唯一的方法!