[hibernate-issues] [Hibernate-JIRA] Created: (HHH-3969) HQL does not give proper return type for mathematical operations on custom types within an aggregation

Evan Hecht (JIRA) noreply at atlassian.com
Wed Jun 17 21:21:33 EDT 2009


HQL does not give proper return type for mathematical operations on custom types within an aggregation
------------------------------------------------------------------------------------------------------

                 Key: HHH-3969
                 URL: http://opensource.atlassian.com/projects/hibernate/browse/HHH-3969
             Project: Hibernate Core
          Issue Type: Bug
          Components: query-hql
    Affects Versions: 3.3.1
         Environment: Hibernate 3.3.1, MySQL Server 5.1.33
            Reporter: Evan Hecht


I have a custom type that is essentially a double, and each instance of my class has an instance of that type and an integer. I'd like to sum the integer times the custom double for each instance of my class:

select sum(f.bar * f.baz) for Foo f;

However, casting the uniqueResult() of that query to Double results in: java.lang.ClassCastException: java.lang.Long cannot be cast to java.lang.Double

baz uses my custom type. I've simplified my case as much as possible, all relevant info should be included below, Main and output at the bottom. Including any non-floating-point type in an expression within an aggregation query results in a Long being returned instead of a Double, even for division. Leaving the type unset within my mapping file or setting it to "double" gives correct return types/values for all my test cases.


Foo.java:
--------------------------------------------------
public class Foo {
	private Long id;
	private int bar;
	private Double baz;
	
    public void setId(Long id) {
        this.id = id;
    }
    public Long getId() {
        return id;
    }
    public void setBar(int bar) {
        this.bar = bar;
    }
    public int getBar() {
        return bar;
    }
    public void setBaz(Double baz) {
        this.baz = baz;
    }
    public Double getBaz() {
        return baz;
    }
	
}
--------------------------------------------------


Classes.hbm.xml:
--------------------------------------------------
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
	<class name="Foo" table="FOO">
		<id name="id" column="FOO_ID" type="long"><generator class="native"/></id>
		<property name="bar"/>
		<property name="baz" type="DoubleUserType"/>
	</class>
</hibernate-mapping>
--------------------------------------------------


DoubleUserType.java:
--------------------------------------------------
public class DoubleUserType implements UserType{

    public int[] sqlTypes() { return new int[] {Types.DOUBLE}; }
    
    public Class<Double> returnedClass() { return Double.class; }
    

    public void nullSafeSet(PreparedStatement st, Object value, int index) throws SQLException {
        if(value == null) {
            st.setNull(index, Types.DOUBLE);
        }
        else {
            st.setDouble(index, (Double)value);
        } 
    }
    
    public Object nullSafeGet(ResultSet rs, String[] names, Object owner) throws SQLException {
        return rs.wasNull() ? null : rs.getDouble(1);
    }
    
    
    public boolean isMutable() { return false; }
    
    public boolean equals(Object x, Object y) { return x == y; }

    public int hashCode(Object x) { return ((Double)x).hashCode(); }
    
    public Object assemble(Serializable cached, Object owner) { return cached; }

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

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

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


