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.
|