52

This is my Database structure, One-to-One mapping in MySQL:

DB Schema

This is my java file:

public class Employee {

private EmployeeDetail empdetail;
private String firstname;
private String lastname;

// getters and setters 

}


public class EmployeeDetail {
    private Employee employee ;
    private int employee_id;
    private String street;
    private String city;
    private String state;
    private String country;

// getters and setters 

}

This is my mapping file:

<hibernate-mapping>

     <class name="Employee" table="employee">
        <id name="employee_id"  type="java.lang.Integer"  column="employee_id">
            <generator class="assigned" />
        </id>
        <one-to-one name="empdetail" class="EmployeeDetail"
            cascade="all"></one-to-one>

        <property name="firstname" type="java.lang.String" column="firstname" />
        <property name="lastname" type="java.lang.String" column="lastname" />

    </class>



 <class name="EmployeeDetail" table="employeedetail">
        <id name="employee_id" type="java.lang.Integer"  column="employee_id">
       <generator class="foreign">
                <param name="property">employee</param>
            </generator>
        </id>
        <one-to-one name="employee" class="Employee"
            cascade="save-update"></one-to-one>

        <property name="street"  type="java.lang.String" column="street" />
        <property name="city" type="java.lang.String" column="city" />
        <property name="state" type="java.lang.String" column="state" />
        <property name="country" type="java.lang.String" column="country" />

 </class>

</hibernate-mapping>

This is my client program:

Session session = factory.openSession();
        Transaction tx = session.beginTransaction();

        EmployeeDetail edetail = new EmployeeDetail();
        edetail.setCity("Hyd");
        edetail.setCountry("India");
        edetail.setEmployee_id(222);
        edetail.setState("Andhra Pradesh");

        Employee employee = new Employee();

        employee.setEmployee_id(222);
        employee.setFirstname("Pavan");
        employee.setLastname("Jaooi");
        employee.setEmpdetail(edetail);

        session.merge(employee);
        tx.commit();
        session.close();
        factory.close();

This is the exception I am getting:

INFO: Not binding factory to JNDI, no JNDI name configured
Hibernate: select employee0_.employee_id as employee1_0_1_, employee0_.firstname as firstname0_1_, employee0_.lastname as lastname0_1_, employeede1_.employee_id as employee1_1_0_, employeede1_.street as street1_0_, employeede1_.city as city1_0_, employeede1_.state as state1_0_, employeede1_.country as country1_0_ from employee employee0_ left outer join employeedetail employeede1_ on employee0_.employee_id=employeede1_.employee_id where employee0_.employee_id=?
Hibernate: select employeede0_.employee_id as employee1_1_0_, employeede0_.street as street1_0_, employeede0_.city as city1_0_, employeede0_.state as state1_0_, employeede0_.country as country1_0_ from employeedetail employeede0_ where employeede0_.employee_id=?
Exception in thread "main" org.hibernate.id.IdentifierGenerationException: attempted to assign id from null one-to-one property: employee
    at org.hibernate.id.ForeignGenerator.generate(ForeignGenerator.java:44)
    at org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:99)
    at org.hibernate.event.def.DefaultMergeEventListener.entityIsTransient(DefaultMergeEventListener.java:186)
    at org.hibernate.event.def.DefaultMergeEventListener.entityIsDetached(DefaultMergeEventListener.java:240)
    at org.hibernate.event.def.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:120)
    at org.hibernate.impl.SessionImpl.fireMerge(SessionImpl.java:687)
    at org.hibernate.impl.SessionImpl.merge(SessionImpl.java:669)
    at org.hibernate.engine.CascadingAction$6.cascade(CascadingAction.java:245)
    at org.hibernate.engine.Cascade.cascadeToOne(Cascade.java:268)
    at org.hibernate.engine.Cascade.cascadeAssociation(Cascade.java:216)
    at org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:169)
    at org.hibernate.engine.Cascade.cascade(Cascade.java:130)
    at org.hibernate.event.def.AbstractSaveEventListener.cascadeAfterSave(AbstractSaveEventListener.java:456)
    at org.hibernate.event.def.DefaultMergeEventListener.entityIsTransient(DefaultMergeEventListener.java:194)
    at org.hibernate.event.def.DefaultMergeEventListener.entityIsDetached(DefaultMergeEventListener.java:240)
    at org.hibernate.event.def.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:120)
    at org.hibernate.event.def.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:53)
    at org.hibernate.impl.SessionImpl.fireMerge(SessionImpl.java:677)
    at org.hibernate.impl.SessionImpl.merge(SessionImpl.java:661)
    at org.hibernate.impl.SessionImpl.merge(SessionImpl.java:665)
    at CustomerClient.main(CustomerClient.java:31)
