-->
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.  [ 9 posts ] 
Author Message
 Post subject: bidirectional one-to-one with polymorphism
PostPosted: Fri Jul 06, 2007 4:53 pm 
Newbie

Joined: Wed Aug 02, 2006 11:30 am
Posts: 7
I'm hoping somebody can point me at the documentation or an example that covers a situation such as this:

class A {
D dReference;
}

class B implements D{
A aReference;
}

class C implements D{
A aReference;
}

interface D {
}


The physical mapping will result in 3 tables, one each for classes A, B and C. And when I call A.getD(), I'll get either an instance of B or C back.

Please let me know if that makes sense. I'm wondering how to write the mapping files for the classes.

Thanks!


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jul 09, 2007 11:36 am 
Newbie

Joined: Wed Aug 02, 2006 11:30 am
Posts: 7
Has anybody done this before?

Thanks.


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jul 13, 2007 6:19 am 
Newbie

Joined: Fri Jun 08, 2007 4:20 am
Posts: 13
Hi, I am having a similar problem.

Did you find an answer for this?


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jul 13, 2007 6:41 am 
Senior
Senior

Joined: Thu May 17, 2007 2:31 am
Posts: 194
Location: Sri Lanka
Hi

send your database structure for 3 tables


Amila


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jul 13, 2007 4:42 pm 
Newbie

Joined: Wed Aug 02, 2006 11:30 am
Posts: 7
amila733 wrote:
Hi

send your database structure for 3 tables


Amila


There are no restrictions on the database structure, other than the fact that there needs to be one table per concrete class (A, B and C).

It's the relationship between classes that has been defined; I'm just looking for the hibernate mapping files to translate those relationships to the database.

Thanks.


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jul 13, 2007 7:52 pm 
Expert
Expert

Joined: Fri Jul 13, 2007 8:18 am
Posts: 370
Location: london
Hi feh

I've implemented your example scenario using table-per-subclass to create a separate table for A, B and C.

I couldn't get this working using the D interface alone - added an abstract class 'DImpl' that implements D and is extended by concrete classes B and C. There are private (hibernate only) get/set methods for the DImpl in A and public get/set methods for DReference.

The test case creates 2 instances of A in the database - one with A as the DRef, one with B as the DRef. When the A's are re-loaded from the DB I print the class names of the DRef. As expected they are A and B;)

My HibernateUtil is configured to use hsqldb.

Mike


class A
Code:
package test.feh;

public class A {

   private Long id;
   private DImpl dImpl;
   
   public Long getId() {
      return id;
   }
   public void setId(Long id) {
      this.id = id;
   }
   public D getDReference() {
      return dImpl;
   }
   public void setDReference(D dReference) {
      if (!(dReference instanceof DImpl)) {
         throw new IllegalArgumentException("DReference must be an instance of class DImpl");
      }
      dReference = (DImpl)dReference;
   }
   
   @SuppressWarnings("unused")
   private DImpl getDImpl() {
      return dImpl;
   }
   @SuppressWarnings("unused")
   private void setDImpl(DImpl impl) {
      dImpl = impl;
   }
}



class B
Code:
package test.feh;

public class B extends DImpl {
}



class C
Code:
package test.feh;

public class C extends DImpl {
}



interface D
Code:
package test.feh;
public interface D {
}



abstract class DImpl
Code:
package test.feh;

public abstract class DImpl implements D {
   private Long id;
   private A aReference;

   public Long getId() {
      return id;
   }

   public void setId(Long id) {
      this.id = id;
   }

   public A getAReference() {
      return aReference;
   }

   public void setAReference(A reference) {
      aReference = reference;
   }
}



class HibernateUtil
Code:
package test.feh;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class HibernateUtil {

    private static SessionFactory factory;

    static {
       Configuration config = new Configuration()
          .addClass(A.class)
          .addClass(DImpl.class)
          .setProperty("hibernate.dialect", "org.hibernate.dialect.HSQLDialect")
         .setProperty("hibernate.connection.driver_class", "org.hsqldb.jdbcDriver")
         .setProperty("hibernate.connection.url", "jdbc:hsqldb:mem:test")
          .setProperty("hibernate.connection.username", "sa")
          .setProperty("hibernate.connection.password", "")
          .setProperty("hibernate.hbm2ddl.auto", "create-drop")
         .setProperty("hibernate.cache.provider_class", "org.hibernate.cache.NoCacheProvider")
         .setProperty("hibernate.show_sql", "true");
        HibernateUtil.setSessionFactory(config.buildSessionFactory());
    }

    public static synchronized Session getSession() {
        if (factory == null) {
            factory = new Configuration().configure().buildSessionFactory();
        }
        return factory.openSession();
    }

    public static void setSessionFactory(SessionFactory factory) {
        HibernateUtil.factory = factory;
    }
}



class TestIt
Code:
package test.feh;

