How to transfer the embedded identifier from a grandparent to a grandchild @OneToMany mapping using Hibernate


#1

I have 3 model classes (namely A, B & C). A is the parent having onetoMany bidirectional relationship with B. B is parent to C and having onetoMany bidirectional. B & C have an embedded pKey.

From A --> one primary key is being set to B. From B --> 2 pKeys is being set to C.

Inside service I am setting A inside B, and B inside C.

Get method works fine. while Post I am getting null pointer in C ( as the Pkey is not being transferred from B).

Can someone help how to transfer pKey from A to C ( as one of the Pkey inside B is derived from A).

Model classes:

Class A:

package com.newmodel;

import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;

import javax.persistence.*;



@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;


    //bi-directional many-to-one association to LineItem
    @OneToMany(mappedBy="order",cascade=CascadeType.ALL,fetch=FetchType.EAGER)
    @Fetch(value = FetchMode.SUBSELECT)
    private List<ProductItem> productItems;

    getters & setters
}

Class B:

package com.newmodel;

import java.io.Serializable;
import javax.persistence.*;
import java.math.BigDecimal;
import java.util.List;


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

    @EmbeddedId
    private ProductItemPK id;

    private String asku;



    //bi-directional many-to-one association to ItemTax
    @OneToMany(mappedBy="productItem",cascade=CascadeType.ALL,fetch=FetchType.EAGER)
    @Fetch(value=FetchMode.SUBSELECT)
    private List<ProductItemTax> productItemTaxs;



    //bi-directional many-to-one association to Order
    @ManyToOne
    @JoinColumn(name="ORDER_ID",referencedColumnName="ORDER_ID")
    @MapsId("orderId")
    @JsonIgnore
    private Order order;

    getters & setters
}

Class B PK:

package com.newmodel;

import java.io.Serializable;
import javax.persistence.*;

@Embeddable
public class ProductItemPK implements Serializable {
    //default serial version id, required for serializable classes.
    private static final long serialVersionUID = 1L;

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

    @Column(name="PRODUCT_NUMBER")
    private long productNumber;

   getters & setters

    public boolean equals(Object other) {
        if (this == other) {
            return true;
        }
        if (!(other instanceof ProductItemPK)) {
            return false;
        }
        ProductItemPK castOther = (ProductItemPK)other;
        return 
            this.orderId.equals(castOther.orderId)
            && (this.productNumber == castOther.productNumber);
    }

    public int hashCode() {
        final int prime = 31;
        int hash = 17;
        hash = hash * prime + this.orderId.hashCode();
        hash = hash * prime + ((int) (this.productNumber ^ (this.productNumber >>> 32)));

        return hash;
    }
}

class c:

package com.newmodel;

import java.io.Serializable;
import javax.persistence.*;
import java.math.BigDecimal;


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

    @EmbeddedId
    private ProductItemTaxPK id;

    @Column(name="CREATE_DATE")
    private java.sql.Date createDate;



    //bi-directional many-to-one association to LineItem
    @ManyToOne
    @MapsId("id")
    @JoinColumns({
        @JoinColumn(name="ORDER_ID",referencedColumnName="ORDER_ID"),
        @JoinColumn(name="PRODUCT_NUMBER",referencedColumnName="PRODUCT_NUMBER")})
    @JsonIgnore
    private ProductItem productItem;


    getters & setters
}

class c PK:

package com.newmodel;

import java.io.Serializable;
import javax.persistence.*;

@Embeddable
public class ProductItemTaxPK implements Serializable {
    //default serial version id, required for serializable classes.
    private static final long serialVersionUID = 1L;

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

    @Column(name="PRODUCT_NUMBER")
    private long productNumber;

    @Column(name="TAX_SEQUENCE")
    private long taxSequence;

    getters & setters

    public boolean equals(Object other) {
        if (this == other) {
            return true;
        }
        if (!(other instanceof ProductItemTaxPK)) {
            return false;
        }
        ProductItemTaxPK castOther = (ProductItemTaxPK)other;
        return 
            this.orderId.equals(castOther.orderId)
            && (this.productNumber == castOther.productNumber)
            && (this.taxSequence == castOther.taxSequence);
    }

    public int hashCode() {
        final int prime = 31;
        int hash = 17;
        hash = hash * prime + this.orderId.hashCode();
        hash = hash * prime + ((int) (this.productNumber ^ (this.productNumber >>> 32)));
        hash = hash * prime + ((int) (this.taxSequence ^ (this.taxSequence >>> 32)));

        return hash;
    }
}

#2

You need ti add the data access code and the SQL queries executed by Hibernate which demonstrate the problem you are facing.


#3

Vlad, can you please let me know how to obtain data access code. I am using CRUD repository interface with no implmentation


#4

The code using those repositories is also the data access code. Or if you use specific methods interacting with Hibernate. Without seeing the code and the queries, it’s impossible to tell what the issue is.


#5

Vlad, Thanks for your quick response. Please find the repositories and service class used for the same.

