-->
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: Problem implementing a one-to-one mapping
PostPosted: Thu Mar 06, 2008 11:05 am 
Newbie

Joined: Thu Mar 06, 2008 4:03 am
Posts: 1
Ok, help with the following table association mapping problem would be much appreciated. I have been trying to solve this problem for a few days by applying sets, FK mapping etc to the various hbm.xml files but to no avail.


Hibernate version: 3



Oracle Database 10g Enterprise Edition Release 10.2.0.3.0 - 64bi
PL/SQL Release 10.2.0.3.0 - Production
CORE 10.2.0.3.0 Production
TNS for Linux: Version 10.2.0.3.0 - Production
NLSRTL Version 10.2.0.3.0 - Production


Code in test class that calls the findByID() method on the DAO:

try
{
PortDaoImpl portDaoImpl = new PortDaoImpl( PortDto.class );
Session session = HibernateSession.getCurrentSession();
Transaction transaction = session.beginTransaction();
PortDto hbPortDto = portDaoImpl.findByID( 1L );
transaction.commit();
}
catch (Exception e)
{
e.printStackTrace();
}


Method called in PortDaoImpl::class by the test class above;


public class PortDaoImpl....
// used in PortDto.hbm.xml
public static final String QUERY_PORT_ID = "findByID";

public PortDto findByID( Long portID )
{
return (PortDto)getCurrentSession().
getNamedQuery( QUERY_PORT_ID ).
setParameter( "id", portID ).
uniqueResult();
}
}


I have 2 tables defined in Oracle as follows;

TVS_PORT
PORT_ID <PK> NOT NULL
CNTRY_ID <FK> NOT NULL
..other rows

TVS_CNTRY
CNTRY_ID <PK> NOT NULL
..other rows

These are mapped using the following classes with their relevant hbm.xml files;


PortDto.hbm.xml

<?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>
<class name="PortDto" table="TVS_PORT">
<id name="id" column="PORT_ID" type="long">
<generator class="native"></generator>
</id>
<property name="cntryID" column="CNTRY_ID" type="long" not-null="true"/>
<many-to-one name="tvsCountry" class="CountryDto" column="CNTRY_ID" unique="false"
insert="false" update="false" not-null="true" cascade="none" />
</class>
<query name="findByID"><![CDATA[select p from PortDto p where p.id = :id ]]></query>
</hibernate-mapping>

PortDto::class maps to TVS_PORT table

public class PortDto
{
private Long id;
private Long cntryID;
private CountryDto tvsCountry;

getters & setters obviously created for this class
}

CountryDto.hbm.xml

<?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>
<class name="CountryDto" table="TVS_CNTRY">
<id name="countryId" column="CNTRY_ID" type="long">
<generator class="native"> </generator>
</id>
<property name="countryName"
column="CNTRY_NAME" type="string" not-null="true" insert="false" update="false"/>
<one-to-one name="port" class="PortDto" property-ref="tvsCountry" />

</class>
</hibernate-mapping>

CountryDto::class maps to TVS_CTRY table

public class CountryDto
{
private Long countryId;
private PortDto port;

getters & setters obviously created for this class
}


and this is the mapping in the Hibernate.cfg.xml file;


<!-- Mapping resources for the DTO files -->
<mapping resource="PortDto.hbm.xml"/>
<mapping resource="CountryDto.hbm.xml"/>



TVS_PORT can be updated with a full set of CRUD & finder methods applied to it.
However, the TVS_CNTRY table can only be amended at Administrator level. Therefore, a Create, Update or Delete on the TVS_PORT table will not make any changes to the TVS_CNTRY table &, as shown, the reference to the TVS_CNTRY <PK> is held in the TVS_PORT CNTRY_ID<FK> mapping.
(Sorry if this is becoming a convoluted explanation - just wanted to put down as much info as possible).

So, there is a basic one-to-one mapping between the TVS_PORT & TVS_COUNTRY tables.

If I create data in the dBase as follows and implement my findByID() method;

PORT_ID || CNTRY_ID
1 ||1
2 ||2

Hibernate works fine & sucks the data out the dBase with no problem.

However, if I the amend the data as follows where two different ports use the same Country;

PORT_ID || CNTRY_ID
1 ||1
2 ||1

the following error occurs as the ORM is trying to use the setter in the CountryDto class to set the PortDto but raises an exception cause the CNTRY_IDs found in the dBase are not unique per Port row;


