-1

I have two entities Requirement and Product in a Spring Boot Application. Requirement has One to Many relationship with Product.

In the product repo interface I have a simple query method that fetches list of products with given requirement_id. Here's the code:

interface ProductRepo: JpaRepository<Product, Int>{

 fun findProductsByRequirement_Id(id:Int) : List<Product>
}

However when this method is called from a Rest Controller I can see that along with each product, hibernate is returning the corresponding requirement object instead of just requirement_id. It doesn't stop there, it also returns every foreign objects (Deal & Sale) associated with the requirement object in the JSON result.

I have set this option "spring.jpa.properties.hibernate.show_sql=true" in the application.properties file and every time that query method is called I see in the console that Hibernate keeps calling lots of complex outer join queries even I haven't asked for it. In my opinion this is consuming unnecessary resource when I don't need for all these foreign objects.

Here is the sql queries that hibernate runs in the background:

Hibernate: select requiremen0_.id as id1_8_0_, requiremen0_.deal_id as deal_id4_8_0_, requiremen0_.requirement_total as requirem2_8_0_, requiremen0_.sale_id as sale_id5_8_0_, requiremen0_.title as title3_8_0_, deal1_.id as id1_4_1_, deal1_.account_id as account_7_4_1_, deal1_.crm_customer_id as crm_cust8_4_1_, deal1_.exp_closing_date as exp_clos2_4_1_, deal1_.requirement_title as requirem3_4_1_, deal1_.sales_value as sales_va4_4_1_, deal1_.stage as stage5_4_1_, deal1_.type as type6_4_1_, deal1_.user_id as user_id9_4_1_, account2_.id as id1_0_2_, account2_.address as address2_0_2_, account2_.crm_customer_id as crm_cust9_0_2_, account2_.email as email3_0_2_, account2_.key_person as key_pers4_0_2_, account2_.name as name5_0_2_, account2_.phone as phone6_0_2_, account2_.status as status7_0_2_, account2_.type as type8_0_2_, account2_.user_id as user_id10_0_2_, crmcustome3_.id as id1_2_3_, crmcustome3_.address as address2_2_3_, crmcustome3_.email as email3_2_3_, crmcustome3_.key_person as key_pers4_2_3_, crmcustome3_.name as name5_2_3_, crmcustome3_.number_of_users as number_o6_2_3_, crmcustome3_.phone as phone7_2_3_, crmcustome3_.subscription_type as subscrip8_2_3_, crmcustome3_.validity_exp_date as validity9_2_3_, user4_.id as id1_11_4_, user4_.crm_customer_id as crm_cust8_11_4_, user4_.designation as designat2_11_4_, user4_.hierarchy_level as hierarch3_11_4_, user4_.logged_in as logged_i4_11_4_, user4_.login_id as login_id5_11_4_, user4_.login_pw as login_pw6_11_4_, user4_.name as name7_11_4_, sale5_.id as id1_9_5_, sale5_.crm_customer_id as crm_cust4_9_5_, sale5_.deal_id as deal_id5_9_5_, sale5_.sales_date as sales_da2_9_5_, sale5_.sales_value as sales_va3_9_5_, sale5_.user_id as user_id6_9_5_, crmcustome6_.id as id1_2_6_, crmcustome6_.address as address2_2_6_, crmcustome6_.email as email3_2_6_, crmcustome6_.key_person as key_pers4_2_6_, crmcustome6_.name as name5_2_6_, crmcustome6_.number_of_users as number_o6_2_6_, crmcustome6_.phone as phone7_2_6_, crmcustome6_.subscription_type as subscrip8_2_6_, crmcustome6_.validity_exp_date as validity9_2_6_, deal7_.id as id1_4_7_, deal7_.account_id as account_7_4_7_, deal7_.crm_customer_id as crm_cust8_4_7_, deal7_.exp_closing_date as exp_clos2_4_7_, deal7_.requirement_title as requirem3_4_7_, deal7_.sales_value as sales_va4_4_7_, deal7_.stage as stage5_4_7_, deal7_.type as type6_4_7_, deal7_.user_id as user_id9_4_7_, user8_.id as id1_11_8_, user8_.crm_customer_id as crm_cust8_11_8_, user8_.designation as designat2_11_8_, user8_.hierarchy_level as hierarch3_11_8_, user8_.logged_in as logged_i4_11_8_, user8_.login_id as login_id5_11_8_, user8_.login_pw as login_pw6_11_8_, user8_.name as name7_11_8_ from requirements requiremen0_ left outer join deals deal1_ on requiremen0_.deal_id=deal1_.id left outer join accounts account2_ on deal1_.account_id=account2_.id left outer join crm_customers crmcustome3_ on deal1_.crm_customer_id=crmcustome3_.id left outer join users user4_ on deal1_.user_id=user4_.id left outer join sales sale5_ on requiremen0_.sale_id=sale5_.id left outer join crm_customers crmcustome6_ on sale5_.crm_customer_id=crmcustome6_.id left outer join deals deal7_ on sale5_.deal_id=deal7_.id left outer join users user8_ on sale5_.user_id=user8_.id where requiremen0_.id=?

