-->
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.  [ 7 posts ] 
Author Message
 Post subject: Part of composit primary key as part of composit foreign key
PostPosted: Thu Mar 02, 2006 11:05 am 
Newbie

Joined: Thu Mar 02, 2006 9:18 am
Posts: 3
Hi,

I have two tables with a relation one-to-many from CONTINENT to COUNTRY

Here are the tables:

CONTINENT (id_continent, id_language, continent)
COUNTRY (id_country, id_language,id_continent, country)


So, id_language is shared by both the foreign and the primary keys in COUNTRY table

I'd like to use hibernate mapping in this context through a class key containing id_X and id_language properties.

Do you think it's possible?




Thanks
Guillaume


Top
 Profile  
 
 Post subject:
PostPosted: Thu Mar 02, 2006 6:01 pm 
Expert
Expert

Joined: Thu Dec 23, 2004 9:08 pm
Posts: 2008
Absolutely. The mapping from continent to country is easy, I presume that it's the reverse mapping you're interested in. So long as you use "formula" elements/attrbiutes in the foreign key when referring to columns that have already been mapped (obviously the id_language column, in your case), then the normal rules apply. You'd get something like this:
Code:
<class name="Country" ...>
  ...
  <many-to-one name="Continent" ...>
    <column name="id_continent"/>
    <formula>id_language</formula>
  </many-to-one>
  ...
</class>


Top
 Profile  
 
 Post subject: Replacing column tag with formula causes NPE in many-to-many
PostPosted: Fri Apr 28, 2006 1:15 pm 
Newbie

Joined: Tue Sep 13, 2005 9:15 am
Posts: 4
Hi,

I am facing a similar challenge with a legacy database (that of course cannot be altered). I have 2 tables that have 2 natural composite keys each and a join table. The 2 tables share one of the composite keys just like the previous posting.

However, instead of a one-to-many relation, we were trying to implement a many-to-many with a join table and were getting the "org.hibernate.MappingException: Repeated column in mapping for collection: ..." error.

After researching the Forum, we tried the workaround in the previous posting (replacing the <column> with the <formula> tag) on the repeated key and this resulted in a NullPointerException.

Oddly enough it worked when we replaced <column> with <formula> on BOTH keys, but this only worked on read operations. When inserting, it tries to put NULL into the other key column.

I have all the details to recreate this. I tried to boil this down to a simple runnable example. Here are the table defs:

Code:
CREATE TABLE `table1` (
  `id_a` int(11) NOT NULL default '0',
  `id_b` int(11) NOT NULL default '0',
  `data` char(20) default NULL,
  PRIMARY KEY  (`id_a`,`id_b`)
)

CREATE TABLE `table2` (
  `id_b` int(11) NOT NULL default '0',
  `id_c` int(11) NOT NULL default '0',
  `data` char(20) default NULL,
  PRIMARY KEY  (`id_b`,`id_c`)
)

CREATE TABLE `join12` (
  `id_a` int(11) NOT NULL default '0',
  `id_b` int(11) NOT NULL default '0',
  `id_c` int(11) NOT NULL default '0',
  PRIMARY KEY  (`id_a`,`id_b`,`id_c`)
)



Here are the mappings for Table1 and Table2

Code:
<hibernate-mapping package="domain" >
  <class name="Table1Class" table="TABLE1" >

    <composite-id>
      <key-property name="id_a" column="ID_A" type="integer" />
      <key-property name="id_b" column="ID_B" type="integer" />
    </composite-id>      
    <property name="data" column="DATA" type="string" />
      
    <bag name="table2Items" table="join12">      
      <key>
        <column name="id_a" />
        <column name="id_b"/>
      </key>
      <many-to-many class="Table2Class">         
        <formula>id_b</formula>
        <column name="id_c"/>            
      </many-to-many>
    </bag>
  </class>
</hibernate-mapping>


<hibernate-mapping package="domain" >
  <class name="Table2Class" table="TABLE2">
    <composite-id>
      <key-property name="id_b" column="ID_B" type="integer" />
      <key-property name="id_c" column="ID_C" type="integer" />
    </composite-id>      
    <property name="data" column="DATA" type="string" />
  </class>
</hibernate-mapping>


To save space the classes are not listed. We did make sure they were Serializable and that both Equals and hashCode were implemented correctly.

When running this you get the following stack trace: (please note that the stack trace is from our production code rather than the example above. Everything above buildSessionFactory is the same.)