Hibernate:
select
portdto0_.PORT_ID as PORT1_0_,
portdto0_.CNTRY_ID as CNTRY2_0_,
portdto0_.PORT_TYP_CODE as PORT3_0_,
portdto0_.PORT_CODE as PORT4_0_,
portdto0_.PORT_NAME as PORT5_0_,
portdto0_.CITY as CITY0_,
portdto0_.PERM_IPOC_YN as PERM7_0_,
portdto0_.JUXTAPOSED_YN as JUXTAPOSED8_0_,
portdto0_.STAFFED_YN as STAFFED9_0_,
portdto0_.CNCTD_TO_SYSTM_YN as CNCTD10_0_,
portdto0_.CREATE_TMSTMP as CREATE11_0_,
portdto0_.PORT_DESC as PORT12_0_,
portdto0_.REGION as REGION0_,
portdto0_.LST_UPDT_TMSTMP as LST14_0_
from
TVS_PORT portdto0_
where
portdto0_.PORT_ID=?
Hibernate:
select
countrydto0_.CNTRY_ID as CNTRY1_1_1_,
countrydto0_.CNTRY_NAME as CNTRY2_1_1_,
countrydto0_.CREATE_TMSTMP as CREATE3_1_1_,
countrydto0_.ISO_ALPHA2_CODE as ISO4_1_1_,
countrydto0_.ISO_ALPHA3_CODE as ISO5_1_1_,
countrydto0_.ISO_NUM3_CODE as ISO6_1_1_,
countrydto0_.MEMBER_OF_EC_YN as MEMBER7_1_1_,
countrydto0_.MEMBER_OF_EEA_YN as MEMBER8_1_1_,
countrydto0_.MEMBER_OF_EU_YN as MEMBER9_1_1_,
countrydto0_.LAST_UPDT_TMSTMP as LAST10_1_1_,
portdto1_.PORT_ID as PORT1_0_0_,
portdto1_.CNTRY_ID as CNTRY2_0_0_,
portdto1_.PORT_TYP_CODE as PORT3_0_0_,
portdto1_.PORT_CODE as PORT4_0_0_,
portdto1_.PORT_NAME as PORT5_0_0_,
portdto1_.CITY as CITY0_0_,
portdto1_.PERM_IPOC_YN as PERM7_0_0_,
portdto1_.JUXTAPOSED_YN as JUXTAPOSED8_0_0_,
portdto1_.STAFFED_YN as STAFFED9_0_0_,
portdto1_.CNCTD_TO_SYSTM_YN as CNCTD10_0_0_,
portdto1_.CREATE_TMSTMP as CREATE11_0_0_,
portdto1_.PORT_DESC as PORT12_0_0_,
portdto1_.REGION as REGION0_0_,
portdto1_.LST_UPDT_TMSTMP as LST14_0_0_
from
TVS_CNTRY countrydto0_,
TVS_PORT portdto1_
where
countrydto0_.CNTRY_ID=portdto1_.CNTRY_ID(+)
and countrydto0_.CNTRY_ID=?
org.hibernate.HibernateException: More than one row with the given identifier was found: 1, for class: com.tb.dai.persistence.dto.port.CountryDto
at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:69)
at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:42)
at org.hibernate.persister.entity.AbstractEntityPersister.load(AbstractEntityPersister.java:3044)
at org.hibernate.event.def.DefaultLoadEventListener.loadFromDatasource(DefaultLoadEventListener.java:395)
at org.hibernate.event.def.DefaultLoadEventListener.doLoad(DefaultLoadEventListener.java:375)
at org.hibernate.event.def.DefaultLoadEventListener.load(DefaultLoadEventListener.java:139)
at org.hibernate.event.def.DefaultLoadEventListener.onLoad(DefaultLoadEventListener.java:98)
at org.hibernate.impl.SessionImpl.fireLoad(SessionImpl.java:878)
at org.hibernate.impl.SessionImpl.immediateLoad(SessionImpl.java:836)
at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:66)
at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:111)
at org.hibernate.proxy.pojo.cglib.CGLIBLazyInitializer.invoke(CGLIBLazyInitializer.java:150)
at com.tb.dai.persistence.dto.port.CountryDto$$EnhancerByCGLIB$$ef195829.getCountryName(<generated>)
at com.tb.dai.persistence.dao.port.impl.PortDaoImplTest.comparePort(PortDaoImplTest.java:850)
at com.tb.dai.persistence.dao.port.impl.PortDaoImplTest.testFindByID(PortDaoImplTest.java:472)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:585)
at org.junit.internal.runners.TestMethodRunner.executeMethodBody(TestMethodRunner.java:99)
at org.junit.internal.runners.TestMethodRunner.runUnprotected(TestMethodRunner.java:81)
at org.junit.internal.runners.BeforeAndAfterRunner.runProtected(BeforeAndAfterRunner.java:34)
at org.junit.internal.runners.TestMethodRunner.runMethod(TestMethodRunner.java:75)
at org.junit.internal.runners.TestMethodRunner.run(TestMethodRunner.java:45)
at org.junit.internal.runners.TestClassMethodsRunner.invokeTestMethod(TestClassMethodsRunner.java:71)
at org.junit.internal.runners.TestClassMethodsRunner.run(TestClassMethodsRunner.java:35)
at org.junit.internal.runners.TestClassRunner$1.runUnprotected(TestClassRunner.java:42)
at org.junit.internal.runners.BeforeAndAfterRunner.runProtected(BeforeAndAfterRunner.java:34)
at org.junit.internal.runners.TestClassRunner.run(TestClassRunner.java:52)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:38)
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)


Ive tried various settings in the hbm.xml files & mapping the classes in various ways but cannot get the basic one-to-one mapping of a Port referencing a Country.
I have also seen a few other posts that seem to suggest that my problem may be a limitation of Hibernate, tho i can't see that as it is a basic ORM. Also, someone mentioned that changing the scope of the PortDto setter in the CountryDto class to private would work tho then ull just get a Hibernate error as the mapping uve defined cannot be applied.

Any ideas most welcome as I'm starting to sob into my keyboard.


Top
 Profile  
 
 Post subject: Reg: one to one mapping
PostPosted: Fri Mar 07, 2008 7:19 am 
Newbie

Joined: Thu Mar 06, 2008 1:28 am
Posts: 2
Even one to one mapping seems to be difficult in hibernate, you can always handle it from the Database side.

1) Add a unique constraint in the database.
2) Raise application error from db to java app
3) Add appropriate exception handling in the front end.


And one more thing, you have called uniqueResult . Moreover the database doesn't have such unique constraints.

uniqueResult
public Object uniqueResult()
throws HibernateExceptionConvenience method to return a single instance that matches the query, or null if the query returns no results.

Code:
Returns:
the single result or null
Throws:
NonUniqueResultException - if there is more than one matching result
HibernateException


NonUniqueResultException extends HibernateException
Thrown when the application calls Query.uniqueResult() and the query returned more than one result. Unlike all other Hibernate exceptions, this one is recoverable!

_________________
Everything has an answer.................................................. Either possible or not


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.