package com.sample.service;

import java.util.List;

import javax.ws.rs.core.Response;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.newModel.Order;
import com.newModel.ProductItem;
import com.newModel.ProductItemTax;


@Component
       public class MobilityServiceImpl implements MobilityService {


	private static Logger log = LoggerFactory.getLogger(MobilityServiceImpl.class);
	
	@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) {
		
			
			if(null!=orderDetails.getProductItems()) {
				
			List<ProductItem> orderLines=orderDetails.getProductItems();
			for (ProductItem productItem : orderLines) {
				productItem.setOrder(orderDetails);
				if(productItem.getProductItemTaxs()!=null)
				{
					List<ProductItemTax> productItemTaxs = productItem.getProductItemTaxs();
					for (ProductItemTax productItemTax : productItemTaxs) {
						productItemTax.setProductItem(productItem);
					}
				}
			}
		}	
		Order orderResponse =mobilityRepo.save(orderDetails);
		String resp=orderResponse.getOrderId()+" is Success";
		return Response.ok(resp).build();
	}

	}

Repository:

package com.sample.service;

import org.springframework.data.repository.CrudRepository;

import com.newModel.Order;

public interface MobilityRepository extends CrudRepository<Order, String> {
	
	Order findByOrderId(String orderId);
	
	Order save(Order order);
	
	}


#6

OK, so what exactly doe snot work?

Try to explain in code and SQL statements what you meant by:

Can someone help how to transfer pKey from A to C ( as one of the Pkey inside B is derived from A).

I don’t understand what exactly is not working.

Also, what’s the purpose of this method:

if(null!=orderDetails.getProductItems()) {
		
	List<ProductItem> orderLines=orderDetails.getProductItems();
	for (ProductItem productItem : orderLines) {
		productItem.setOrder(orderDetails);
		if(productItem.getProductItemTaxs()!=null)
		{
			List<ProductItemTax> productItemTaxs = productItem.getProductItemTaxs();
			for (ProductItemTax productItemTax : productItemTaxs) {
				productItemTax.setProductItem(productItem);
			}
		}
	}
}	
Order orderResponse =mobilityRepo.save(orderDetails);

Why do you iterate the ProductItem to set the Order? How come that’s not already set when you pass the Order?

Also, why do you use null for collections?

Don’t do that:

private List<ProductItemTax> productItemTaxs;

Use this instead:

private List<ProductItemTax> productItemTaxs = new ArrayList<>();

And save the null checks.


#7

Why do you iterate the ProductItem to set the Order ? How come that’s not already set when you pass the Order ? When I am trying to save OrderDetails it is giving nullpointer exception for the mapping List productItems; with the error stating cannot assign one-one null property.

If I set order inside ProductItem it is working, but the same approach is not working for productItemTax.setProductItem(productItem);

Basically I am not able to pass order id from order class to below subsequent class until I do the manual set of order inside each child class.

can you please let me know how to achieve one to many mapping for the above without the iteration. Input Json request contains all the three class values and it should work with save method.

Also, why do you use null for collections? I am not setting null for collections I am just checking list is not null… I will use the approach of creating the array and assign it.


#8

I still don’t understand what exactly you cannot pass. Try to debug it and see exactly why it fails.

Also, you need to make sure you read the User Guide if you want to succeed with JPA and Hibernate.


#10

Vlad, If I have the above model classes, & all repo and service and when I use the Json request mentioned below I am getting the following exception. Please find the queries and exception from console.

Json request:

