-->
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.  [ 2 posts ] 
Author Message
 Post subject: Dynamic-runtime Hibernate schema change
PostPosted: Thu Dec 06, 2007 9:24 am 
Beginner
Beginner

Joined: Tue Apr 24, 2007 12:53 pm
Posts: 28
Hopefully this post makes sense and someone can assist me. I have also posted this in the Spring User's forum because even though it is a Hibernate question, I'm attempting to use Spring's support functionality.


My organization partitions its data into multiple schema's. In my example we would have a single table, defined as follows:

Code:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="my.package.domain.model">
    <class name="Person" table="PERSON" lazy="false" >
        <id name="id" column="ID" type="java.lang.String" >
            <generator class="assigned" />
        </id>

        <property name="givenName" column="GIVEN_NAME" type="java.lang.String" not-null="true" />

        <property name="surname" column="SURNAME" type="java.lang.String" not-null="true" />
       
        <property name="birthDate" column="BIRTH_DATE" type="java.util.Date" not-null="true" />
       
    </class>
</hibernate-mapping>



This table would actually exist in 5 schemas. Depending on some property of the object, for this example we'll use birth dates, the object will be persisted to a specific schema.

ie:
if the person's brithday is before 1950, it goes in "SCHEMA_A"
if the person's birthday is after 1950 but before 1970, it goes in "SCHEMA_B",
etc.

Now, since we're using Spring & Hibernate, we want to have the following spring bean definitions:

Code:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
    <bean id="DataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
        <property name="jndiName" value="jdbc/MyDataSource" />
    </bean>

    <bean id="MySessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
        <property name="mappingResources">
            <list>
                <value>Person.hbm.xml</value>
                <value>PersonSchemaMap.hbm.xml</value>
            </list>
        </property>
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.DB2Dialect</prop>
                <prop key="hibernate.default_schema">DEFAULT</prop>
                <prop key="hibernate.show_sql">false</prop>
                <prop key="hibernate.format_sql">false</prop>
                <prop key="hibernate.query.substitutions">true 'Y', false 'N', yes 'Y', no 'N'</prop>
            </props>
        </property>
        <property name="dataSource">
            <ref bean="MyDataSource" />
        </property>
    </bean>

    <bean id="PersonDao" class="my.package.dao.hibernate.HibernatePersonDao" >
        <!-- Extends Spring's HibernateDaoSupport class -->       
        <property name="sessionFactory" ref="MySessionFactory" />
    </bean>

</beans>


The Crux of the problem:

All of the Schemas exist in the same Database, so the same SessionFactory should suffice. Except that when defined, the SessionFactory's schema is set and cannot be changed.

Some gotchas: The DAO's all extend the HibernateDaoSupport class of Spring, which allows us to properly manage transactions and isolation levels, in a declarative manner. Therefore we can't go arbitrarily creating new SessionFactories (via programmatic interface), or we'll break the Spring support for this.

We can't use multiple SessionFactory instances, as this would require we define multiple DAO bean instances, which in turn would require that anything using it know which bean-id to look for (Kinda defeats the purpose, no?)

What I'm really looking for is some way to...

intercept the session after the call to a "getHibernateTemplate()" method (eg: save) is made,
change the setting of the schema on it,
allow the original requested functionality to perform,
return the schema value to the previous setting,
return to the DAO that made the call to "getHibernateTemplate()" method.

OR - Change the stored Schema value on the Session before it builds the SQL, and change it back after


But all of that without breaking the transaction/isolation support and keeping it platform independent. I know that Hibernate has a project in the works for supporting something like this (Hibernate Shards) but I can't use a beta codebase for anything I'm doing.

Does this make sense? Is it possible with the current Spring/Hibernate frameworks, or am I out of luck til Hibernate Shards is complete?

Thanks for any help...

-B


Top
 Profile  
 
 Post subject: More Info
PostPosted: Fri Dec 07, 2007 11:04 am 
Beginner
Beginner

Joined: Tue Apr 24, 2007 12:53 pm
Posts: 28
I'm posting this response to provide more information about my original problem, hopefully someone can assist me.

The real core problem I'm running into is the SQL generation.

Lets start by providing a better example than my previous one:

Code:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="my.companys.package">
    <class name="Member" table="MEMBER" lazy="false" >
        <id>
            <key-property name="id" column="ID" type="java.lang.String" >
            <generator class="assigned" />
        </id>
       
        <property name="customerId" column="CUSTOMER_ID" type="java.lang.Integer" />
                   
        <property name="firstName" column="FIRST_NAME" type="java.lang.String" not-null="true" />

        <property name="lastName" column="LAST_NAME" type="java.lang.String" not-null="true" />

        <property name="dateOfBirth" column="BIRTH_DATE" type="java.util.Date" not-null="false" />

        <property name="addressLine1" column="ADDR_LINE_1" type="java.lang.String" not-null="true" />

        <property name="addressLine2" column="ADDR_LINE_2" type="java.lang.String" not-null="false" />

        <property name="city" column="CITY" type="java.lang.String" not-null="true" />

        <property name="state" column="ST" type="java.lang.String" not-null="true" />

        <property name="zip" column="ZIP_CDE" type="java.lang.String" not-null="true" />
    </class>
</hibernate-mapping>




Our Member data is partitioned across multiple schemas. The schema in which a member resides is determined by using the Customer ID on the Member instance. Customer A's data cannot co-exist in the same table as Customer B's data.

When SessionFactory instances are created, you define a default schema for all of the Sessions that will be created. You don't necessarily have to define the default schema for the SessionFactory, however if you don't, it will use whatever the default schema is for the connection.

When you go to use the Session to create a Criteria, or run a HQL query, at some point, it converts all of this into a native SQL string to be executed. If a default schema is defined, this information is used to qualify the table names for the mapped entities when generating the SQL string.

What I would like to do is provide a means to plug-in a class that knows how to differentiate which schema to use based on the instance of the mapped entity to be found. In essence: change the schema that will be used when the SQL generation is done, for each Entity in the query. Meaning: if it's a multi-tiered mapped structure, each mapped entity is used to resolve the schema for that entities SQL string.

This functionality would need to work for all of the operations supported for an Entity.

Obviously I would define the class that would make the determination. But from what I can see in the API, theres no real way to change the schema value once/if its defined.

I could use multiple connections/SessionFactory instances, but this would require that I create 100+ connection pools. Not something I want to do. (And we use JNDI configured connection pools so c3p0 is out...)

Does anyone know if this is possible? Does it seem like its something that would be useful?

I tried to find out where exactly in the framework the SQL is generated, so i could find out if it'd be possible to wrap an interceptor around that part and update that value. Unfortunately I haven't been able to dig that deep into the framework (as it is way, way, way down there, necessarily so). As such I'm still at a loss for what to do.

Any help would really be great. Thanks in advance.

-B


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 2 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.