Code:
java.lang.NullPointerException
   at org.hibernate.util.StringHelper.replace(StringHelper.java:78)
   at org.hibernate.util.StringHelper.replace(StringHelper.java:66)
   at org.hibernate.util.StringHelper.replace(StringHelper.java:72)
   at org.hibernate.persister.collection.AbstractCollectionPersister.getElementColumnNames(AbstractCollectionPersister.java:796)
   at org.hibernate.loader.OuterJoinLoader.walkCollectionTree(OuterJoinLoader.java:250)
   at org.hibernate.loader.OuterJoinLoader.walkCollectionTree(OuterJoinLoader.java:217)
   at org.hibernate.loader.collection.CollectionLoader.<init>(CollectionLoader.java:75)
   at org.hibernate.loader.collection.CollectionLoader.<init>(CollectionLoader.java:58)
   at org.hibernate.loader.collection.CollectionLoader.<init>(CollectionLoader.java:49)
   at org.hibernate.loader.collection.BatchingCollectionInitializer.createBatchingCollectionInitializer(BatchingCollectionInitializer.java:92)
   at org.hibernate.persister.collection.BasicCollectionPersister.createCollectionInitializer(BasicCollectionPersister.java:272)
   at org.hibernate.persister.collection.AbstractCollectionPersister.postInstantiate(AbstractCollectionPersister.java:472)
   at org.hibernate.impl.SessionFactoryImpl.<init>(SessionFactoryImpl.java:248)
   at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1005)
   at com.nppc.mes.hibernate.HibernateConnector.<init>(HibernateConnector.java:127)
   at com.nppc.mes.hibernate.HibernateConnector.createHibernateConnector(HibernateConnector.java:91)
   at com.nppc.mes.hibernate.HibernateConnector.createDefault(HibernateConnector.java:87)
   at com.nppc.mes.batching.carma.Carma.initializeApplication(Carma.java:242)
   at com.nppc.mes.AbstractApplicationStartup.start(AbstractApplicationStartup.java:454)
   at com.nppc.mes.batching.carma.Carma.main(Carma.java:145)


I have debugged this and found that, if it is a bug, that it involves the elementFormulaTemplates array member of the AbstractCollectionPersister class. The array is initialized in the constructor from a column iterator. I noticed here that all non-formulas are skipped and the value at the corresponding index is left as null in the elementFormulaTemplates array. Later from the getElementColumnNames() method (same class), it calls down into several StringHelper.replace() methods that assume that the template String argument will never be null when it calls template.indexOf().

I realize that 99% of these are actually user error and not bugs in the system. If I'm making a conceptual mistake, could you please steer me in the right direction? If this is a bug that has been addressed then my apologies for missing it in my JIRA search.

We are reshuffling some of our stories in our project to buy us some time in hopes of being able to find a workaround for this. Please let me know if you need any more information. Just wanted to say that Hibernate is a most excellent tool!

Thanks very much for your help in this!

Jerry


Top
 Profile  
 
 Post subject:
PostPosted: Sun Apr 30, 2006 6:18 pm 
Expert
Expert

Joined: Thu Dec 23, 2004 9:08 pm
Posts: 2008
Before I think too hard about this one, I've got to ask why you're repeating column "id_b" in the join table mapping. That doesn't look right. At first glance I'd say that you want to refer to just id_a in the bag's key, and use two columns in the many-to-many association.

If there's a genuine need to repeat the id_b column, then my recommendation would be to make the table1 to table2 association inverse, so that you can safely use formula attributes in the many-to-many mapping. This will require that Table2Class objects are created, saved, then added to any Table1Class objects. The mapping above would allow them to be created, added to a Table1Class object, then cascade-saved from there: you'd lose that ability but gain the ability to use the formula attributes, if you map that mapping inverse.

_________________
Code tags are your friend. Know them and use them.


Top
 Profile  
 
 Post subject:
PostPosted: Tue May 02, 2006 3:17 pm 
Newbie

Joined: Tue Sep 13, 2005 9:15 am
Posts: 4
Hi,

Thank you for your response.

I agree that something feels wrong here. The relationship between Table1 and Table2 is straighforward I think. Table1Class holds onto a list of Table2Class items. We only need unidirectional navigation from the Table1Class to the list of Table2Class items. We know there's a one-to-many relationship there, but there is also a many-to-one because a Table2Class item can be shared between different Table1Class objects.

The column is repeated because the column in the join table is shared in two separate foreign keys on both sides of the join. In a situation with surrogate keys, this is a no brainer. You have Table1 with key 'a' and Table2 with key 'b' and the join table would have 2 columns 'a' and 'b'. The composite keys on some of the legacy tables are throwing us for a loop . Here's an example that describes our situation. Table1 has composte key 'a' and 'b' and Table2 has composite key 'c' and 'd'. One would think that the join table would be all 4 columns 'a' thru 'd'. In our case, the join table only has 3 columns because 'b' and 'c' are actually the same natural key.

