5

我有一个实体,它在数据库中有一些BIT字段:

  • 可编辑
  • 需求审查
  • 积极的

这些字段boolean使用 Hibernate 3.6.9 版本映射到其 Java 类中的字段。这迫使我为我想要获取的每个实体列表编写一个接口方法:

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。然后我的问题是,有什么方法可以Set<EntityType> entityTypes根据该BIT字段映射到休眠状态,还是我必须自己管理该逻辑boolean

更新

将它们映射为 aSet意味着可以使用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();
4

3 回答 3

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 源代码中的示例(内置复合用户类型)。

以类似的方式,您可以映射有意义的位属性。您可以使用引用您的 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 的中级知识。

于 2013-09-27T19:58:33.507 回答
1

我不认为休眠提供了一种按照您描述的方式管理映射的方法。您可以创建自己的UserTypehttps://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:请注意,代码只是一个示例,并未完整或经过测试。

于 2013-09-04T13:24:12.650 回答
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 回答