使用UserType列在Hibernate中进行查询不会使用nullSafeSet进行转换

时间:2016-04-25 19:13:45

标签: java hibernate hibernate-mapping hibernate-criteria

在我正在使用的一些遗留代码中,我有一个包含UserType属性的Hibernate模型,用于表示使用整数进行索引的布尔值。此外,它将'false'存储为Oracle优化的null。

/**
 * Data type for a boolean value stored in a NUMBER oracle column. 1 evaluates
 * to true, null evaluates to false.
 * 
 * This is designed for oracle indexing, since null values are not indexed.
 * 
 * 
 */

public class BooleanUserType implements UserType {
    private static final int[] SQL_TYPES = { Types.INTEGER };

    public int[] sqlTypes() {
        return SQL_TYPES;
    }

    public Class<?> returnedClass() {
        return Boolean.class;
    }

    public Object nullSafeGet(ResultSet resultSet, String[] names, Object owner) throws HibernateException, SQLException {
        Boolean result = false;
        resultSet.getInt(names[0]);
        if (!resultSet.wasNull()) {
            result = true;
        }
        return result;
    }

    public void nullSafeSet(PreparedStatement preparedStatement, Object value, int index) throws HibernateException, SQLException {
        Boolean input = (Boolean) value;
        if (input == null || input == false) {
            preparedStatement.setNull(index, Types.INTEGER);

        } else {
            preparedStatement.setInt(index, 1);
        }
    }

    public Object deepCopy(Object value) throws HibernateException {
        return value;
    }

    public boolean isMutable() {
        return false;
    }

    public Object assemble(Serializable cached, Object owner) throws HibernateException {
        return cached;
    }

    public Serializable disassemble(Object value) throws HibernateException {
        return (Serializable) value;
    }

    public Object replace(Object original, Object target, Object owner) throws HibernateException {
        return original;
    }

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

    public boolean equals(Object x, Object y) throws HibernateException {
        if (x == y)
            return true;
        if (null == x || null == y)
            return false;
        return x.equals(y);
    }

此BooleanUserType用作此(已编辑)模型的列:

public class CustomString implements Serializable{
  private static final long serialVersionUID = -5311205585865001342L;

  @Id
  @Column(name="custom_string_id")
  private Long id;

  @Column(name="text")
  private String text;

  @Column(name="active")
  @Type(type="my.org.BooleanUserType")  // store false as 'null' -- an Oracle optimization
  private Boolean active;

  (...)

根据this StackOverflow reply

  

在保存/更新实体时都会调用nullSafeSet   当必须设置查询参数时

但是,这不是我观察到的行为,因为当我使用active列的布尔值查询此模型时,没有进行转换,我没有得到预期的结果。

我在DB上保存了一个CustomString对象:

new CustomString(1, "text", Boolean.FALSE);  

我使查询无法正常工作:

Criteria c = session.createCriteria(CustomString.class);
c.add(Restrictions.eq("active", Boolean.FALSE));
c.list() // No results!

如果我这样做,它会起作用:

Criteria c = session.createCriteria(CustomString.class);
c.add(Restrictions.isNull("active"));
c.list() // Expected result!

同时使用Boolean.TRUE而不是FALSE保存对象,然后查询TRUE,工作正常。

我在nullSafeSet方法中添加了一些调试日志行,我可以验证在进行查询时没有调用它。

Hibernate是否应该nullSafeSet实际调用它,并且某种方式不能按预期工作,或者我有一些我不知道的东西,我应该以不同的方式处理它?<​​/ p>

1 个答案:

答案 0 :(得分:0)

仅在插入/更新方案中将对象实际刷新到DB时才会评估基础逻辑nullSafeSet()逻辑。在构建Critera查询时,Hibernate将使用@Column注释来确定列名,但不会自动翻转&#34; = FALSE&#34;到&#34; is null&#34;对你而言。

更确切地说,查询的SELECT部分是由您班级中定义的@Columns确定的,因此Hibernate将始终运行:

SELECT custom_string_id, text, active FROM [schema].[table_name]

Restrictions添加到Criteria会增加WHERE个谓词,但在它返回要解析为实例的行之前,它不会利用您的BooleanUserType逻辑你的实体。

最后,我不确定您指的是哪种Oracle优化,但如果您尝试利用空值从索引中删除的事实,那么您可能不想查询{{ 1}}而不是NULL,因为它会运行全表扫描。但是,如果您只关心活动(False)行并且想要有效地弃用/归档非活动行,那么将旧数据设置为True的方法确实可以获得索引性能提升你指的是。