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....
-
For more information on JIRA, see:
http://www.atlassian.com/software/jira