{
  "orderId": "1006730",
  "accessId": "1810_CRU",
  "productItems": [
    {
      "id": {
        "productNumber": 1
      },
      "asku": "DF",
      "productItemTaxs": [
        {
          "id": {
            "taxSequence": 1
          },
          "createDate": "2018-10-30"
        }
      ]
    }
  ]
}
Hibernate: select order0_.order_id as order_id1_0_1_, order0_.access_id as access_i2_0_1_, productite1_.order_id as order_id1_1_3_, productite1_.product_number as product_2_1_3_, productite1_.order_id as order_id1_1_0_, productite1_.product_number as product_2_1_0_, productite1_.asku as asku3_1_0_ from orders order0_ left outer join product_items productite1_ on order0_.order_id=productite1_.order_id where order0_.order_id=?
Hibernate: select productite0_.order_id as order_id1_1_1_, productite0_.product_number as product_2_1_1_, productite0_.asku as asku3_1_1_, productite1_.order_id as order_id1_2_3_, productite1_.product_number as product_2_2_3_, productite1_.tax_sequence as tax_sequ3_2_3_, productite1_.order_id as order_id1_2_0_, productite1_.product_number as product_2_2_0_, productite1_.tax_sequence as tax_sequ3_2_0_, productite1_.create_date as create_d4_2_0_ from product_items productite0_ left outer join product_tax productite1_ on productite0_.order_id=productite1_.order_id and productite0_.product_number=productite1_.product_number where productite0_.order_id=? and productite0_.product_number=?
Hibernate: select productite0_.order_id as order_id1_2_0_, productite0_.product_number as product_2_2_0_, productite0_.tax_sequence as tax_sequ3_2_0_, productite0_.create_date as create_d4_2_0_ from product_tax productite0_ where productite0_.order_id=? and productite0_.product_number=? and productite0_.tax_sequence=?
Hibernate: insert into orders (access_id, order_id) values (?, ?)
Hibernate: insert into product_items (asku, order_id, product_number) values (?, ?, ?)
Hibernate: insert into product_tax (create_date, order_id, product_number, tax_sequence) values (?, ?, ?, ?)
2018-11-20 22:45:00.217  WARN 9160 --- [nio-8080-exec-4] o.h.engine.jdbc.spi.SqlExceptionHelper   : SQL Error: -10, SQLState: 23502
2018-11-20 22:45:00.217 ERROR 9160 --- [nio-8080-exec-4] o.h.engine.jdbc.spi.SqlExceptionHelper   : integrity constraint violation: NOT NULL check constraint; SYS_CT_10136 table: PRODUCT_TAX column: ORDER_ID
2018-11-20 22:45:00.219 ERROR 9160 --- [nio-8080-exec-4] o.h.i.ExceptionMapperStandardImpl        : HHH000346: Error during managed flush [org.hibernate.exception.ConstraintViolationException: could not execute statement]
2018-11-20 22:45:00.240 ERROR 9160 --- [nio-8080-exec-4] 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_10136 table: PRODUCT_TAX column: ORDER_ID

#11

In service code If i remove all the iterations and contains only the below code in saveOrderDetails method, I am getting below exception.

Modified service class:

package com.sample.service;

import java.util.List;

import javax.ws.rs.core.Response;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.newModel.Order;
import com.newModel.ProductItem;
import com.newModel.ProductItemTax;


@Component
public class MobilityServiceImpl implements MobilityService {

	private static Logger log = LoggerFactory.getLogger(MobilityServiceImpl.class);
	
	@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) {
		
		Order orderResponse =mobilityRepo.save(orderDetails);
		String resp=orderResponse.getOrderId()+" is Success";
		return Response.ok(resp).build();
	}

	}

Exception:

Hibernate: select order0_.order_id as order_id1_0_1_, order0_.access_id as access_i2_0_1_, productite1_.order_id as order_id1_1_3_, productite1_.product_number as product_2_1_3_, productite1_.order_id as order_id1_1_0_, productite1_.product_number as product_2_1_0_, productite1_.asku as asku3_1_0_ from orders order0_ left outer join product_items productite1_ on order0_.order_id=productite1_.order_id where order0_.order_id=?
Hibernate: select productite0_.order_id as order_id1_1_1_, productite0_.product_number as product_2_1_1_, productite0_.asku as asku3_1_1_, productite1_.order_id as order_id1_2_3_, productite1_.product_number as product_2_2_3_, productite1_.tax_sequence as tax_sequ3_2_3_, productite1_.order_id as order_id1_2_0_, productite1_.product_number as product_2_2_0_, productite1_.tax_sequence as tax_sequ3_2_0_, productite1_.create_date as create_d4_2_0_ from product_items productite0_ left outer join product_tax productite1_ on productite0_.order_id=productite1_.order_id and productite0_.product_number=productite1_.product_number where productite0_.order_id=? and productite0_.product_number=?
2018-11-20 22:49:53.779 ERROR 4588 --- [nio-8080-exec-3] 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.ProductItem.order]; nested exception is org.hibernate.id.IdentifierGenerationException: attempted to assign id from null one-to-one property [com.newModel.ProductItem.order]] with root cause

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

#12

@vlad Kindly help me in resolving the above two issues… Please let me know any more queries.

Thanks for your help in resolving this issue.


#13
org.hsqldb.HsqlException: integrity constraint violation: NOT NULL check constraint; SYS_CT_10136 table: PRODUCT_TAX column: ORDER_ID

That’s because productItemTax does not have the orderId property set. You need to make sure you set it.

The problem is that the ProductItem.order property has a null identifier. You didn’t set it properly from the incoming JSON.

Therefore, you need to make sure you can recreate the right associations from the JSON document.


#14

That’s because productItemTax does not have the orderId property set. You need to make sure you set it.

Means do we need to pass the orderId property from Json request ? I want this orderId value to be passed from Order class, is that possible to transfer the property from parent to grandchild ?


#15

That’s not a question about Hibernate. It’s a matter of constructing the entity from JSOM. If you don’t have the property in JSON, it means the JSON object is broken.


#16

Got it. Thanks for all your replies & clarifications @vlad … It means a lot…


#17

@vlad one final query… we are setting order inside productitem using iterator… is it the right way or that also to be deal with json request ?


#18

It’s right only if you can build the entities from JSON. Otherwise, you need to use EntityManager.getRegerence to use Proxies that set the To one associations in case you only have the entity identifers.