-->
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.  [ 5 posts ] 
Author Message
 Post subject: Problem with mapping inheritance
PostPosted: Tue Sep 30, 2008 1:11 pm 
Newbie

Joined: Tue Sep 30, 2008 12:43 pm
Posts: 3
Hello,

im relative new to hibernate and constantly running into a problem for severall hours now. I hope you can help.
Im building up a new web applikation and started developing my domain model. I need to persist two implementing classes of an interface:

Code:
public interface IInsurance {

   public Integer getId();

   public void setId(Integer id);

   public String getName();

   public void setName(String name);

   public Set<IPatient> getPatients();

   public void setPatients(Set<IPatient> patients);
   
   public void addPatient(IPatient patient);

}

Code:
public class HealthInsuranceImpl implements IInsurance {

   private Integer         id;
   private String         name;
   private Set<IPatient>   patients   = new HashSet<IPatient>();

   public Integer getId() {
      return id;
   }

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

   public String getName() {
      return name;
   }

   public void setName(String name) {
      this.name = name;
   }

   public Set<IPatient> getPatients() {
      return patients;
   }

   public void setPatients(Set<IPatient> patients) {
      this.patients = patients;
   }

   public void addPatient(IPatient patient) {
      patient.setInsurance(this);
      if (patients == null) {
         patients = new HashSet<IPatient>();
      }
      patients.add(patient);
   }

}

Code:
public class CareInsuranceImpl implements IInsurance {

   private Integer         id;
   private String         name;
   private Set<IPatient>   patients   = new HashSet<IPatient>();
   
   
   public Integer getId() {
      return id;
   }

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


   public String getName() {
      return name;
   }

   public void setName(String name) {
      this.name = name;
   }


   public Set<IPatient> getPatients() {
      return patients;
   }

   public void setPatients(Set<IPatient> patients) {
      this.patients = patients;
   }
   
   public void addPatient(IPatient patient) {
      patient.setInsurance(this);
      if (patients == null) {
         patients = new HashSet<IPatient>();
      }
      patients.add(patient);
   }

}


on the otherhand i got a class Patient. So one insurance can have many patients:

Code:
public class PatientImpl implements IPatient {

   // persistant fields
   private Integer            id;
   private String            firstName;
   private String            lastName;
   private PatientStateEnum   state;
   private Date            dateOfBirth;
   private IInsurance         insurance;

   public Integer getId() {
      return id;
   }

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

.
.
.

   public IInsurance getInsurance() {
      return insurance;
   }

   public void setInsurance(IInsurance insurance) {
      this.insurance = insurance;
   }

}


this is the mapping file for the Patient class:
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>
   <class name="PatientImpl"
      table="patient">
      <id name="id" type="java.lang.Integer">
         <column name="id" />
         <generator class="native" />
      </id>

      <many-to-one name="insurance" column="insurance_fk"
         class="IInsurance" cascade="save-update"/>

   </class>
</hibernate-mapping>


Im trying to map the interface using the one table per concrete subclass strategy. This is the mapping file for the interface (and the subclasses)

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>
   <class name="IInsurance"
      abstract="true">
      <id name="id" type="java.lang.Integer">
         <column name="id" />
         <generator class="native" />
      </id>
      <union-subclass
         name="HealthInsuranceImpl"
         table="insurance_health">

         <property name="name" column="name" length="255" not-null="true" />
         <set name="patients" inverse="false" cascade="save-update">
            <key column="insurance_fk" />
            <one-to-many
               class="PatientImpl" />
         </set>
      </union-subclass>
      <!--
      <union-subclass
         name="CareInsuranceImpl"
         table="insurance_care">
         <property name="name" column="name" length="255" not-null="true" />
         <set name="patients" inverse="false" cascade="save-update">
            <key column="insurance_fk" />
            <one-to-many
               class="PatientImpl" />
         </set>
      </union-subclass>
      -->
   </class>
</hibernate-mapping>