import junit.framework.TestCase;

import org.hibernate.Hibernate;
import org.hibernate.Session;

public class TestIt extends TestCase {

   public void testLoad() {
      Long aWithBId = createDatabaseObjects(new B());
      Long aWithCId = createDatabaseObjects(new C());

      Session session = HibernateUtil.getSession();
      A aWithB = (A)session.load(A.class, aWithBId);
      A aWithC = (A)session.load(A.class, aWithCId);
      Hibernate.initialize(aWithB);
      Hibernate.initialize(aWithC);
      session.close();
      
      System.out.println("A with B - DReference class = "+aWithB.getDReference().getClass());
      System.out.println("A->B->A.id: "+aWithB.getDReference().getAReference().getId());
      System.out.println("A with C - DReference class = "+aWithC.getDReference().getClass());
      System.out.println("A->C->A.id: "+aWithC.getDReference().getAReference().getId());

   }

   private Long createDatabaseObjects(D d) {
      Session session = HibernateUtil.getSession();
      session.beginTransaction();
      
      session.save(d);
      A a = new A();
      a.setDReference(d);
      Long aId = (Long)session.save(a);
      
      session.getTransaction().commit();
      session.close();
      return aId;
   }
   
}



Mapping File A.hbm.xml
Code:
<?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 package="test.feh">

<class name="A" table="a_table">
   <id name="id" column="id">
      <generator class="increment"/>
   </id>

   <one-to-one name="DImpl"/>
   
</class>

</hibernate-mapping>



Mapping File DImpl.hbm.xml
Code:
<?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 package="test.feh">

<class name="DImpl" table="d_table">
    <id name="id" column="id">
        <generator class="native"/>
    </id>

    <one-to-one name="AReference"/>
   
    <joined-subclass name="B" table="b_table">
        <key column="id"/>
    </joined-subclass>

    <joined-subclass name="C" table="c_table">
        <key column="id"/>
    </joined-subclass>
</class>

</hibernate-mapping>


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jul 23, 2007 3:36 pm 
Newbie

Joined: Wed Aug 02, 2006 11:30 am
Posts: 7
Thank you very much. It was very nice of you to spend the time to put that example together.

Unfortunately, there is one problem with it - it actually uses 4 tables (d_table contains DImpls), therefor there are 4 tables, not 3. That won't work for me, as this is an existing application, and the tables b_table and c_table already exist. Therefor, trying to migrate the data from those tables to the new d_table isn't workable.

I probably should've mentioned that originally, but didn't want to make a complicated situation more confusing!

So, getting back to my original situation, the tables representing objects B and C already exist, Table A would be new.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Jul 25, 2007 5:44 pm 
Expert
Expert

Joined: Fri Jul 13, 2007 8:18 am
Posts: 370
Location: london
Ok, understood. If you use union-subclass instead there's no need for a fourth table. However, it requires an id generation strategy capable of generating unique ids over the b/c tables, e.g. a single sequence for the ids of both tables.

If your existing b/c table data already has overlapping ids you could be in trouble. One possibility is a programatic approach - define a one-to-one relationship between a->b and a->c, ensuring that both cannot be set at the same time. Calls to getDRef() would return the instance of either B or C, whichever isn't null. This would break any HQL queries you have that reference D because D is no longer a hibernate entity, solely an object in your Java domain.

Mike


union-subclass example:
Code:
<?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 package="test.feh2">

<class name="DImpl">
    <id name="id" column="id">
        <generator class="increment">
        </generator>
    </id>

    <one-to-one name="AReference"/>
   
    <union-subclass name="B" table="b_table">
    </union-subclass>

    <union-subclass name="C" table="c_table">
    </union-subclass>
</class>

</hibernate-mapping>


Top
 Profile  
 
 Post subject:
PostPosted: Thu Jul 26, 2007 9:04 am 
Newbie

Joined: Wed Aug 02, 2006 11:30 am
Posts: 7
thatmikewilliams wrote:
Ok, understood. If you use union-subclass instead there's no need for a fourth table. However, it requires an id generation strategy capable of generating unique ids over the b/c tables, e.g. a single sequence for the ids of both tables.

If your existing b/c table data already has overlapping ids you could be in trouble. One possibility is a programatic approach - define a one-to-one relationship between a->b and a->c, ensuring that both cannot be set at the same time. Calls to getDRef() would return the instance of either B or C, whichever isn't null. This would break any HQL queries you have that reference D because D is no longer a hibernate entity, solely an object in your Java domain.

Mike


Mike - once again, thanks!

I've been doing a lot of reading and experimenting over the last couple days, and the fact that the PKs for tables B and C overlap does seem to throw a wrench into things, so I'm glad you backed up my opinion.

I'm starting to think there may not be a solution for the problem as currently defined. I may have to muck with the existing data and/or use an alternate schema.


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