I've thought about this and tried the mapping changes you suggested (only one column id_a in the key and 2 columns, id_b and id_c in the many-to-many mapping) and we get the following error (please note the stack trace is from the version at work)

Code:
2006-05-01 08.46.47  resolving reference to class: com.nppc.mes.batching.carma.domain.CampaignFinishedProduct
org.hibernate.MappingException: Foreign key (FKB8023DE13627C279:PACK_LINE_CODES [GCM_ID])) must have same number of columns as the referenced primary key (PACK_COMPLIANCE [GCM_ID,MANF_UPC_C])
   at org.hibernate.mapping.ForeignKey.alignColumns(ForeignKey.java:86)
   at org.hibernate.mapping.ForeignKey.setReferencedTable(ForeignKey.java:51)
   at org.hibernate.cfg.Configuration.secondPassCompileForeignKeys(Configuration.java:976)
   at org.hibernate.cfg.Configuration.secondPassCompile(Configuration.java:921)
   at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:999)
   at com.nppc.mes.hibernate.HibernateConnector.<init>(HibernateConnector.java:127)
   at com.nppc.mes.hibernate.HibernateConnector.createHibernateConnector(HibernateConnector.java:91)
   at com.nppc.mes.hibernate.HibernateConnector.createDefault(HibernateConnector.java:87)
   at com.nppc.mes.batching.carma.Carma.initializeApplication(Carma.java:242)
   at com.nppc.mes.AbstractApplicationStartup.start(AbstractApplicationStartup.java:454)
   at com.nppc.mes.batching.carma.Carma.main(Carma.java:145)
2006-05-01 08.46.47  Application Terminated due to exception in startup.


Seen this one before. Hibernate requires the number of foreign key columns from the join table (PACK_LINE_CODES) to match the number of primary key columns in Table1 (PACK_COMPLIANCE). I tried this the other way just for grins (2 columns in the key (id_a and id_b) and only one in the many-to-many mapping (id_c) and got a similar error:


Code:
2006-05-01 08.56.46  resolving reference to class: com.nppc.mes.batching.carma.domain.LineCode
org.hibernate.MappingException: Foreign key (FKB8023DE187BA4136:PACK_LINE_CODES [LINE_CODE])) must have same number of columns as the referenced primary key (CARMA_LINE_CODES_V [UPC,LINE_CODE])
   at org.hibernate.mapping.ForeignKey.alignColumns(ForeignKey.java:86)
   at org.hibernate.mapping.ForeignKey.setReferencedTable(ForeignKey.java:51)
   at org.hibernate.cfg.Configuration.secondPassCompileForeignKeys(Configuration.java:976)
   at org.hibernate.cfg.Configuration.secondPassCompile(Configuration.java:921)
   at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:999)
   at com.nppc.mes.hibernate.HibernateConnector.<init>(HibernateConnector.java:127)
   at com.nppc.mes.hibernate.HibernateConnector.createHibernateConnector(HibernateConnector.java:91)
   at com.nppc.mes.hibernate.HibernateConnector.createDefault(HibernateConnector.java:87)
   at com.nppc.mes.batching.carma.Carma.initializeApplication(Carma.java:242)
   at com.nppc.mes.AbstractApplicationStartup.start(AbstractApplicationStartup.java:454)
   at com.nppc.mes.batching.carma.Carma.main(Carma.java:145)


Now the number of columns in the foreign key don't match the number of primary keys from Table2 (CARMA_LINE_CODES_V).

I will give some thought about your second suggestion and try out a few things.

Thanks again for your help!

Jerry


Top
 Profile  
 
 Post subject:
PostPosted: Tue May 02, 2006 11:48 pm 
Expert
Expert

Joined: Tue Apr 25, 2006 12:04 pm
Posts: 260
Reference documentation has an example mapping, If in case, you havent looked at it.

http://www.hibernate.org/hib_docs/v3/re ... manytomany


Top
 Profile  
 
 Post subject:
PostPosted: Thu May 04, 2006 11:07 am 
Newbie

Joined: Tue Sep 13, 2005 9:15 am
Posts: 4
As usual, I find things the hard way. I wound up finding the <formula> technique thru forum postings, but that example 23.4.3 is right on.

The only difference is that we wouldn't have the inverse on the "users" set in the mapping for the Group class. For us, there is no set mapping for the "User" class because we only need unidirectional navigation from Group to a list of User items.

I'm wondering if were making any conceptional mistakes there. There's nothing that requires us to make the navigation bidirectional in this case, is there?

Other than that, I don't see any significant difference from the example and what we are trying to do. We are on version 3, but not the latest release. I've been on the JIRA trying to see if this is a recently-fixed bug (because I'm getting management pushback about getting the latest), but I'm about ready to just grab the latest and see if it works.

Thanks for the help, it is much appreciated!


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