Issue Details (XML | Word | Printable)

Key: HHH-1936
Type: Bug Bug
Status: Open Open
Priority: Major Major
Assignee: Unassigned
Reporter: Leonid Shtivelman
Votes: 8
Watchers: 10
Operations

If you were logged in you would be able to see more operations.
Hibernate Core

IdentityGenerator doesn't support BigInteger as a valid identity type.

Created: 24/Jul/06 11:56 AM   Updated: 14/Apr/09 04:17 AM
Component/s: core
Affects Version/s: 3.0.5
Fix Version/s: None

Time Tracking:
Not Specified

File Attachments: 1. Java Source File IdentifierGeneratorFactory.java (6 kB)

Environment: Hibernate 3.0.5, Sybase 12.05, Window/ Unix
Issue Links:
Relates
 

Bug Testcase Reminder (view):
REMINDER: Bug reports should generally be accompanied by a test case
Participants: Andrew Thompson, chandesris, Dan, Dejan Predovic, Guenther Enthaler, Julien Kronegg, Kristian Schjelderup, Leonid Shtivelman, Michel Nolard and Sarah MacLeod


 Description  « Hide

Identity generator strategy doesn't support BigInteger as a valid id type. This causes problem with Sybase which requires identity column to be numeric. It would seem an obvious way to get around this problem is to set column mapping to long in hibernate xml and the actual java object. This will solve obvious problem of creating identity, but will cause performance problem on selection. In order for Sybase to use index for parameter query the variable type of the parameter and column index type has to be the same. If ones maps column type to Long, Hibernate will use JDBC method setLong(long) to set value in the prepared statement. This will cause mismatch between parameter type and column index type, and Sybase will not use index to locate index. As you can see this is a big problem for anyone that is using identity columns Sybase and Hibernate



Kristian Schjelderup added a comment - 29/Jan/07 01:34 PM

We are struggling with the same issue when integrating with an 10+ yr old Sybase db using numeric for identity columns.

Tried making changes to the IdentifierGeneratorFactory and extended the two if..else-blocks to support BigDecimal (which is the native mapping type for numeric) with success. But - obviously we would like this issue handled correctly, and are anxiously waiting for someone in the core team to comment on this issue.


Andrew Thompson added a comment - 18/Apr/07 09:47 AM

To break the original issue down a bit for clarity:

  • Sybase requires that all auto-increment identity columns be of type numeric:
    e.g. create table Foo ( foo_id numeric(9,0) identity )
  • Sybase's JConnect drvier maps this to BigInteger (well BigDecimal really) in Java code [*] see below
  • Hibernate won't allow BigInteger as a primary key column for an object, due, I think, to code in IdentityGeneratorFactory which checks explicitly

Result - we're caught between the proverbial rock and hard place.

So to partially get around this, we do this:

<id name="fooNum" column="foo_id" type="com.example.hibernate.usertype.NumericType">
<generator class="identity" />
</id>

And NumericType is then a bit ghastly....

public class NumericType extends LongType {
public void set(PreparedStatement st, Object value, int index) throws HibernateException, SQLException { st.setBigDecimal( index, new BigDecimal( BigInteger.valueOf(((Long)value).longValue()))); }

public int sqlType() { return Types.NUMERIC; }
}

Basically the field in our class is then a Long and we fake out the mapping from Long to Numeric here.

