Unidirection OnetoOne mapping foreign primary key is not generating in child table

1k views Asked by At

I have one parent table CUSTOMER_ACCOUNT and one child table CUSTOMER_TEMPINFO.

I'm trying to generate the primary key in parent table and assign the same in child table.

I've to use the unidirectional way, so bidirectional solution please until absolutely necessary.

Is there something wrong in the association order!

Need to go from child -> parent?

Need some clue and proper explanation!

Here are the hbm mappings.

CustomerAccount.hbm.xml

<hibernate-mapping>
<class name="com.rup.example.po.CustomerAccount" table="CUSTOMER_ACCOUNT">
    <id name="customerId" type="int" column="customer_id">
        <generator class="native" />
    </id>
    <property name="customerName" column="customer_name" type="string" />
    <property name="birthDay" column="birthday" type="date" />
    <one-to-one name="tempInfo" 
        class="com.rup.example.po.CustomerTempInfo"
        property-ref="customerId"
        cascade="all" />
</class>
</hibernate-mapping>

CustomerTempInfo.hbm.xml

<hibernate-mapping>
<class name="com.rup.example.po.CustomerTempInfo" table="CUSTOMER_TEMPINFO">
    <id name="customerId" type="int" column="customer_id">
        <generator class="foreign">

        </generator>
    </id>
    <property name="passCode" column="pass_code" type="string" />
    <property name="codeExpiryDate" column="code_expiry_date" type="date" />
</class>
</hibernate-mapping>

And I'm getting the following trace:-

Initial SessionFactory creation failed.org.hibernate.MappingException: Could not instantiate id generator [entity-name=com.rup.example.po.CustomerTempInfo]
org.hibernate.MappingException: Could not instantiate id generator [entity-name=com.rup.example.po.CustomerTempInfo]
    at org.hibernate.id.factory.internal.DefaultIdentifierGeneratorFactory.createIdentifierGenerator(DefaultIdentifierGeneratorFactory.java:123)
    at org.hibernate.mapping.SimpleValue.createIdentifierGenerator(SimpleValue.java:191)
    at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:305)
    at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1737)
    at com.rup.example.util.HibernateUtil.buildSessionFactory(HibernateUtil.java:24)
    at com.rup.example.util.HibernateUtil.getSessionFactory(HibernateUtil.java:36)
    at com.rup.example.main.TestMainApp.main(TestMainApp.java:21)
Caused by: org.hibernate.MappingException: param named "property" is required for foreign id generation strategy
    at org.hibernate.id.ForeignGenerator.configure(ForeignGenerator.java:88)
    at org.hibernate.id.factory.internal.DefaultIdentifierGeneratorFactory.createIdentifierGenerator(DefaultIdentifierGeneratorFactory.java:117)
    ... 6 more
Exception in thread "main" java.lang.NullPointerException
    at com.rup.example.main.TestMainApp.main(TestMainApp.java:40)

UPDATE 20150610

Even after considering Bi-directional way, Was able to insert record but not lucky while reading the same back. This time got the following trace -