What can I do to avoid this situation?

Here are the Entity classes for Product and Requirement:

package com.ksk.crmserver.Model

import com.fasterxml.jackson.annotation.JsonIgnore
import javax.persistence.*

@Entity
@Table(name = "requirements")
data class Requirement(
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        val id: Int = 0,
        @Column(name = "title")
        val title: String = "",

        @OneToMany(mappedBy = "requirement")
        @JsonIgnore
        val products: List<Product>? = null,
        @Column(name = "requirement_total")

        val requirementTotal: Int = 0,

        @ManyToOne
        var deal: Deal? = null,

        @ManyToOne
        val sale: Sale?=null
)


package com.ksk.crmserver.Model

import javax.persistence.*


@Entity
@Table(name = "products", indexes = [Index(name = "idx_product", columnList = "name")])

data class Product(
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        val id: Int = 0,
        val name: String = "",
        val description: String? = "",

        @Column(name = "unit_cost")
        val unitCost: Double = 0.0,

        @Column(name = "qty")
        val qty: Double? = 0.0,

        val unit: String? = "",

        @Column(name = "total_Cost")
        val totalCost: Double? = 0.0,

        @Column(name = "profit_margin")
        val profitMargin: Double? = 0.0,

        val profit: Double? = 0.0,

        @Column(name = "vat_percentage")
        val vatPercentage: Double? = 0.0,

        val vat: Double? = 0.0,

        @Column(name = "total_price")
        val totalPrice: Double? = 0.0,

        @ManyToOne
        @JoinColumn(name = "requirement_id")
        val requirement: Requirement? = null


)

Edit: I have tried using EntityManager.getReference to get an instance of Requirement Object. But it is still running a long sql query to fetch the Requirement by id from DB. Here is my code from the RestController class for Product:

I'm afraid it's still running the sql join query. Here is my Rest Controller code:

@RestController
@CrossOrigin
class ProductRoute(val productRepo: ProductRepo, val requirementRepo: RequirementRepo) {

    @Autowired
    val entityManager: EntityManager?=null