There's a side issue where every time we want to use HQL or criteria queries, we have to be very careful to always remember to call Query.setBigDecimal() and convert the Long to an BigInteger manually. If we don't do that (i.e. we call setLong or let Hibernate figure it out) then the Sybase JConnect driver emits SQL code which causes the database to ignore the index (because the types don't match) and we table scan the table.

What we want to do is to change our class to use BigInteger as the id column, because that's really what is needed with Sybase, but the checks in Hibernate won't allow that.


Guenther Enthaler added a comment - 24/Jul/08 12:03 AM

I tried extending IdentityGenerator to get around all this, and then came up against HBX-910.


Dejan Predovic added a comment - 19/Aug/08 09:07 AM

This issue is still open for both 3.2 and 3.3 branches.

We patched it by simply adding BigDecimal as an allowed type:

public static Serializable get(ResultSet rs, Type type) throws SQLException, IdentifierGenerationException {
Class clazz = type.getReturnedClass();
if ( clazz == Long.class ) { return new Long( rs.getLong( 1 ) ); }
else if ( clazz == Integer.class ) { return new Integer( rs.getInt( 1 ) ); }
else if ( clazz == Short.class ) { return new Short( rs.getShort( 1 ) ); }
else if ( clazz == String.class ) { return rs.getString( 1 ); }
else if ( clazz == java.math.BigDecimal.class ) { return rs.getBigDecimal( 1 ); }
else { throw new IdentifierGenerationException( "this id generator generates long, integer, short, bigdecimal or string" ); }

}

and it works, we see no side effects. It would be nice if this could find it's way into the trunk.


Guenther Enthaler added a comment - 19/Aug/08 06:42 PM

I'd like to add BigInteger as an allowed type:

...
else if ( clazz == java.math.BigDecimal.class ) { return rs.getBigDecimal( 1 ); }
else if (clazz == BigInteger.class) { return rs.getBigDecimal(1).toBigIntegerExact(); }
else { throw new IdentifierGenerationException( "this id generator generates long, integer, short, biginteger, bigdecimal or string" ); }

Likewise, this is working for us...


Dan added a comment - 14/Oct/08 03:20 PM

Anyone know how to use IdentifierGenerator is an extension point as suggested here?:

http://opensource.atlassian.com/projects/hibernate/browse/HHH-3125


Sarah MacLeod added a comment - 01/Nov/08 10:38 AM

Would you please consider removing Sybase 12.05 from the Environment field?

We are using MySQL 5 and just switched to Hibernate JPA from Toplink JPA.

We are affected by this bug because most of our auto-generated Ids are of type BigInteger.


Andrew Thompson added a comment - 03/Nov/08 09:49 AM

We couldn't find a way to change that field.


Dan added a comment - 04/Nov/08 11:39 AM

Leave the field a Long and use the @Type hibernate annotation like this:

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Type(type = "com.foo.util.SybaseIdentityType")
@Column(name = "iIdCon")
private Long id;

Here is the src for com.foo.util.SybaseIdentityType:

package com.foo.util;

import java.math.*;
import java.sql.*;

import org.hibernate.HibernateException;
import org.hibernate.type.LongType;

public class SybaseIdentityType extends LongType {

private static final long serialVersionUID = 1L;

public void set(PreparedStatement st, Object value, int index)
throws HibernateException, SQLException { st.setBigDecimal(index, new BigDecimal(BigInteger .valueOf(((Long) value).longValue()))); }

public int sqlType() { return Types.NUMERIC; }
}


chandesris added a comment - 11/Mar/09 06:26 AM

File replaced in the current GA release and it works for me.


Julien Kronegg added a comment - 17/Mar/09 07:06 AM

The proposed file IdentifierGeneratorFactory.java (# 14444 ) would not work for very high value because of this code snippet:

else if ( clazz==BigDecimal.class ) { return new BigDecimal( rs.getLong(1) ); }

If the database table primary key is a DECIMAL(21) or longer, (silent) overflow will occur since the max value is 10^21-1 for DECIMAL(21) and Long has a max value of 2^63-1, which is lower than 10^21-1.
You would use rs.getBigDecimal(1) instead of new BigDecimal( rs.getLong(1) )

The same would probably occur for the following code snippet:

else if ( clazz==BigInteger.class ) { return new BigInteger( "" + rs.getInt(1) ); }


Michel Nolard added a comment - 14/Apr/09 04:17 AM

Maybe nobody noticed it yet but this issue is nearly 3 years old !

It needs to be tackled down in an elegant and quick way for it appears in a very common use case : when using DBMS native ID systems
E.g: using Oracle, one creates a table whose ID is a value generated from a sequence -> the bug occurs
E.g: using Sybase, one creates a table whose ID is an auto-incremented value -> the bug occurs
E.g: using SQL Server, one creates a table whose ID is an auto-incremented value -> the bug occurs
...
the complete list is quite long.

I think 3 years for this simple thing is quite long... don't you think ?

Instead of adding the "not so JDBC standard compliant" implementation in the main IdentifierGeneratorFactory ( which is bad and I understand it), there should at least be an optional not fully compliant (but USEFUL) implementation providing the following seamless mappings:
Oracle's NUMBER( p, 0 ) -> BigInteger
Sybase-SqlServer's auto-increment -> BigInteger

Thanks by advance,

Michel Nolard