Hibernate throws "org.hibernate.id.IdentifierGenerationException: attempted to assign id from null one-to-one property"


#1

Hi,
I am trying to create onetoone mapping between two tables where the parent key primary key acts as the primary key for child as well. While trying to save parent I am getting the following error.

message": "attempted to assign id from null one-to-one property [com.newModel.Compensation.order]; nested exception is org.hibernate.id.IdentifierGenerationException: attempted to assign id from null one-to-one property [com.newModel.Compensation.order]"

Please find the below console log, model classes and service class used for the same. Can someone pls help to resolve the error.

Basically want to transfer the order id from order class to order id under compensation using crud repo

Parent class:

package com.newModel;
import java.io.Serializable;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.OneToOne;
import javax.persistence.Table;

@Entity
@Table(name="ORDERS")
public class Order implements Serializable {
    private static final long serialVersionUID = 1L;

    @Id
    @Column(name="ORDER_ID")
    private String orderId;

    @Column(name="ACCESS_ID")
    private String accessId;

    @OneToOne(cascade=CascadeType.ALL,mappedBy="order",fetch=FetchType.EAGER)
	private Compensation compensation;
   
//getters & setters	
	}

Child class

package com.newModel;

import java.io.Serializable;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.MapsId;
import javax.persistence.NamedQuery;
import javax.persistence.OneToOne;
import javax.persistence.Table;


/**
 * The persistent class for the ORDER_COMPENSATION database table.
 * 
 */
@Entity
@Table(name="COMPENSATION")
@NamedQuery(name="Compensation.findAll", query="SELECT o FROM Compensation o")
public class Compensation implements Serializable {
	private static final long serialVersionUID = 1L;

	@Id
	@Column(name="ORDER_ID",insertable = true, updatable = false)
	private String orderId;
	
	@Column(name="CHANNEL_DEALER_CODE")
	private String channelDealerCode;

	//bi-directional one-to-one association to Order
	@MapsId
	@OneToOne(cascade=CascadeType.ALL)
	@JoinColumn(name="ORDER_ID")
	private Order order;

	public Compensation() {
	}

	//getters & setters	
}

Service class

package com.sample.service;

import javax.ws.rs.core.Response;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.newModel.Order;


@Component
public class MobilityServiceImpl implements MobilityService {

	
	@Autowired
	private MobilityRepository mobilityRepo;
		
	@Override
	public Response getOrderDetails(String orderId) {

		Order orderDetails=mobilityRepo.findByOrderId(orderId);
		
		return Response.ok(orderDetails).build();
	}

	@Override
	public Response saveOrderDetails(Order orderDetails) {
		
		orderDetails.getCompensation().setOrder(orderDetails);
				
		Order orderResponse =mobilityRepo.save(orderDetails);
		String resp=orderResponse.getOrderId()+" is Success";
		return Response.ok(resp).build();
	}

	}

Console log

Hibernate: select order0_.order_id as order_id1_1_1_, order0_.access_id as access_i2_1_1_, compensati1_.order_id as order_id1_0_0_, compensati1_.channel_dealer_code as channel_2_0_0_ from orders order0_ left outer join compensation compensati1_ on order0_.order_id=compensati1_.order_id where order0_.order_id=?
2018-11-23 14:14:23.268 ERROR 20112 --- [nio-8080-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.orm.jpa.JpaSystemException: attempted to assign id from null one-to-one property [com.newModel.Compensation.order]; nested exception is org.hibernate.id.IdentifierGenerationException: attempted to assign id from null one-to-one property [com.newModel.Compensation.order]] with root cause

org.hibernate.id.IdentifierGenerationException: attempted to assign id from null one-to-one property [com.newModel.Compensation.order]

JSON Request:

{
  "orderId": "1006730",
  "accessId": "1810_CRU",
  "compensation": {
                
                "channelDealerCode": "ABCD"
				}
}

#2

The issue is that the @OneToOne association is null. This is because you didn’t resolve it properly when building the entity from the JSON object.

So, either the Order object has a null compensation association or the Compensation object has a null order association.

Either way, you need to make sure you set both sides of the bidirectional association using real entities.


#3

Debug

@vlad, Order is having Compensation and compensation is having Order. Please find the attached image for the same. However the orderId from Orders is not beimapped to orderId inside Compensation. Could you pls help me in this regard.


#4

You should also set the orderId because it’s null. That’s the entity identifier, and @MapsId only tells Hibernate to skip the @OneToOne when handling the mapping identifier since the entity id is the owner of that mapping.

So, just the orderId property to order.getOrderId() and everything will work fine.


#5

Thanks. Is there any annotation to tell hibernate to pick the orderId value from the Order object inside Compensation rather than manually setting the orderId into compensation ?


#6

You don’t need an annotation for that. You can easily do that in the setter:

public void setOrder(Order order) {
    this.order = order;
    this.orderId = order.getOrderId();
}

#7

Thanks vlad. I had an assumption that MapsId will help to map the primarykeys of parent to child primary as the primary key of parent act as foreign key in child as well


#8

@vlad, Please correct me if I am doing something wrong. Apologize for the experimentation. I have made the following changes in Compensation mapping with Orders table.

Updated Compensation class(removed orderid field and made Orders as Id field). Below are the queries generated in console.

Hibernate: create table compensation (channel_dealer_code varchar(255), order_id varchar(255) not null, primary key (order_id))
Hibernate: create table orders (order_id varchar(255) not null, access_id varchar(255), primary key (order_id))
Hibernate: alter table compensation add constraint FKic3016mn3tbu38ye2hx4ad66t foreign key (order_id) references orders