notice that the subclass "CareInsuranceImpl" is commented out. so only one subclass of the interface is actually mapped. Running my Unittest with this configuration works. My objects get written into the database correctly.
Here comes the problem in. As soon as i comment the second subclass in (and thus map it) my test code fails. I get following error:
Quote:
INFO - Hibernate 3.2.6
INFO - hibernate.properties not found
INFO - Bytecode provider name : cglib
INFO - using JDK 1.4 java.sql.Timestamp handling
INFO - configuring from file: hibernate.cfg.xml
INFO - Reading mappings from resource : hibernate-mapping/PatientImpl.hbm.xml
INFO - Mapping class: quelltextschmiede.papp.papp3.domain.patient.PatientImpl -> patient
INFO - Reading mappings from resource : hibernate-mapping/IInsurance.hbm.xml
INFO - Mapping class: quelltextschmiede.papp.papp3.domain.insurance.IInsurance -> IInsurance
INFO - Mapping union-subclass: quelltextschmiede.papp.papp3.domain.insurance.HealthInsuranceImpl -> insurance_health
INFO - Mapping union-subclass: quelltextschmiede.papp.papp3.domain.insurance.CareInsuranceImpl -> insurance_care
INFO - Configured SessionFactory: null
INFO - Mapping collection: quelltextschmiede.papp.papp3.domain.insurance.HealthInsuranceImpl.patients -> patient
INFO - Mapping collection: quelltextschmiede.papp.papp3.domain.insurance.CareInsuranceImpl.patients -> patient
INFO - Using Hibernate built-in connection pool (not for production use!)
INFO - Hibernate connection pool size: 20
INFO - autocommit mode: false
INFO - using driver: oracle.jdbc.OracleDriver at URL: jdbc:oracle:thin:@localhost:1521:XE
INFO - connection properties: {user=papp, password=****}
INFO - RDBMS: Oracle, version: Oracle Database 10g Express Edition Release 10.2.0.1.0 - Production
INFO - JDBC driver: Oracle JDBC driver, version: 10.2.0.1.0XE
INFO - Using dialect: org.hibernate.dialect.Oracle10gDialect
INFO - Using default transaction strategy (direct JDBC transactions)
INFO - No TransactionManagerLookup configured (in JTA environment, use of read-write or transactional second-level cache is not recommended)
INFO - Automatic flush during beforeCompletion(): disabled
INFO - Automatic session close at end of transaction: disabled
INFO - JDBC batch size: 15
INFO - JDBC batch updates for versioned data: disabled
INFO - Scrollable result sets: enabled
INFO - JDBC3 getGeneratedKeys(): disabled
INFO - Connection release mode: auto
INFO - Default batch fetch size: 1
INFO - Generate SQL with comments: disabled
INFO - Order SQL updates by primary key: disabled
INFO - Order SQL inserts for batching: disabled
INFO - Query translator: org.hibernate.hql.ast.ASTQueryTranslatorFactory
INFO - Using ASTQueryTranslatorFactory
INFO - Query language substitutions: {}
INFO - JPA-QL strict compliance: disabled
INFO - Second-level cache: enabled
INFO - Query cache: disabled
INFO - Cache provider: org.hibernate.cache.NoCacheProvider
INFO - Optimize cache for minimal puts: disabled
INFO - Structured second-level cache entries: disabled
INFO - Echoing all SQL to stdout
INFO - Statistics: disabled
INFO - Deleted entity synthetic identifier rollback: disabled
INFO - Default entity-mode: pojo
INFO - Named query checking : enabled
INFO - building session factory
INFO - Not binding factory to JNDI, no JNDI name configured
INFO - Running hbm2ddl schema export
INFO - exporting generated schema to database
INFO - schema export complete
Hibernate:
select
hibernate_sequence.nextval
from
dual
Hibernate:
select
hibernate_sequence.nextval
from
dual
Hibernate:
insert
into
insurance_health
(name, id)
values
(?, ?)
Hibernate:
insert
into
patient
(first_name, last_name, state, date_of_birth, insurance_fk, id)
values
(?, ?, ?, ?, ?, ?)
WARN - SQL Error: 2291, SQLState: 23000
ERROR - ORA-02291: Integritäts-Constraint (PAPP.FKD0D3EB05BABC3590) verletzt - übergeordneter Schlüssel nicht gefunden

WARN - SQL Error: 2291, SQLState: 23000
ERROR - ORA-02291: Integritäts-Constraint (PAPP.FKD0D3EB05BABC3590) verletzt - übergeordneter Schlüssel nicht gefunden