Session created
Hibernate: select hibernate_sequence.nextval from dual
Hibernate: insert into CUSTOMER_TEMPINFO (pass_code, code_expiry_date, customer_id) values (?, ?, ?)
Hibernate: insert into CUSTOMER_ACCOUNT (customer_name, birthday, customer_id) values (?, ?, ?)
Customer ID=22
Hibernate: select customerac0_.customer_id as customer_id1_0_0_, customerac0_.customer_name as customer_name2_0_0_, customerac0_.birthday as birthday3_0_0_ from CUSTOMER_ACCOUNT customerac0_ where customerac0_.customer_id=?
Exception occured. null
java.lang.NullPointerException
    at org.hibernate.persister.entity.AbstractEntityPersister.loadByUniqueKey(AbstractEntityPersister.java:2385)
    at org.hibernate.type.EntityType.loadByUniqueKey(EntityType.java:767)
    at org.hibernate.type.EntityType.resolve(EntityType.java:505)
    at org.hibernate.engine.internal.TwoPhaseLoad.doInitializeEntity(TwoPhaseLoad.java:170)
    at org.hibernate.engine.internal.TwoPhaseLoad.initializeEntity(TwoPhaseLoad.java:144)
    at org.hibernate.loader.plan.exec.process.internal.AbstractRowReader.performTwoPhaseLoad(AbstractRowReader.java:244)
    at org.hibernate.loader.plan.exec.process.internal.AbstractRowReader.finishUp(AbstractRowReader.java:215)
    at org.hibernate.loader.plan.exec.process.internal.ResultSetProcessorImpl.extractResults(ResultSetProcessorImpl.java:140)
    at org.hibernate.loader.plan.exec.internal.AbstractLoadPlanBasedLoader.executeLoad(AbstractLoadPlanBasedLoader.java:138)
    at org.hibernate.loader.plan.exec.internal.AbstractLoadPlanBasedLoader.executeLoad(AbstractLoadPlanBasedLoader.java:102)
    at org.hibernate.loader.entity.plan.AbstractLoadPlanBasedEntityLoader.load(AbstractLoadPlanBasedEntityLoader.java:186)
    at org.hibernate.persister.entity.AbstractEntityPersister.load(AbstractEntityPersister.java:4126)
    at org.hibernate.event.internal.DefaultLoadEventListener.loadFromDatasource(DefaultLoadEventListener.java:503)
    at org.hibernate.event.internal.DefaultLoadEventListener.doLoad(DefaultLoadEventListener.java:468)
    at org.hibernate.event.internal.DefaultLoadEventListener.load(DefaultLoadEventListener.java:213)
    at org.hibernate.event.internal.DefaultLoadEventListener.onLoad(DefaultLoadEventListener.java:146)
    at org.hibernate.internal.SessionImpl.fireLoad(SessionImpl.java:1070)
    at org.hibernate.internal.SessionImpl.immediateLoad(SessionImpl.java:976)
    at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:174)
    at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:286)
    at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:185)
    at com.rup.example.po.CustomerAccount_$$_jvst751_1.toString(CustomerAccount_$$_jvst751_1.java)
    at java.lang.String.valueOf(String.java:2981)
    at java.lang.StringBuilder.append(StringBuilder.java:131)
    at com.rup.example.main.TestMainApp.printTransactionData(TestMainApp.java:65)
    at com.rup.example.main.TestMainApp.main(TestMainApp.java:38)
Closing SessionFactory
Jun 10, 2015 11:55:19 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl stop
INFO: HHH000030: Cleaning up connection pool [jdbc:oracle:thin:@localhost:1521:xe]
2

There are 2 answers

2
Guillermo On BEST ANSWER

Unfortunately you must specify from which associated entity you want to get the ID value, through the required property property to initialize the foreign generator. So you need to add at least the association from child to parent. Something like this:

<hibernate-mapping>
<class name="com.rup.example.po.CustomerTempInfo" table="CUSTOMER_TEMPINFO">
    <id name="customerId" type="int" column="customer_id">
        <generator class="foreign">
            <param name="property">parent</param>
        </generator>
    </id>
    <property name="passCode" column="pass_code" type="string" />
    <property name="codeExpiryDate" column="code_expiry_date" type="date" />
    <one-to-one name="parent" class="com.rup.example.po.CustomerAccount" constrained="true" />
</class>
</hibernate-mapping>

A useful link: see 5.1.2.2.7. Identity copy (foreign generator)

0
Rup Majumder On

Okay I got it now. The thumb rule is with one-to-one mapping, you can't let hibernate to assign the PK of child entity. I had to separately assign the PK in child after doing a saveOrUpdate.

Here are the changes I made.

removed property-ref

<one-to-one name="tempInfo" 
    class="com.rup.example.po.CustomerTempInfo"
    cascade="all"/>

assigned generator

<class name="com.rup.example.po.CustomerTempInfo" table="CUSTOMER_TEMPINFO">
    <id name="customerId" type="int" column="customer_id">
        <generator class="assigned"/>
    </id>
    <property name="passCode" column="pass_code" type="string" />
    <property name="codeExpiryDate" column="code_expiry_date" type="timestamp" />
</class>

and here goes the main method :-

CustomerAccount account = new CustomerAccount();
account.setBirthDay(new Date());
account.setCustomerName("Rup Majumder");

tx = session.beginTransaction();
session.saveOrUpdate(account);
CustomerTempInfo tempInfo = new CustomerTempInfo();
tempInfo.setCodeExpiryDate(new Date());
tempInfo.setPassCode("ABCD");
tempInfo.setCustomerId(account.getCustomerId());
account.setTempInfo(tempInfo);
tx.commit();

Worked with 4.0.1.Final