最近更新: 2009-12-21

Hibernate 查詢某個欄位的絕對值

當我們試圖從資料庫中查詢某個欄位的絕對值時,我們有兩種途徑。第一種是自定新的 Hibernate UserType ,再透過 HQL 查詢。 第二種則是利用 SQL abs() 函數,透過 SQL 查詢。

自定新的 Hibernate UserType 並不太難,僅需實作 UserType 介面,再套用一個範例來修改即可。 範例可參考 https://www.hibernate.org/388.html 。

Implements Hibernate's UserType to define a custom class for absolute integer.

package tw.idv.rock;
import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import org.hibernate.Hibernate;
import org.hibernate.usertype.UserType;

/**
 * Custom class for absolute values on the way out from the database, Hibernate 3 version.
 * This is based on Paul Newport's work.
 * @see https://www.hibernate.org/388.html
 * @author rock
 */
public class AbsoluteInteger implements UserType {
    public int[] sqlTypes() {
        return new int[] { Types.INTEGER };
    }

    @SuppressWarnings("unchecked")
	public Class returnedClass() {
        return int.class;
    }

    public boolean equals(Object x, Object y) {
        return (x == y) || (x != null && y != null && (x.equals(y)));
    }

    public Object nullSafeGet(ResultSet inResultSet, String[] names, Object o)
        throws SQLException {
        Integer val =
            (Integer) Hibernate.INTEGER.nullSafeGet(inResultSet, names[0]);
        return Math.abs(val);
    }

    public void nullSafeSet(
        PreparedStatement inPreparedStatement,
        Object o, int i)
        throws SQLException {

    	Integer val = (Integer) o;
    	inPreparedStatement.setInt(i, val);
    }

    public Object deepCopy(Object o) {
        if (o == null) {
            return null;
        }

        return new Integer(((Integer) o));
    }

    public boolean isMutable() {
        return false;
    }

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

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

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

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

}

Use this user type in Hibernate Mapping (HBM).

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping
    PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
    <class name="tw.idv.rock.model.MyMap" table="mymap">
    
            <id name="id" type="int" unsaved-value="null">
              <column name="id" sql-type="serial(10)" />
              <generator class="sequence">
                <param name="sequence">mymap_id_seq</param>
              </generator>
            </id>
            <property name="length" type="tw.idv.rock.AbsoluteInteger"/>
<!--           
            <property name="length" type="int"/>
-->
    </class>
</hibernate-mapping>

HQL Query

Query query = session.createQuery("from MyMap where length >= :length")
    .setInteger("length", minLength);
items = query.list();

然而,透過 Hibernate 的 UserType 途徑取得欄位的絕對值之效能,遠遜於透過 SQL 的 abs() 函數。

SQLQuery query = session.createSQLQuery("select id, abs(length) as length " 
        + " from " + MyMap.tableQuoteName() + " where length >= :length");
    query.addEntity(MyMap.class);
    query.setInteger("length", minLength);
items = query.list();

查詢結果集的內容愈多,則效能差異愈顯著。

如果你採用 SQL 方式取欄位的絕對值,則你不必定義新的 UserType 與 HBM。

樂多舊網址: http://blog.roodo.com/rocksaying/archives/11126093.html