ERROR - Could not synchronize database state with session
org.hibernate.exception.ConstraintViolationException: Could not execute JDBC batch update
at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:71)
at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:43)
at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:253)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:266)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:167)
at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:298)
at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:27)
at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1000)
at quelltextschmiede.papp.papp3.domain.patient.SaveTest.testSave(SaveTest.java:64)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at junit.framework.TestCase.runTest(TestCase.java:168)
at junit.framework.TestCase.runBare(TestCase.java:134)
at junit.framework.TestResult$1.protect(TestResult.java:110)
at junit.framework.TestResult.runProtected(TestResult.java:128)
at junit.framework.TestResult.run(TestResult.java:113)
at junit.framework.TestCase.run(TestCase.java:124)
at junit.framework.TestSuite.runTest(TestSuite.java:232)
at junit.framework.TestSuite.run(TestSuite.java:227)
at org.junit.internal.runners.JUnit38ClassRunner.run(JUnit38ClassRunner.java:79)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:45)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:460)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:673)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:386)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:196)
Caused by: java.sql.BatchUpdateException: ORA-02291: Integritäts-Constraint (PAPP.FKD0D3EB05BABC3590) verletzt - übergeordneter Schlüssel nicht gefunden

at oracle.jdbc.driver.DatabaseError.throwBatchUpdateException(DatabaseError.java:498)
at oracle.jdbc.driver.OraclePreparedStatement.executeBatch(OraclePreparedStatement.java:12369)
at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:48)
at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:246)
... 25 more
INFO - closing
INFO - cleaning up connection pool: jdbc:oracle:thin:@localhost:1521:XE
[/code]

Here is my testcode:

Code:
   public void testSave() {
   
      Session session = sessionFactory.openSession();
      
      IInsurance insurance = new HealthInsuranceImpl();
      insurance.setName("BLA");
      
      PatientImpl patient = new PatientImpl();
      patient.setFirstName("Hans");
      patient.setLastName("Müller3");
      patient.setState(PatientStateEnum.ACTIVE);
      patient.setDateOfBirth(new Date());
      
      insurance.addPatient(patient);
      
      session.saveOrUpdate(insurance);
      session.flush();
      
      session.close();
   }
[/code]

this is very strange, because everything works fine if i just have one subclass of the interface mapped. As soon i have the second subclass mapped, i get this error. Please help :(
P.S.: im using Oracle Database 10g Express Edition


Top
 Profile  
 
 Post subject:
PostPosted: Tue Sep 30, 2008 1:54 pm 
Expert
Expert

Joined: Mon Nov 26, 2007 2:29 pm
Posts: 443
The Hibernate documentation is so confusing, so cryptic and Laconic, that it makes you loose sight of one simple fact: you cannot do illegal things to the database.

Think for a moment what you are trying to do (exclusively in terms of database integrity)

You are trying to force the same child table to have a FK to 2 different parent tables. When you add a child connected to one parent, the other, inevitably will complain!

Your options are: either choosing another inheritance mapping strategy that uses only one parent table (such as "table per class hierarchy"), or using 2 different tables for the children (Patients) of each subclass.

_________________
Gonzalo Díaz


Top
 Profile  
 
 Post subject:
PostPosted: Tue Sep 30, 2008 4:06 pm 
Newbie

Joined: Tue Sep 30, 2008 12:43 pm
Posts: 3
thanks much for your quick reply
i understand now why this cant work :-)

Here is a little classdiagram from this part of the domain model im trying to map:
Image

Im planning to widely make use of interfaces in my domain model. So i guess this type of relation between classes and interfaces will be very common.
What would you suggest how to map these relations? I got a bad feeling that i cant avoid to have additional tables for the superclasses (interfaces in my case), but maybe im wrong?
Some personal hints, weblinks or literature recommendations regarding this problem are very welcome! :-)

Georg


Top
 Profile  
 
 Post subject:
PostPosted: Tue Sep 30, 2008 5:21 pm 
Expert
Expert

Joined: Mon Nov 26, 2007 2:29 pm
Posts: 443
It seems that Care and Health are very similar. Why not use a single table for both, with a discriminator field?
In that way, your FK problems disappear, and you are not forced to use a double set of Patients.

_________________
Gonzalo Díaz


Top
 Profile  
 
 Post subject:
PostPosted: Tue Sep 30, 2008 6:39 pm 
Newbie

Joined: Tue Sep 30, 2008 12:43 pm
Posts: 3
I mapped both subclasses in the same table using a discriminator field as u discribed and this works well. I had a bad feeling about this because i thought this is somehow bad database design. But well i can live with it, if this is considered quite "normal" (sorry, im new to the whole topic ORM).
Thanks much for your help and hints!

Georg


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