Updated Compensation model:

package com.newModel;

import java.io.Serializable;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.MapsId;
import javax.persistence.NamedQuery;
import javax.persistence.OneToOne;
import javax.persistence.Table;



@Entity
@Table(name="COMPENSATION")
@NamedQuery(name="Compensation.findAll", query="SELECT o FROM Compensation o")
public class Compensation implements Serializable {
	private static final long serialVersionUID = 1L;

	/*@Id
	@Column(name="ORDER_ID")
	private String orderId;*/
	
	@Column(name="CHANNEL_DEALER_CODE")
	private String channelDealerCode;

	//bi-directional one-to-one association to Order
	
	@Id
	@OneToOne(cascade=CascadeType.ALL)
	@JoinColumn(name="ORDER_ID")
	private Order order;

	
}

Following is the error in console:

Hibernate: select order0_.order_id as order_id1_1_1_, order0_.access_id as access_i2_1_1_, compensati1_.order_id as order_id2_0_0_, compensati1_.channel_dealer_code as channel_1_0_0_ from orders order0_ left outer join compensation compensati1_ on order0_.order_id=compensati1_.order_id where order0_.order_id=?
Hibernate: select compensati0_.order_id as order_id2_0_0_, compensati0_.channel_dealer_code as channel_1_0_0_ from compensation compensati0_ where compensati0_.order_id=?
Hibernate: insert into orders (access_id, order_id) values (?, ?)
Hibernate: insert into compensation (channel_dealer_code, order_id) values (?, ?)
2018-11-23 16:13:53.210  WARN 17532 --- [nio-8080-exec-1] o.h.engine.jdbc.spi.SqlExceptionHelper   : SQL Error: -10, SQLState: 23502
2018-11-23 16:13:53.211 ERROR 17532 --- [nio-8080-exec-1] o.h.engine.jdbc.spi.SqlExceptionHelper   : integrity constraint violation: NOT NULL check constraint; SYS_CT_10118 table: COMPENSATION column: ORDER_ID
2018-11-23 16:13:53.214 ERROR 17532 --- [nio-8080-exec-1] o.h.i.ExceptionMapperStandardImpl        : HHH000346: Error during managed flush [org.hibernate.exception.ConstraintViolationException: could not execute statement]
2018-11-23 16:13:53.244 ERROR 17532 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint [null]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement] with root cause

org.hsqldb.HsqlException: integrity constraint violation: NOT NULL check constraint; SYS_CT_10118 table: COMPENSATION column: ORDER_ID

In Debug found order is available inside compensation, pls find the attached screenshot for the same.
NewDebug


#9

I created this test case on GitHub and everything runs like a charm:

public class OneToOneIdTest extends AbstractTest {

    @Override
    protected Class<?>[] entities() {
        return new Class<?>[] {
            Post.class,
            PostDetails.class,
        };
    }

    @Test
    public void testLifecycle() {
        Post _post = doInJPA(entityManager -> {
            Post post = new Post();
            post.setId(1L);
            post.setTitle("First post");

            PostDetails details = new PostDetails();
            details.setCreatedBy("John Doe");

            post.setDetails(details);
            entityManager.persist(post);

            return post;
        });

        _post.setTitle("Second post");
        _post.getDetails().setCreatedBy("Vlad Mihalcea");

        doInJPA(entityManager -> {
            Post post  = entityManager.merge(_post);
        });

        doInJPA(entityManager -> {
            PostDetails id = new PostDetails();
            id.setPost(_post);

            PostDetails details = entityManager.find(PostDetails.class, id);
            assertEquals("Vlad Mihalcea", details.getCreatedBy());
            assertEquals("Second post", details.getPost().getTitle());
        });
    }

    @Entity(name = "Post")
    @Table(name = "post")
    public static class Post implements Serializable {

        @Id
        private Long id;

        private String title;

        @OneToOne(mappedBy = "post", cascade = CascadeType.ALL)
        private PostDetails details;

        public Long getId() {
            return id;
        }

        public void setId(Long id) {
            this.id = id;
        }

        public String getTitle() {
            return title;
        }

        public void setTitle(String title) {
            this.title = title;
        }

        public PostDetails getDetails() {
            return details;
        }

        public void setDetails(PostDetails details) {
            this.details = details;
            this.details.setPost(this);
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (!(o instanceof Post)) return false;
            return id != null && id.equals(((Post) o).id);
        }

        @Override
        public int hashCode() {
            return 31;
        }
    }

    @Entity(name = "PostDetails")
    @Table(name = "post_details")
    public static class PostDetails implements Serializable {

        @Id
        @OneToOne
        private Post post;

        @Column(name = "created_on")
        private Date createdOn = new Date();

        @Column(name = "created_by")
        private String createdBy;

        public Post getPost() {
            return post;
        }

        public void setPost(Post post) {
            this.post = post;
        }

        public Date getCreatedOn() {
            return createdOn;
        }

        public void setCreatedOn(Date createdOn) {
            this.createdOn = createdOn;
        }

        public String getCreatedBy() {
            return createdBy;
        }

        public void setCreatedBy(String createdBy) {
            this.createdBy = createdBy;
        }
    }
}

So, you can fork the repo and modify the test case to prove your issue.


#10

Which version of Hibernate are you using? I believe this broke in 5.2.14 and is the issue that was fixed on 5.4 CR1.


#11

I believe it is 5.2.13


#12

See: https://hibernate.atlassian.net/browse/HHH-12436