naXa stands with Ukraine
  • 35,493
  • 19
  • 190
  • 259
Pawan
  • 31,545
  • 102
  • 256
  • 434

2 Answers2

93

You told Hibernate to generate the EmployeeDetail ID from the ID of its employee property, but you never initialized this property.

Add edetail.setEmployee(employee); to your code.

JB Nizet
  • 678,734
  • 91
  • 1,224
  • 1,255
  • 3
    Thanks , but why employee.setEmpdetail(edetail); isn't sufficient ?? why do i need to do edetail.setEmployee(employee); also ?? – Pawan Jun 20 '12 at 05:35
  • 6
    Because you're supposed to maintain the coherence of the object graph and, more important, because you configured the EmployeeDetail entity with an ID generator which gets the ID of EmployeeDetail from its `employee` property. So obviously, if this property is null, Hibernate can't generate the ID. That's what is clearly said in the error message. – JB Nizet Jun 20 '12 at 05:54
  • 15
    I thought hibernate is supposed to do this automatically. When a ParentEntity p has a collection, can't it be assumed that all c within p's children have a parent of p? Why do I have to loop through and set all of them manually in order to avoid the null id error in hibernate? – pete Oct 22 '14 at 20:18
  • 2
    @PeterJames Because that's how it works and is specified. Entities are POJOs. Hibernate can't magically set the parent of a child when you add it to a parent's collection. – JB Nizet Oct 22 '14 at 20:27
  • 16
    Yea, but others ORMs do it so easily, because it really "supposed to do this automatically". This error is not a fault of the programmer, but the ORM itself – Gaspar Apr 02 '19 at 18:33
  • So you're telling me `generator class="foreign"` doesn't actually generate anything? – Collin Jan 30 '23 at 18:56
  • I've created a hibernate forum post for this https://discourse.hibernate.org/t/generator-class-foreign-does-not-actually-generate-anything/7208 – Collin Jan 30 '23 at 19:28
2

I got the same error here. In my case I was trying to create the relation one-to-one, using the shared Primary Key between the parent and the child class. I resolved the issue by adding in my parent class, inside the "set" method for the definition of the child class, the "this.childClassName.setParentClass(this)".

e.g:

Parent Class

@Entity
@Table(name = "TB_CLIENTS")
public class ClientModel {

    @Id
    @Column(name = "cpf", nullable = false, length = 11)
    private String cpf;
    
    @Column(name = "full_name", nullable = false, length = 80)
    private ClientName fullName;

    @OneToOne(mappedBy = "client", cascade = CascadeType.ALL)
    @PrimaryKeyJoinColumn
    private HomeAddress homeAddress; // the instance of the child class inside the parent class

    ... getters and setters

    public void setHomeAddress(HomeAddress homeAddress) {
        this.homeAddress = homeAddress;
        this.homeAddress.setClient(this); // setting the parent class as the value for the child instance
    }

}

Child Class

@Entity
@Table(name = "TB_HOME_ADDRESS")
public class HomeAddress {
    
    @Id
    @Column(name = "client_cpf")
    private String id;
    
    @OneToOne 
    @MapsId
    @JoinColumn(name = "client_cpf")
    private ClientModel client;
    
    ... other attributes + getters and setters
}