-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 6 posts ] 
Author Message
 Post subject: how to add a type override for a dialect in hibernate 4
PostPosted: Sun Jan 15, 2012 3:49 pm 
Newbie

Joined: Sun Jan 15, 2012 3:27 pm
Posts: 1
I have an entity class that contains a java.util.UUID field
Code:
@Entity
public class Item {
    private UUID uuid;
}


I want to use the native uuid sql type in postgresql and the default sql type for uuid in other databases (e.g. h2).

In hibernate 3, I just need to implement a DialectResolver
Code:
public class DialectResolver extends BasicDialectResolver {

    public DialectResolver() {
        super("PostgreSQL", PostgreSQLDialect.class);
    }

    public static class PostgreSQLDialect extends org.hibernate.dialect.PostgreSQLDialect {
        public PostgreSQLDialect() {
            addTypeOverride(new PostgresUUIDType() {
                @Override
                protected boolean registerUnderJavaType() {
                    return true;
                }
            });
        }
    }
}


Then I can switch between postgresql and h2 databases by changing the driver class and jdbc url only. Everything else is handled automatically.

In hibernate 4, I couldn't find any way to do it. The Dialect.addTypeOverride method is removed in hibernate 4.

Is it possible to add a type override for a dialect in hibernate 4?


Top
 Profile  
 
 Post subject: Re: how to add a type override for a dialect in hibernate 4
PostPosted: Sun Mar 25, 2012 7:11 am 
Newbie

Joined: Mon Jun 02, 2008 11:03 am
Posts: 3
Hopefully you've solved this by now, but here is the hack I have come up with for posterity.

I created a user type which is basically a copy of org.hibernate.type.PostgresUUIDType and added this to the UUID columns:

Code:
package  net.zorq.censeo.domain;

import org.hibernate.type.AbstractSingleColumnStandardBasicType;
import org.hibernate.type.descriptor.ValueBinder;
import org.hibernate.type.descriptor.ValueExtractor;
import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.descriptor.java.UUIDTypeDescriptor;
import org.hibernate.type.descriptor.sql.BasicBinder;
import org.hibernate.type.descriptor.sql.BasicExtractor;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.UUID;

/**
* @author David Beaumont
* @see org.hibernate.type.PostgresUUIDType
*/
public class UUIDType extends AbstractSingleColumnStandardBasicType<UUID> {
    public static final UUIDType INSTANCE = new UUIDType();

    public UUIDType() {
        super( UUIDSqlTypeDescriptor.INSTANCE, UUIDTypeDescriptor.INSTANCE );
    }

    @Override
    public String getName() {
        return "custom-uuid";
    }

    public static class UUIDSqlTypeDescriptor implements SqlTypeDescriptor {
        public static final UUIDSqlTypeDescriptor INSTANCE = new UUIDSqlTypeDescriptor();

        @Override
        public int getSqlType() {
            return Types.OTHER;
        }

        @Override
        public boolean canBeRemapped() {
            return true;
        }

        @Override
        public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) {
            return new BasicBinder<X>( javaTypeDescriptor, this ) {
                @Override
                protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options) throws SQLException {
                    /**
                     * This check ensures the normal path is taken for UUIDs on
                     * other databases. This is the single place where this
                     * class differs from that bundled in Hibernate at
                     * org.hibernate.type.PostgresUUIDType
                     */
                    if(st.getConnection().getMetaData().getDriverName().equals("org.postgresql.Driver")) {
                        st.setObject( index, javaTypeDescriptor.unwrap( value, UUID.class, options ), Types.OTHER );
                    } else {
                        st.setObject( index, javaTypeDescriptor.unwrap( value, UUID.class, options ));
                    }

                }
            };
        }

        @Override
        public <X> ValueExtractor<X> getExtractor(final JavaTypeDescriptor<X> javaTypeDescriptor) {
            return new BasicExtractor<X>( javaTypeDescriptor, this ) {
                @Override
                protected X doExtract(ResultSet rs, String name, WrapperOptions options) throws SQLException {
                    return javaTypeDescriptor.wrap( rs.getObject( name ), options );
                }
            };
        }
    }
}


The only difference is that before PreparedStatement.setObject is called, a check is done to see which database driver is in use. In my test cases, I am using H2 rather than postgresql. Some more work is needed, as with the user type, H2 doesn't know how to create the columns for JDBC type Types.OTHER. To circumvent this, I created a new dialect:

Code:
package net.zorq.censeo.util;

import org.hibernate.HibernateException;

import java.sql.Types;

/**
* Needed to keep UUIDs in H2 working after specifying a user type to make
* UUIDs use Postgres' native type in production.
* This needs doing because in Hibernate 4 you can no longer add type overrides
* per dialect. See: https://forum.hibernate.org/viewtopic.php?p=2451813
* @see net.zorq.censeo.domain.UUIDType
* @author David Beaumont
*/
public class H2Dialect extends org.hibernate.dialect.H2Dialect {

    public String getTypeName(int code, long length, int precision, int scale) throws HibernateException {
        if(code == Types.OTHER) {
            return "binary(16)";
        } else {
            return super.getTypeName(code, length, precision, scale);
        }
    }

}


Then in my test classes, I make sure that new dialect is the one used. This all seems very fragile (only works for two specified databases) so it would if any Hibernate devs are listening, what's the proper way of achieving this?

David


Top
 Profile  
 
 Post subject: Re: how to add a type override for a dialect in hibernate 4
PostPosted: Sat Apr 21, 2012 1:29 pm 
Newbie

Joined: Mon Jun 02, 2008 11:03 am
Posts: 3
My above implementation didn't quite work - you could read existing UUIDs in the database but not save any new rows.

I've done a better job and written it up here: https://zorq.net/b/2012/04/21/switching-hibernates-uuid-type-mapping-per-database/


Top
 Profile  
 
 Post subject: Re: how to add a type override for a dialect in hibernate 4
PostPosted: Fri Feb 28, 2014 3:02 pm 
Newbie

Joined: Fri Feb 28, 2014 2:54 pm
Posts: 1
overriding the type in a new pg dialect like this worked for me in hibernate 4.3:

Code:
public class PostgresUuidDialect extends PostgreSQL9Dialect {

    @Override
    public void contributeTypes(final TypeContributions typeContributions, final ServiceRegistry serviceRegistry) {
        super.contributeTypes(typeContributions, serviceRegistry);
        typeContributions.contributeType(new InternalPostgresUUIDType());
    }

    protected static class InternalPostgresUUIDType extends PostgresUUIDType {

        @Override
        protected boolean registerUnderJavaType() {
            return true;
        }
    }
}


Top
 Profile  
 
 Post subject: Re: how to add a type override for a dialect in hibernate 4
PostPosted: Fri Sep 12, 2014 11:26 am 
Newbie

Joined: Fri Sep 12, 2014 11:23 am
Posts: 1
hiberhead, that worked where all else failed. Thanks for the post!


Top
 Profile  
 
 Post subject: Re: how to add a type override for a dialect in hibernate 4
PostPosted: Wed Apr 01, 2015 11:16 am 
Newbie

Joined: Sun Aug 11, 2013 2:00 am
Posts: 3
[quote="skunk"]My above implementation didn't quite work - you could read existing UUIDs in the database but not save any new rows.

I've done a better job and written it up here:

skunk,

any idea how to make your solution work with spring? It seems that this solution is the correct one. I just can no figure how to force spring read the class UUIDCustomType in the beginning i.e. it has to be registered somehow.


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 6 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.