    @GetMapping("/get/productsByReqId")
    fun getProductsByRequirementId(@RequestParam id: Int): List<Product> {
        val requirement = this.entityManager?.getReference(Requirement::class.java, id) as Requirement
        print("Done fetching the requirement")
        return this.productRepo.findProductsByRequirement(requirement)
    }

This is the sql Hibernate is running:

Hibernate: select requiremen0_.id as id1_8_0_, requiremen0_.deal_id as deal_id4_8_0_, requiremen0_.requirement_total as requirem2_8_0_, requiremen0_.sale_id as sale_id5_8_0_, requiremen0_.title as title3_8_0_, deal1_.id as id1_4_1_, deal1_.account_id as account_7_4_1_, deal1_.crm_customer_id as crm_cust8_4_1_, deal1_.exp_closing_date as exp_clos2_4_1_, deal1_.requirement_title as requirem3_4_1_, deal1_.sales_value as sales_va4_4_1_, deal1_.stage as stage5_4_1_, deal1_.type as type6_4_1_, deal1_.user_id as user_id9_4_1_, account2_.id as id1_0_2_, account2_.address as address2_0_2_, account2_.crm_customer_id as crm_cust9_0_2_, account2_.email as email3_0_2_, account2_.key_person as key_pers4_0_2_, account2_.name as name5_0_2_, account2_.phone as phone6_0_2_, account2_.status as status7_0_2_, account2_.type as type8_0_2_, account2_.user_id as user_id10_0_2_, crmcustome3_.id as id1_2_3_, crmcustome3_.address as address2_2_3_, crmcustome3_.email as email3_2_3_, crmcustome3_.key_person as key_pers4_2_3_, crmcustome3_.name as name5_2_3_, crmcustome3_.number_of_users as number_o6_2_3_, crmcustome3_.phone as phone7_2_3_, crmcustome3_.subscription_type as subscrip8_2_3_, crmcustome3_.validity_exp_date as validity9_2_3_, user4_.id as id1_11_4_, user4_.crm_customer_id as crm_cust8_11_4_, user4_.designation as designat2_11_4_, user4_.hierarchy_level as hierarch3_11_4_, user4_.logged_in as logged_i4_11_4_, user4_.login_id as login_id5_11_4_, user4_.login_pw as login_pw6_11_4_, user4_.name as name7_11_4_, products5_.requirement_id as require13_5_5_, products5_.id as id1_5_5_, products5_.id as id1_5_6_, products5_.description as descript2_5_6_, products5_.name as name3_5_6_, products5_.profit as profit4_5_6_, products5_.profit_margin as profit_m5_5_6_, products5_.qty as qty6_5_6_, products5_.requirement_id as require13_5_6_, products5_.total_cost as total_co7_5_6_, products5_.total_price as total_pr8_5_6_, products5_.unit as unit9_5_6_, products5_.unit_cost as unit_co10_5_6_, products5_.vat as vat11_5_6_, products5_.vat_percentage as vat_per12_5_6_, sale6_.id as id1_9_7_, sale6_.crm_customer_id as crm_cust4_9_7_, sale6_.deal_id as deal_id5_9_7_, sale6_.sales_date as sales_da2_9_7_, sale6_.sales_value as sales_va3_9_7_, sale6_.user_id as user_id6_9_7_, crmcustome7_.id as id1_2_8_, crmcustome7_.address as address2_2_8_, crmcustome7_.email as email3_2_8_, crmcustome7_.key_person as key_pers4_2_8_, crmcustome7_.name as name5_2_8_, crmcustome7_.number_of_users as number_o6_2_8_, crmcustome7_.phone as phone7_2_8_, crmcustome7_.subscription_type as subscrip8_2_8_, crmcustome7_.validity_exp_date as validity9_2_8_, deal8_.id as id1_4_9_, deal8_.account_id as account_7_4_9_, deal8_.crm_customer_id as crm_cust8_4_9_, deal8_.exp_closing_date as exp_clos2_4_9_, deal8_.requirement_title as requirem3_4_9_, deal8_.sales_value as sales_va4_4_9_, deal8_.stage as stage5_4_9_, deal8_.type as type6_4_9_, deal8_.user_id as user_id9_4_9_, user9_.id as id1_11_10_, user9_.crm_customer_id as crm_cust8_11_10_, user9_.designation as designat2_11_10_, user9_.hierarchy_level as hierarch3_11_10_, user9_.logged_in as logged_i4_11_10_, user9_.login_id as login_id5_11_10_, user9_.login_pw as login_pw6_11_10_, user9_.name as name7_11_10_ from requirements requiremen0_ left outer join deals deal1_ on requiremen0_.deal_id=deal1_.id left outer join accounts account2_ on deal1_.account_id=account2_.id left outer join crm_customers crmcustome3_ on deal1_.crm_customer_id=crmcustome3_.id left outer join users user4_ on deal1_.user_id=user4_.id left outer join products products5_ on requiremen0_.id=products5_.requirement_id left outer join sales sale6_ on requiremen0_.sale_id=sale6_.id left outer join crm_customers crmcustome7_ on sale6_.crm_customer_id=crmcustome7_.id left outer join deals deal8_ on sale6_.deal_id=deal8_.id left outer join users user9_ on sale6_.user_id=user9_.id where requiremen0_.id=?

Done fetching the requirement 2019-02-16 21:01:00.341 INFO 80558 --- [nio-8080-exec-1] o.h.h.i.QueryTranslatorFactoryInitiator : HHH000397: Using ASTQueryTranslatorFactory

Hibernate: select product0_.id as id1_5_, product0_.description as descript2_5_, product0_.name as name3_5_, product0_.profit as profit4_5_, product0_.profit_margin as profit_m5_5_, product0_.qty as qty6_5_, product0_.requirement_id as require13_5_, product0_.total_cost as total_co7_5_, product0_.total_price as total_pr8_5_, product0_.unit as unit9_5_, product0_.unit_cost as unit_co10_5_, product0_.vat as vat11_5_, product0_.vat_percentage as vat_per12_5_ from products product0_ where product0_.requirement_id=?

javaland235
  • 743
  • 3
  • 11
  • 21
  • Can you change it to `fun findProductsByRequirement(req: Requirement) : List` and tell me what happend? – Andronicus Feb 16 '19 at 09:31
  • now the console output for sql is: Hibernate: select product0_.id as id1_5_, product0_.description as descript2_5_, product0_.name as name3_5_, product0_.profit as profit4_5_, product0_.profit_margin as profit_m5_5_, product0_.qty as qty6_5_, product0_.requirement_id as require13_5_, product0_.total_cost as total_co7_5_, product0_.total_price as total_pr8_5_, product0_.unit as unit9_5_, product0_.unit_cost as unit_co10_5_, product0_.vat as vat11_5_, product0_.vat_percentage as vat_per12_5_ from products product0_ where product0_.requirement_id=? – javaland235 Feb 16 '19 at 10:11
  • I can see that sql query does not have any join keyword, but in the JSON output I still get the entire requirement object and all the foreign referenced object in the requirement object as well. – javaland235 Feb 16 '19 at 10:14

1 Answers1

0

You can set the fetchType of your ManyToOne Relations to Lazy (Default for ManyToOne Relations is Eager). This way Hibernate will only load the referenced Objects if you call them. If you want Hibernate to return the Id instead of the whole referenced Object you can look up here: Hibernate - Foreign keys instead of Entities

SrKn
  • 1
  • 2
  • Thanks for your answer, but I've tried using lazy fetch type. Still I get referenced object in the json output. Maybe spring data jpa calls the reference object – javaland235 Feb 16 '19 at 16:35