 hibernate.cfg.xml:
--------------------------------------------------
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

<hibernate-configuration>
<session-factory>
      <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
      <property name="hibernate.connection.url">jdbc:mysql://localhost/htest</property>
      <property name="hibernate.connection.username">tester</property>
      <property name="hibernate.connection.password"></property>
      <property name="dialect">org.hibernate.dialect.MySQLDialect</property>
      <property name="hibernate.hbm2ddl.auto">create</property>
</session-factory>
</hibernate-configuration>
--------------------------------------------------


Main.java:
--------------------------------------------------
public class Main {
    static Session s;
    public static void main(String[] args) {
        Configuration cfg = new Configuration().configure(new File("./hibernate.cfg.xml"));
        cfg.addResource("Classes.hbm.xml");
        SessionFactory sessions = cfg.buildSessionFactory();

        s = sessions.openSession();

        Transaction tx = s.beginTransaction();
        for (int i = 0; i < 3; i++) {
            Foo f = new Foo();
            f.setBar(i);
            f.setBaz(.5);
            s.save(f);
        }
        tx.commit();

        executeQuery("select sum(f.bar) from Foo f",         3L);
        executeQuery("select sum(f.baz) from Foo f",         1.5d);
        executeQuery("select min(f.baz) from Foo f",          .5d);
        executeQuery("select max(f.baz) from Foo f",          .5d);
        executeQuery("select sum(f.baz * f.bar) from Foo f", 1.5d);
        executeQuery("select sum(f.baz + 1) from Foo f",     4.5d);
        executeQuery("select sum(f.baz + 1.0) from Foo f",   4.5d);
        executeQuery("select max(f.baz + 1) from Foo f",     1.5d);
        executeQuery("select max(f.baz + 1.0) from Foo f",   1.5d);
        executeQuery("select max(f.baz * 1) from Foo f",      .5d);
        executeQuery("select max(f.baz * 1.0) from Foo f",    .5d);
        executeQuery("select sum(f.baz / 2) from Foo f",      .75d);
        executeQuery("select sum(f.baz / 2.0) from Foo f",    .75d);
        executeQuery("select sum(f.baz * f.baz) from Foo f",  .75d);

        s.close();
    }
    
    public static void executeQuery(String query, Number expected) {
        Query q = s.createQuery(query);
        System.out.println("\""+query+"\":");
        System.out.print("\tExpected <"+expected.getClass().getSimpleName()+">: "+expected+" Got: ");
        try {
            Number res = expected instanceof Double ? (Double) q.uniqueResult() : (Long) q.uniqueResult();
            System.out.print(res);
            System.out.println();
        }
        catch(ClassCastException cce) {
            System.out.flush();
            cce.printStackTrace();
        }
    }
}
--------------------------------------------------


OUTPUT
--------------------------------------------------
"select sum(f.bar) from Foo f":
	Expected <Long>: 3 Got: 3.0
"select sum(f.baz) from Foo f":
	Expected <Double>: 1.5 Got: 1.5
"select min(f.baz) from Foo f":
	Expected <Double>: 0.5 Got: 0.5
"select max(f.baz) from Foo f":
	Expected <Double>: 0.5 Got: 0.5
"select sum(f.baz * f.bar) from Foo f":
	Expected <Double>: 1.5 Got: java.lang.ClassCastException: java.lang.Long cannot be cast to java.lang.Double
	at Main.executeQuery(Main.java:50)
	at Main.main(Main.java:31)
"select sum(f.baz + 1) from Foo f":
	Expected <Double>: 4.5 Got: java.lang.ClassCastException: java.lang.Long cannot be cast to java.lang.Double
	at Main.executeQuery(Main.java:50)
	at Main.main(Main.java:32)
"select sum(f.baz + 1.0) from Foo f":
	Expected <Double>: 4.5 Got: 4.5
"select max(f.baz + 1) from Foo f":
	Expected <Double>: 1.5 Got: java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.Double
	at Main.executeQuery(Main.java:50)
	at Main.main(Main.java:34)
"select max(f.baz + 1.0) from Foo f":
	Expected <Double>: 1.5 Got: 1.5
"select max(f.baz * 1) from Foo f":
	Expected <Double>: 0.5 Got: java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.Double
	at Main.executeQuery(Main.java:50)
	at Main.main(Main.java:36)
"select max(f.baz * 1.0) from Foo f":
	Expected <Double>: 0.5 Got: 0.5
"select sum(f.baz / 2) from Foo f":
	Expected <Double>: 0.75 Got: java.lang.ClassCastException: java.lang.Long cannot be cast to java.lang.Double
	at Main.executeQuery(Main.java:50)
	at Main.main(Main.java:38)
"select sum(f.baz / 2.0) from Foo f":
	Expected <Double>: 0.75 Got: 0.75
"select sum(f.baz * f.baz) from Foo f":
	Expected <Double>: 0.75 Got: 0.75


-- 
This message is automatically generated by JIRA.
-
If you think it was sent incorrectly contact one of the administrators: http://opensource.atlassian.com/projects/hibernate/secure/Administrators.jspa
-
For more information on JIRA, see: http://www.atlassian.com/software/jira

        



More information about the hibernate-issues mailing list