Type scoping

Version 7

    There are two main goals here:

    1. create the ability for *non-basic* Types to have access to the SessionFactory
    2. allow users to override the basic Type used for particular mappings


    SessionFactory access

    Currently types are completely unscoped.  The only way they have access to a Session or SessionFactory is by passing those into the API methods.  The issue is that historically we have added these piecemeal so that today we have a bunch of new methods accepting a Session or a SessionFactory and in turn delegating to methods which do not.  For basic types (strings, ints, etc) this is not at all an issue.  However in the case of association types this is a huge problem since they need access to their underlying persiasters to do anything.  It is also a big impediment to moving to this scheme for components.  Ideally I'd really like for all (what JPA 2 refers to as) "managed types" to always have access to the SessionFactory.  The biggest gain here is to allow these "managed types" to offload a lot of their logic to the SessionFactory and to access it when needed.  This is in fact the exact model followed by the entity and collection types except that they do not always have access to the SessionFactory.


    When I say "always", yes, we need to keep in mind still the 2 phases of building a SessionFactory and that the types may or may not be available when building the Configuration.


    Type overridding

    This is the more user-visible (and certainly the more user-requested) of the goals.  Lets look at this by way of an example.  Say you are running against an Oracle database and you are using a VARCHAR column.  Oracle has a long outstandaing "feature" where it treats an empty string in a special, but unfortunately not symmetric, manner.  If you bind an empty string to a PreparedStatement for writing to a VARCHAR column, Oracle will convert that into a NULL.  However, it does not do the same when you bind an empty string for say a WHERE-clause restriction.  So literally even though you passed it an empty string to store and are now trying to match based on an empty string you will get no matches.  So what you'd really like to have happen is to convert the empty string to a NULL before passing to the driver.


    These column-level concerns are the realm of Types.  However the built-in Hibernate are not going to perform such value manipulation.  So developers would need to write a custom Type to handle this. So the developer writes their ImprovedOracleStringType class. That is fine and works insofar as it goes.  The concern here is that the developer must then explicitly specify this custom type everywhere they want to use it (on each property mapping!).  That is tedious.  Ideally, there would be a way for them to ask Hibernate to use their custom ImprovedOracleStringType class whenever it would normally resolve to use its built-in StringType for a property.


    The proposal then is to introduce a TypeRegistry which the binder code would use to locate the proper types to use for binding to properties.  This TypeRegistry would need a scheme for defining the keys under which the Types are stored and by which clients (the binders being the primary use case) can get Types.  The initial proposal here is to follow a scheme similar to the current way "basic" types are handled on the existing TypeFactory class (see http://fisheye.jboss.org/browse/Hibernate/core/trunk/core/src/main/java/org/hibernate/type/TypeFactory.java?r=18568#l67)


    The Design

    Types model a specific style of binding between a Java type and one or more JDBC types.  Often times we ended up with duplicate code in Types for dealing with particular Java types as well as particular JDBC types.  So the first thing I did was to split these concepts out on their own:

    1. http://anonsvn.jboss.org/repos/hibernate/core/trunk/core/src/main/java/org/hibernate/type/descriptor/java/ describes the Java type aspects
    2. http://anonsvn.jboss.org/repos/hibernate/core/trunk/core/src/main/java/org/hibernate/type/descriptor/sql/ describes the JDBC type aspects
    3. http://anonsvn.jboss.org/repos/hibernate/core/trunk/core/src/main/java/org/hibernate/type/descriptor/ describes the contracts used to communicate between the other 2

    Specifically, these are used to describe the aspects of "basic" types (mapping a single column/JDBC-type to a Java class).


    I then modelled this a little more concretely in the Type hierarchy by introducing the BasicType and SingleColumnType interfaces.  For the most part (see AbstractSingleColumnStandardBasicType) a basic type is as simple as combining one of the org.hibernate.type.descriptor.sql.SqlTypeDescriptor and one of the org.hibernate.type.descriptor.java.JavaTypeDescriptor together.


    BasicTypes are also special because they can be registered with a BasicTypeRegistry.  The BasicTypeRegistry is what provides the ability for application developers to override the basic types implementations used when resolving mappings.  The BasicTypeRegistry is available immediately from the Configuration, so an application developer might immediately get the BasicTypeRegistry and register the specific type(s) they want (overriding the defaults).  This "registration" process is the purpose of the BasicType.getRegistrationKeys() method; it defines the keys under which the BasicType is to be registered within the BasicTypeRegistry.


    Up next...

    Now on to the scoping of non-basic types..



    Related JIRA issues