How to retrieve data from a one to one mapped entity after a filter is applied on to the mapped entity's attribute spring - spring

How to retrieve data from a one to one mapped entity after a filter is applied on to the mapped entity's attribute.
This is my Hotel Entity Class..
package com.springmvcweb.model;
import javax.persistence.*;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
#Entity
#Table(name = "HOTEL", schema = "HOTEL")
public class HotelEntity implements Serializable{
private long hotelId;
private String hotelName;
private String hotelDescription;
private String hotelWebsite;
private Long hotelPhoneNo;
private String hotelEmail;
private Long hotelStarRating;
private AddressEntity addressEntity;
private CategoryEntity categoryEntity;
private List<AmenityEntity> amenitiesList;
#ManyToMany(cascade = {CascadeType.MERGE,CascadeType.PERSIST,CascadeType.DETACH,CascadeType.REFRESH})
#JoinTable(name = "HOTEL_AMENITY", joinColumns = {#JoinColumn(name = "HOTEL_ID", referencedColumnName = "HOTEL_ID")},
inverseJoinColumns = {#JoinColumn(name = "AMENITY_ID", referencedColumnName = "AMENITY_ID")})
public List<AmenityEntity> getAmenitiesList() {
return amenitiesList;
}
public void setAmenitiesList(List<AmenityEntity> amenitiesList) {
this.amenitiesList = amenitiesList;
}
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "HOTEL_ADDRESS_ID")
public AddressEntity getAddressEntity() {
return addressEntity;
}
public void setAddressEntity(AddressEntity addressEntity) {
this.addressEntity = addressEntity;
}
#ManyToOne(cascade = {CascadeType.MERGE,CascadeType.PERSIST,CascadeType.DETACH,CascadeType.REFRESH})
#JoinColumn(name = "HOTEL_CATEGORY_ID")
public CategoryEntity getCategoryEntity() {
return categoryEntity;
}
public void setCategoryEntity(CategoryEntity categoryEntity) {
this.categoryEntity = categoryEntity;
}
#Id
#Column(name = "HOTEL_ID")
public long getHotelId() {
return hotelId;
}
public void setHotelId(long hotelId) {
this.hotelId = hotelId;
}
#Basic
#Column(name = "HOTEL_NAME")
public String getHotelName() {
return hotelName;
}
public void setHotelName(String hotelName) {
this.hotelName = hotelName;
}
#Basic
#Column(name = "HOTEL_DESCRIPTION")
public String getHotelDescription() {
return hotelDescription;
}
public void setHotelDescription(String hotelDescription) {
this.hotelDescription = hotelDescription;
}
#Basic
#Column(name = "HOTEL_WEBSITE")
public String getHotelWebsite() {
return hotelWebsite;
}
public void setHotelWebsite(String hotelWebsite) {
this.hotelWebsite = hotelWebsite;
}
#Basic
#Column(name = "HOTEL_PHONE_NO")
public Long getHotelPhoneNo() {
return hotelPhoneNo;
}
public void setHotelPhoneNo(Long hotelPhoneNo) {
this.hotelPhoneNo = hotelPhoneNo;
}
#Basic
#Column(name = "HOTEL_EMAIL")
public String getHotelEmail() {
return hotelEmail;
}
public void setHotelEmail(String hotelEmail) {
this.hotelEmail = hotelEmail;
}
#Basic
#Column(name = "HOTEL_STAR_RATING")
public Long getHotelStarRating() {
return hotelStarRating;
}
public void setHotelStarRating(Long hotelStarRating) {
this.hotelStarRating = hotelStarRating;
}
public void addAmenities(AmenityEntity amenityEntity){
if(amenitiesList==null){
amenitiesList = new ArrayList<>();
}
amenitiesList.add(amenityEntity);
}
#Override
public String toString() {
return "HotelEntity{" +
"hotelId=" + hotelId +
", hotelName='" + hotelName + '\'' +
", hotelDescription='" + hotelDescription + '\'' +
", hotelWebsite='" + hotelWebsite + '\'' +
", hotelPhoneNo=" + hotelPhoneNo +
", hotelEmail='" + hotelEmail + '\'' +
", hotelStarRating=" + hotelStarRating +
", addressEntity=" + addressEntity +
", categoryEntity=" + categoryEntity +
", amenitiesList=" + amenitiesList +
'}';
}
}
This is AddressEntity Class:
package com.springmvcweb.model;
import javax.persistence.*;
import java.io.Serializable;
#Entity
#Table(name = "ADDRESS", schema = "HOTEL")
public class AddressEntity implements Serializable {
private long addressId;
private String addressLine1;
private String addressLine2;
private String cityName;
private String stateName;
private String countryName;
private Long pincode;
#Id
#Column(name = "ADDRESS_ID")
public long getAddressId() {
return addressId;
}
public void setAddressId(long addressId) {
this.addressId = addressId;
}
#Basic
#Column(name = "ADDRESS_LINE1")
public String getAddressLine1() {
return addressLine1;
}
public void setAddressLine1(String addressLine1) {
this.addressLine1 = addressLine1;
}
#Basic
#Column(name = "ADDRESS_LINE2")
public String getAddressLine2() {
return addressLine2;
}
public void setAddressLine2(String addressLine2) {
this.addressLine2 = addressLine2;
}
#Basic
#Column(name = "CITY_NAME")
public String getCityName() {
return cityName;
}
public void setCityName(String cityName) {
this.cityName = cityName;
}
#Basic
#Column(name = "STATE_NAME")
public String getStateName() {
return stateName;
}
public void setStateName(String stateName) {
this.stateName = stateName;
}
#Basic
#Column(name = "COUNTRY_NAME")
public String getCountryName() {
return countryName;
}
public void setCountryName(String countryName) {
this.countryName = countryName;
}
#Basic
#Column(name = "PINCODE")
public Long getPincode() {
return pincode;
}
public void setPincode(Long pincode) {
this.pincode = pincode;
}
}
Now I want to retrieve all those hotels filtered by their location(say cityname or statename) and I'm using query like this:
#Override
public List<HotelEntity> getHotelsByLocation(String location) {
try{
session = sessionFactory.getCurrentSession();
}catch (Exception e){
session = sessionFactory.openSession();
}
Query query = session.createQuery("from HotelEntity where HotelEntity.addressEntity.cityName " +
"like :location");
query.setParameter("location",location);
return query.getResultList();
}
Also I've used FetchType as EAGER in the HotelEntity OneToOne Mapping like this:
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinColumn(name = "HOTEL_ADDRESS_ID")
public AddressEntity getAddressEntity() {
return addressEntity;
}
Right now it is giving me a null pointer exception. Please guide.

#OneToOne relationship is eagerly fetched anyway so adding that information is redundant.
Regarding the query I would form it like the following:
" select he from HotelEntity he
" inner join he.addressEntity ae"
" where ae.cityName like :location");

Related

Issue with Join Fetch Hibernate query

I am new using hibernate join fetch queries and would like some help on the below matter.
I have one Entity that has quite a few OneToMany associations. And in the rest call
I need to load all of those associations to prepare the result.
I was using Jpa findAll(Pageable) method however on production seeing huge load on read queries. I understand that I ran into N+1 problem which I am trying to solve however getting bellow error:
{
"title" : "Internal Server Error",
"status" : 500,
"detail" : "firstResult/maxResults specified with collection fetch. In memory pagination was about to be applied. Failing because 'Fail on pagination over collection fetch' is enabled.; nested exception is org.hibernate.HibernateException: firstResult/maxResults specified with collection fetch. In memory pagination was about to be applied. Failing because 'Fail on pagination over collection fetch' is enabled.",
"cause" : {
"title" : "Internal Server Error",
"status" : 500,
"detail" : "firstResult/maxResults specified with collection fetch. In memory pagination was about to be applied. Failing because 'Fail on pagination over collection fetch' is enabled."
}
}
====================
using Running with Spring Boot v2.5.4, Spring v5.3.9 and mysql 5.7
Here is my code snippet:
Entity:
package com.pitstop.catalogue.domain;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.pitstop.catalogue.util.EligibleOffer;
import org.hibernate.annotations.BatchSize;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchMode;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.io.Serializable;
import java.time.Instant;
import java.util.*;
/**
* A Product.
*/
#Entity
#Table(name = "product")
#org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class Product implements Serializable {
private static final long serialVersionUID = 1L;
public static final String PRODUCT_NAME = "name";
public static final String PRODUCT_CATEGORY = "category_id";
public static final String PRODUCT_UNIT_PRICE = "unit_price";
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotNull
#Size(max = 250)
#Column(name = PRODUCT_NAME, length = 50, nullable = false)
private String name;
#Size(max = 1000)
#Column(name = "description", length = 200)
private String description;
private Long brandId;
#Size(max = 500)
#Column(name = "product_icon_url", length = 500)
private String productIconUrl;
#Column(name = PRODUCT_UNIT_PRICE)
private Double unitPrice;
#Column(name = "gst_rate")
private Double gstRate;
#Size(max = 45)
#Column(name = "part_no", length = 45)
private String partNo;
#Size(max = 45)
#Column(name = "serial_no", length = 45)
private String serialNo;
#Size(max = 45)
#Column(name = "unit_type", length = 45)
private String unitType;
#Column(name = "line_item_id")
private Long lineItemId;
private Long categoryId;
#OneToMany(mappedBy = "product", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#Fetch(FetchMode.JOIN)
#JsonIgnoreProperties(value = {"product"}, allowSetters = true)
#BatchSize(size = 20)
#org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
private Set<Offer> offers = new HashSet<>();
#OneToMany(mappedBy = "product", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#Fetch(FetchMode.JOIN)
#JsonIgnoreProperties(value = {"product"}, allowSetters = true)
#BatchSize(size = 20)
#org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
private Set<ProductAttributes> productAttributes = new HashSet<>();
#OneToMany(mappedBy = "product", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#Fetch(FetchMode.JOIN)
#JsonIgnoreProperties(value = {"product", "productModel"}, allowSetters = true)
#BatchSize(size = 20)
#org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
private Set<ProductModelMapping> productModelMappings = new HashSet<>();
#OneToMany(mappedBy = "product", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#Fetch(FetchMode.JOIN)
#JsonIgnoreProperties(value = {"product"}, allowSetters = true)
#BatchSize(size = 20)
#org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
private Set<ProductSeller> productSellers = new HashSet<>();
#Size(max = 45)
#Column(name = "tag", length = 50)
private String tag;
#Column(name = "created")
private Instant created;
#Column(name = "created_by", length = 100)
private String createdBy;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getProductIconUrl() {
return productIconUrl;
}
public void setProductIconUrl(String productIconUrl) {
this.productIconUrl = productIconUrl;
}
public Double getUnitPrice() {
return unitPrice;
}
public void setUnitPrice(Double unitPrice) {
this.unitPrice = unitPrice;
}
public Double getGstRate() {
return gstRate;
}
public void setGstRate(Double gstRate) {
this.gstRate = gstRate;
}
public String getPartNo() {
return partNo;
}
public void setPartNo(String partNo) {
this.partNo = partNo;
}
public String getSerialNo() {
return serialNo;
}
public void setSerialNo(String serialNo) {
this.serialNo = serialNo;
}
public String getUnitType() {
return unitType;
}
public void setUnitType(String unitType) {
this.unitType = unitType;
}
public Set<Offer> getOffers() {
return offers;
}
public void setOffers(Set<Offer> offers) {
this.offers = offers;
}
public Set<ProductAttributes> getProductAttributes() {
return productAttributes;
}
public void setProductAttributes(Set<ProductAttributes> productAttributes) {
this.productAttributes = productAttributes;
}
public Set<ProductModelMapping> getProductModelMappings() {
return productModelMappings;
}
public void setProductModelMappings(Set<ProductModelMapping> productModelMappings) {
this.productModelMappings = productModelMappings;
}
public Set<ProductSeller> getProductSellers() {
return productSellers;
}
public void setProductSellers(Set<ProductSeller> productSellers) {
this.productSellers = productSellers;
}
public Long getLineItemId() {
return lineItemId;
}
public void setLineItemId(Long lineItemId) {
this.lineItemId = lineItemId;
}
#Transient
public double getSellingPrice() {
// As discussed with Rohit, iterate over all the valid offers (by date)
// and then apply which has the highest discount value. This is for Pitstop offers only.
// Sellers offers will be created by sellers.
if (this.offers.size() > 0) {
Double sellingPriceFromOffers = EligibleOffer.apply(this.offers, this.unitPrice);
return sellingPriceFromOffers;
}
return this.unitPrice;
}
#Transient
public double getProductUnitPrice() {
return this.unitPrice;
}
#Transient
public double getProductGstRate() {
if (callSellers() != null && callSellers().size() > 0) {
ProductSeller productSeller = callSellers().get(0);
return productSeller.getGstRate();
}
return gstRate;
}
#Transient
public List<ProductSeller> callSellers() {
//sorting products by seller price in ascending order
List<ProductSeller> sellers = new ArrayList<>();
sellers.addAll(productSellers);
Collections.sort(sellers, (Comparator.comparing(ProductSeller::getUnitPrice)));
return sellers;
}
public String getTag() {
return tag;
}
public void setTag(String tag) {
this.tag = tag;
}
public Instant getCreated() {
return created;
}
public void setCreated(Instant created) {
this.created = created;
}
public String getCreatedBy() {
return createdBy;
}
public void setCreatedBy(String createdBy) {
this.createdBy = createdBy;
}
public Long getBrandId() {
return brandId;
}
public void setBrandId(Long brandId) {
this.brandId = brandId;
}
public Long getCategoryId() {
return categoryId;
}
public void setCategoryId(Long categoryId) {
this.categoryId = categoryId;
}
#Override
public String toString() {
return "Product{" +
"id=" + id +
", name='" + name + '\'' +
", description='" + description + '\'' +
", brand=" + brandId +
", productIconUrl='" + productIconUrl + '\'' +
", unitPrice=" + unitPrice +
", gstRate=" + gstRate +
", partNo='" + partNo + '\'' +
", serialNo='" + serialNo + '\'' +
", unitType='" + unitType + '\'' +
", lineItemId=" + lineItemId +
", category=" + categoryId +
", offers=" + offers +
", productAttributes=" + productAttributes +
", productModelMappings=" + productModelMappings +
", productSellers=" + productSellers +
", tag='" + tag + '\'' +
", created=" + created +
", createdBy='" + createdBy + '\'' +
'}';
}
}
Repository:
#Repository
public interface ProductRepository extends JpaRepository<Product, Long>, JpaSpecificationExecutor<Product> {
List<Product> findAllByCategoryId(Long categoryId);
List<Product> findAllByBrandId(Long brandId);
#Query(value = "select p " +
"from Product p " +
"left join fetch p.productAttributes productAttributes",
countQuery = "select count(p) from Product p")
Page<Product> findAll(Pageable pageable);
}

The primary key of the parent table is the primary key of the child table, How can I map it using jpa?

This is the diagram:
References: https://docs.spring.io/spring-batch/docs/current/reference/html/index-single.html
I get the list from the batch_job_execution_params table but the data is repeated with the information in the first row, the count is correct.
My Entity classes are as follows:
BATCH_JOB_EXECUTION TABLE
package com.maxcom.interfact_services.entity;
import com.fasterxml.jackson.annotation.JsonBackReference;
import com.fasterxml.jackson.annotation.JsonManagedReference;
import javax.persistence.*;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;
#Entity
#Table(name = "BATCH_JOB_EXECUTION")
public class BatchJobExecutionEntity implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "JOB_EXECUTION_ID", nullable = false)
private Long jobExecutionId;
#Column(name = "VERSION")
private Long version;
#ManyToOne
#JoinColumn(name = "JOB_INSTANCE_ID", nullable = false, updatable = false)
private BatchJobInstanceEntity jobInstanceId;
#Column(name = "CREATE_TIME")
#Temporal(TemporalType.TIMESTAMP)
private Date createTime;
#Column(name = "START_TIME")
#Temporal(TemporalType.TIMESTAMP)
private Date startTime;
#Column(name = "END_TIME")
#Temporal(TemporalType.TIMESTAMP)
private Date endTime;
#Column(name = "STATUS")
private String status;
#Column(name = "EXIT_CODE")
private String exitCode;
#Column(name = "EXIT_MESSAGE")
private String exitMessage;
#Column(name = "LAST_UPDATED")
#Temporal(TemporalType.TIMESTAMP)
private Date lastUpdated;
#Column(name = "JOB_CONFIGURATION_LOCATION")
private String jobConfigurationLocation;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "batchJobExecutionEntity", orphanRemoval = true)
private Set<BatchStepExecutionEntity> batchSteps;
#JsonManagedReference
#MapsId("jobExecutionId")
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "jobExecutionId", orphanRemoval = true)
// #JsonIgnoreProperties("batchJobExecutionEntity")
private List<BatchJobExecutionParamsEntity> batchJobParams = new ArrayList<>();
public BatchJobExecutionEntity() {
}
public Long getJobExecutionId() {
return jobExecutionId;
}
public void setJobExecutionId(Long jobExecutionId) {
this.jobExecutionId = jobExecutionId;
}
public Long getVersion() {
return version;
}
public void setVersion(Long version) {
this.version = version;
}
#JsonBackReference
public BatchJobInstanceEntity getJobInstanceId() {
return jobInstanceId;
}
public void setJobInstanceId(BatchJobInstanceEntity jobInstanceId) {
this.jobInstanceId = jobInstanceId;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public Date getStartTime() {
return startTime;
}
public void setStartTime(Date startTime) {
this.startTime = startTime;
}
public Date getEndTime() {
return endTime;
}
public void setEndTime(Date endTime) {
this.endTime = endTime;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public String getExitCode() {
return exitCode;
}
public void setExitCode(String exitCode) {
this.exitCode = exitCode;
}
public String getExitMessage() {
return exitMessage;
}
public void setExitMessage(String exitMessage) {
this.exitMessage = exitMessage;
}
public Date getLastUpdated() {
return lastUpdated;
}
public void setLastUpdated(Date lastUpdated) {
this.lastUpdated = lastUpdated;
}
public String getJobConfigurationLocation() {
return jobConfigurationLocation;
}
public void setJobConfigurationLocation(String jobConfigurationLocation) {
this.jobConfigurationLocation = jobConfigurationLocation;
}
#JsonManagedReference
public Set<BatchStepExecutionEntity> getBatchSteps() {
return batchSteps;
}
public void setBatchSteps(Set<BatchStepExecutionEntity> batchSteps) {
this.batchSteps = batchSteps;
}
public List<BatchJobExecutionParamsEntity> getBatchJobParams() {
return batchJobParams;
}
public void setBatchJobParams(List<BatchJobExecutionParamsEntity> batchJobParams) {
this.batchJobParams = batchJobParams;
}
}
BATCH_JOB_EXECUTION_PARAMS TABLE
package com.maxcom.interfact_services.entity;
import javax.persistence.*;
import java.io.Serializable;
import java.util.Date;
#Entity
#Table(name = "BATCH_JOB_EXECUTION_PARAMS")
public class BatchJobExecutionParamsEntity implements Serializable {
#Id
// #GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "JOB_EXECUTION_ID", length = 20)
private Long jobExecutionId;
// #JsonBackReference
// #ManyToOne(fetch = FetchType.LAZY)
// #JoinColumn(name= "JOB_EXECUTION_ID", insertable = false, updatable = false)
// #JsonIgnoreProperties("batchJobParams")
// private BatchJobExecutionEntity batchJobExecutionEntity;
#Column(name = "TYPE_CD", length = 6)
private String typeCd;
#Column(name = "KEY_NAME", length = 100)
private String keyName;
#Column(name = "STRING_VAL", length = 250)
private String stringVal;
#Column(name = "DATE_VAL")
#Temporal(TemporalType.TIMESTAMP)
private Date dateVal;
#Column(name = "LONG_VAL")
private Long longVal;
#Column(name = "DOUBLE_VAL")
private Double doubleVal;
#Column(name = "IDENTIFYING", columnDefinition = "char(1)")
private String identifying;
public BatchJobExecutionParamsEntity() {
}
public Long getJobExecutionId() {
return jobExecutionId;
}
public void setJobExecutionId(Long jobExecutionId) {
this.jobExecutionId = jobExecutionId;
}
public String getTypeCd() {
return typeCd;
}
public void setTypeCd(String typeCd) {
this.typeCd = typeCd;
}
public String getKeyName() {
return keyName;
}
public void setKeyName(String keyName) {
this.keyName = keyName;
}
public String getStringVal() {
return stringVal;
}
public void setStringVal(String stringVal) {
this.stringVal = stringVal;
}
public Date getDateVal() {
return dateVal;
}
public void setDateVal(Date dateVal) {
this.dateVal = dateVal;
}
public Long getLongVal() {
return longVal;
}
public void setLongVal(Long longVal) {
this.longVal = longVal;
}
public Double getDoubleVal() {
return doubleVal;
}
public void setDoubleVal(Double doubleVal) {
this.doubleVal = doubleVal;
}
public String getIdentifying() {
return identifying;
}
public void setIdentifying(String identifying) {
this.identifying = identifying;
}
/*
public BatchJobExecutionEntity getBatchJobExecutionEntity() {
return batchJobExecutionEntity;
}
public void setBatchJobExecutionEntity(BatchJobExecutionEntity batchJobExecutionEntity) {
this.batchJobExecutionEntity = batchJobExecutionEntity;
}
*/
}
How to map 1:N of this scenario?
Having a non-unique primary key is a recipe for disaster (/duplicates).
You missed Appendix B.3:
Note that there is no primary key for this table. This is because the framework has no use for one and, thus, does not require it. If need be, you can add a primary key may be added with a database generated key without causing any issues to the framework itself.
So you can either add a generated primary key to the table and use it as Id or create an EmbeddedId / IdClass on BatchJobExecutionParamsEntity with all the fields.
my solution was the following based on the answer from #Lookslikeitsnot and also to the following article: https://fullstackdeveloper.guru/2021/08/25/what-is-mapsid-used-for-in-jpa-hibernate-part-ii/
The keywords in this scenario were:
#Embeddable
#EmbeddedId
#MapsId
I Share my final code:
- BatchJobExecutionParamsEntity: (Child Table)
package com.maxcom.interfact_services.entity;
import com.fasterxml.jackson.annotation.JsonBackReference;
import javax.persistence.*;
import java.io.Serializable;
#Entity
#Table(name = "BATCH_JOB_EXECUTION_PARAMS")
public class BatchJobExecutionParamsEntity implements Serializable {
#EmbeddedId
private BatchJobParamIdEmbedded id;
#JsonBackReference
#ManyToOne
#MapsId("jobExecutionId")
#JoinColumn(name= "JOB_EXECUTION_ID", insertable = false, updatable = false)
private BatchJobExecutionEntity batchJobExecutionEntity;
public BatchJobExecutionParamsEntity() {
}
public BatchJobParamIdEmbedded getId() {
return id;
}
public void setId(BatchJobParamIdEmbedded id) {
this.id = id;
}
public BatchJobExecutionEntity getBatchJobExecutionEntity() {
return batchJobExecutionEntity;
}
public void setBatchJobExecutionEntity(BatchJobExecutionEntity batchJobExecutionEntity) {
this.batchJobExecutionEntity = batchJobExecutionEntity;
}
}
- BatchJobParamIdEmbedded: (Embedded Entity)
package com.maxcom.interfact_services.entity;
import javax.persistence.Column;
import javax.persistence.Embeddable;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import java.io.Serializable;
import java.util.Date;
#Embeddable
public class BatchJobParamIdEmbedded implements Serializable {
private Long jobExecutionId; // composite key class
#Column(name = "TYPE_CD", length = 6)
private String typeCd;
#Column(name = "KEY_NAME", length = 100)
private String keyName;
#Column(name = "STRING_VAL", length = 250)
private String stringVal;
#Column(name = "DATE_VAL")
#Temporal(TemporalType.TIMESTAMP)
private Date dateVal;
#Column(name = "LONG_VAL")
private Long longVal;
#Column(name = "DOUBLE_VAL")
private Double doubleVal;
#Column(name = "IDENTIFYING", columnDefinition = "char(1)")
private String identifying;
public Long getJobExecutionId() {
return jobExecutionId;
}
public void setJobExecutionId(Long jobExecutionId) {
this.jobExecutionId = jobExecutionId;
}
public String getTypeCd() {
return typeCd;
}
public void setTypeCd(String typeCd) {
this.typeCd = typeCd;
}
public String getKeyName() {
return keyName;
}
public void setKeyName(String keyName) {
this.keyName = keyName;
}
public String getStringVal() {
return stringVal;
}
public void setStringVal(String stringVal) {
this.stringVal = stringVal;
}
public Date getDateVal() {
return dateVal;
}
public void setDateVal(Date dateVal) {
this.dateVal = dateVal;
}
public Long getLongVal() {
return longVal;
}
public void setLongVal(Long longVal) {
this.longVal = longVal;
}
public Double getDoubleVal() {
return doubleVal;
}
public void setDoubleVal(Double doubleVal) {
this.doubleVal = doubleVal;
}
public String getIdentifying() {
return identifying;
}
public void setIdentifying(String identifying) {
this.identifying = identifying;
}
}
- BatchJobExecutionEntity: (Parent Table)
package com.maxcom.interfact_services.entity;
import com.fasterxml.jackson.annotation.JsonBackReference;
import com.fasterxml.jackson.annotation.JsonManagedReference;
import javax.persistence.*;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
#Entity
#Table(name = "BATCH_JOB_EXECUTION")
public class BatchJobExecutionEntity implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "JOB_EXECUTION_ID", nullable = false)
private Long jobExecutionId;
#Column(name = "VERSION")
private Long version;
#ManyToOne
#JoinColumn(name = "JOB_INSTANCE_ID", nullable = false, updatable = false)
private BatchJobInstanceEntity jobInstanceId;
#Column(name = "CREATE_TIME")
#Temporal(TemporalType.TIMESTAMP)
private Date createTime;
#Column(name = "START_TIME")
#Temporal(TemporalType.TIMESTAMP)
private Date startTime;
#Column(name = "END_TIME")
#Temporal(TemporalType.TIMESTAMP)
private Date endTime;
#Column(name = "STATUS")
private String status;
#Column(name = "EXIT_CODE")
private String exitCode;
#Column(name = "EXIT_MESSAGE")
private String exitMessage;
#Column(name = "LAST_UPDATED")
#Temporal(TemporalType.TIMESTAMP)
private Date lastUpdated;
#Column(name = "JOB_CONFIGURATION_LOCATION")
private String jobConfigurationLocation;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "batchJobExecutionEntity", orphanRemoval = true)
private List<BatchStepExecutionEntity> batchSteps;
#JsonManagedReference
#OneToMany(mappedBy = "batchJobExecutionEntity", cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true)
private List<BatchJobExecutionParamsEntity> batchJobParams = new ArrayList<>();
public BatchJobExecutionEntity() {
}
public Long getJobExecutionId() {
return jobExecutionId;
}
public void setJobExecutionId(Long jobExecutionId) {
this.jobExecutionId = jobExecutionId;
}
public Long getVersion() {
return version;
}
public void setVersion(Long version) {
this.version = version;
}
#JsonBackReference
public BatchJobInstanceEntity getJobInstanceId() {
return jobInstanceId;
}
public void setJobInstanceId(BatchJobInstanceEntity jobInstanceId) {
this.jobInstanceId = jobInstanceId;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public Date getStartTime() {
return startTime;
}
public void setStartTime(Date startTime) {
this.startTime = startTime;
}
public Date getEndTime() {
return endTime;
}
public void setEndTime(Date endTime) {
this.endTime = endTime;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public String getExitCode() {
return exitCode;
}
public void setExitCode(String exitCode) {
this.exitCode = exitCode;
}
public String getExitMessage() {
return exitMessage;
}
public void setExitMessage(String exitMessage) {
this.exitMessage = exitMessage;
}
public Date getLastUpdated() {
return lastUpdated;
}
public void setLastUpdated(Date lastUpdated) {
this.lastUpdated = lastUpdated;
}
public String getJobConfigurationLocation() {
return jobConfigurationLocation;
}
public void setJobConfigurationLocation(String jobConfigurationLocation) {
this.jobConfigurationLocation = jobConfigurationLocation;
}
#JsonManagedReference
public List<BatchStepExecutionEntity> getBatchSteps() {
return batchSteps;
}
public void setBatchSteps(List<BatchStepExecutionEntity> batchSteps) {
this.batchSteps = batchSteps;
}
public List<BatchJobExecutionParamsEntity> getBatchJobParams() {
return batchJobParams;
}
public void setBatchJobParams(List<BatchJobExecutionParamsEntity> batchJobParams) {
this.batchJobParams = batchJobParams;
}
/* Add an batchJobParam to jobExecution Entity to maintain the bi-directional OneToMapping */
public void addBatchJobExecutionParam(BatchJobExecutionParamsEntity batchJobParam) {
this.batchJobParams.add(batchJobParam);
batchJobParam.setBatchJobExecutionEntity(this);
}
}
As shown in the following image, I now get different objects, of course encapsulated in the id (BatchJobParamIdEmbedded):
The second picture shows more details:
Thank you all for your support.
Best regards

Spring data repositories - performance issue

I'm using Spring , JPArepostories and hibernate, to save some entities to database.
My entities :
Users:
#Entity
#Table(name = "users")
public class User {
#Id
#GeneratedValue
#Column(name = "ID")
private Long id;
#Column(name = "CARDID",unique=true)
private String cardId;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name ="SUPPLIERUSERID", nullable = true)
#JsonIgnore
private SupplierUser supplierUser;
#Column(name = "NAME")
private String name;
#Column(name = "SURENAME")
private String sureName;
#Column(name = "ACTIVE")
private Boolean active;
#Column(name = "SMS")
private String sms;
#Column(name = "EMAIL")
private String email;
#OneToMany(mappedBy = "user", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private List<Box> boxList = new ArrayList<Box>();
#OneToMany(mappedBy = "user", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private List<Notification> notificationList = new ArrayList<Notification>();
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getCardId() {
return cardId;
}
public void setCardId(String cardId) {
this.cardId = cardId;
}
public SupplierUser getSupplierUser() {
return supplierUser;
}
public void setSupplierUser(SupplierUser supplierUser) {
this.supplierUser = supplierUser;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSureName() {
return sureName;
}
public void setSureName(String sureName) {
this.sureName = sureName;
}
public Boolean getActive() {
return active;
}
public void setActive(Boolean active) {
this.active = active;
}
public String getSms() {
return sms;
}
public void setSms(String sms) {
this.sms = sms;
}
public List<Box> getBoxList() {
return boxList;
}
public void setBoxList(List<Box> boxList) {
this.boxList = boxList;
}
public List<Notification> getNotificationList() {
return notificationList;
}
public void setNotificationList(List<Notification> notificationList) {
this.notificationList = notificationList;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
Users have boxes:
#Entity
#Table(name = "boxes")
public class Box {
#Id
#GeneratedValue
#Column(name = "ID")
private Long id;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name ="USERID", nullable = true)
#JsonIgnore
private User user;
#Column(name = "BOXNUMBER",unique=true)
private int boxNumber;
#Column(name = "MODBUSADDRESS")
private int modbusAddress;
#Column(name = "MODBUSREGISTER")
private int modbusRegister;
#Column(name = "STATE")
private String state;
#OneToMany(mappedBy = "box", fetch =FetchType.EAGER,cascade = CascadeType.ALL)
private List<Transaction> transactionsList = new ArrayList<Transaction>();
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public int getModbusAddress() {
return modbusAddress;
}
public void setModbusAddress(int modbusAddress) {
this.modbusAddress = modbusAddress;
}
public int getModbusRegister() {
return modbusRegister;
}
public void setModbusRegister(int modbusRegister) {
this.modbusRegister = modbusRegister;
}
public List<Transaction> getTransactionsList() {
return transactionsList;
}
public void setTransactionsList(List<Transaction> transactionsList) {
this.transactionsList = transactionsList;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public int getBoxNumber() {
return boxNumber;
}
public void setBoxNumber(int boxNumber) {
this.boxNumber = boxNumber;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
}
Boxes have transactions:
#Entity
#Table(name = "transactions")
public class Transaction {
#Id
#GeneratedValue
#Column(name = "ID")
private Long id;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name ="BOXID", nullable = true)
#JsonIgnore
private Box box;
#Column(name = "TYPE")
private String type;
#Column(name = "SUPPLIERUSERCARDID")
private String supplierUserCardId;
#Column(name = "DATE")
#Temporal(TemporalType.TIMESTAMP)
private Date date;
#OneToMany(mappedBy = "transaction", fetch = FetchType.EAGER,cascade = CascadeType.ALL)
private List<Notification> notificationsList = new ArrayList<Notification>();
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Box getBox() {
return box;
}
public void setBox(Box box) {
this.box = box;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
public String getSupplierUserCardId() {
return supplierUserCardId;
}
public void setSupplierUserCardId(String supplierUserCardId) {
this.supplierUserCardId = supplierUserCardId;
}
public List<Notification> getNotificationsList() {
return notificationsList;
}
public void setNotificationsList(List<Notification> notificationsList) {
this.notificationsList = notificationsList;
}
}
And transaction have notifications (notification refer as well to user):
#Entity
#Table(name = "notifications")
public class Notification {
#Id
#GeneratedValue
#Column(name = "ID")
private Long id;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name ="TRANSACTIONID", nullable = true)
#JsonIgnore
private Transaction transaction;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name ="USERID", nullable = true)
#JsonIgnore
private User user;
#Column(name = "TYPE")
private String type;
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "CREATED")
private Date created;
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "SENDED")
private Date sended;
#Column(name = "RETRIES")
private Long retries;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public Date getCreated() {
return created;
}
public void setCreated(Date created) {
this.created = created;
}
public Date getSended() {
return sended;
}
public void setSended(Date sended) {
this.sended = sended;
}
public Long getRetries() {
return retries;
}
public void setRetries(Long retries) {
this.retries = retries;
}
public Transaction getTransaction() {
return transaction;
}
public void setTransaction(Transaction transaction) {
this.transaction = transaction;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
And my question is - what I'm doing wrong, because following method for list with 150 boxes inside takes about 20 seconds to finish.
public void changeBoxOwners(ArrayList<Box> boxList){
String id = theSoftwareCore.getSupplierUser().getCardId();
ArrayList<Box> boxToSave = new ArrayList<Box>();
for (Box box : boxList){
Box existingBox = theSoftwareCore.boxServiceImp.findByBoxNumber(box.getBoxNumber());
existingBox.setState("full");
User user = theSoftwareCore.userServiceImp.findOneByCardId(box.getUser().getCardId());
//deleting not sent notifications
for (Transaction trans : existingBox.getTransactionsList()){
for (Notification notif: trans.getNotificationsList()){
if (notif.getSended()==null){
notif.setSended(new Date(0));
}
}
}
Transaction transaction = new Transaction();
transaction.setType("in");
transaction.setSupplierUserCardId(id);
transaction.setDate(new Date());
transaction.setBox(existingBox);
Notification notification = new Notification();
notification.setCreated(new Date());
notification.setType("smsTakeYourStaff");
notification.setTransaction(transaction);
notification.setUser(user);
existingBox.setUser(user);
transaction.getNotificationsList().add(notification);
existingBox.getTransactionsList().add(transaction);
boxToSave.add(existingBox);
}
System.out.println("Start saving" + new Date());
theSoftwareCore.boxServiceImp.saveAll(boxToSave);
System.out.println("End " + new Date());
}
Thanks for your time in advance.

need to change default join column in One to Many and Many to One Mapping i n spring data jpa

I am using spring data jpa and want to change default join column happening between the entities. For example in the following mapping,
In Account.java,
#OneToMany(mappedBy = "account")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
private Set<Invoice> invoices = new HashSet<>();
In Invoice.java
#ManyToOne
private Account account;
By default the id column of Account is getting joined with account_id of Invoice as per the given one to many and many to one mapping, which is also working fine for me. But now my requirement is instead of account_id in Invoice getting joined with id of Account, I want another field account_number of Invoice to be joined with id of Account, in my one to many and many to one mappings. Is it possible? As a trail I made the following change to my Invoice
#ManyToOne
#JoinColumn(name="account_number", nullable=false)
private Account account;
But I am getting the following error:
Caused by: org.hibernate.MappingException: Repeated column in mapping for entity: com.trace.domain.Invoice column: account_number (should be mapped with insert="false" update="false")
Account.java
/**
* Account
*/
#Entity
#Table(name = "account")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class Account implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#NotNull
#Column(name = "number", nullable = false)
private String number;
#NotNull
#Column(name = "name")
private String name;
#Column(name = "currency_code")
private String currencyCode;
#Column(name = "edi_type")
private String ediType;
#Column(name = "is_fedex_express_gsr")
private Boolean isFedexExpressGsr;
#Column(name = "is_fedex_ground_gsr")
private Boolean isFedexGroundGsr;
#Column(name = "is_ups_gsr")
private Boolean isUpsGsr;
#Column(name = "electronic_voiding")
private Boolean electronicVoiding;
#Column(name = "activate_signature_service")
private Boolean activateSignatureService;
#Column(name = "reject_invoices")
private Boolean rejectInvoices;
#Column(name = "notify_client_services")
private Boolean notifyClientServices;
#Column(name = "is_active")
private Boolean isActive;
#Column(name = "address")
private String address;
#Column(name = "city")
private String city;
#Column(name = "state")
private String state;
#Column(name = "postal_code")
private String postalCode;
#OneToMany(mappedBy = "account")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
private Set<Invoice> invoices = new HashSet<>();
#ManyToOne
private Courier courier;
#ManyToOne
private Client client;
#ManyToMany
#JoinTable(
name = "account_group_members",
joinColumns = {#JoinColumn(name = "account_id", referencedColumnName = "id")},
inverseJoinColumns = {#JoinColumn(name = "group_id", referencedColumnName = "id")})
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
private Set<AccountGroup> accountGroups = new HashSet<>();
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCurrencyCode() {
return currencyCode;
}
public void setCurrencyCode(String currencyCode) {
this.currencyCode = currencyCode;
}
public String getEdiType() {
return ediType;
}
public void setEdiType(String ediType) {
this.ediType = ediType;
}
public Boolean getIsFedexExpressGsr() {
return isFedexExpressGsr;
}
public void setIsFedexExpressGsr(Boolean isFedexExpressGsr) {
this.isFedexExpressGsr = isFedexExpressGsr;
}
public Boolean getIsFedexGroundGsr() {
return isFedexGroundGsr;
}
public void setIsFedexGroundGsr(Boolean isFedexGroundGsr) {
this.isFedexGroundGsr = isFedexGroundGsr;
}
public Boolean getIsUpsGsr() {
return isUpsGsr;
}
public void setIsUpsGsr(Boolean isUpsGsr) {
this.isUpsGsr = isUpsGsr;
}
public Boolean getElectronicVoiding() {
return electronicVoiding;
}
public void setElectronicVoiding(Boolean electronicVoiding) {
this.electronicVoiding = electronicVoiding;
}
public Boolean getActivateSignatureService() {
return activateSignatureService;
}
public void setActivateSignatureService(Boolean activateSignatureService) {
this.activateSignatureService = activateSignatureService;
}
public Boolean getRejectInvoices() {
return rejectInvoices;
}
public void setRejectInvoices(Boolean rejectInvoices) {
this.rejectInvoices = rejectInvoices;
}
public Boolean getNotifyClientServices() {
return notifyClientServices;
}
public void setNotifyClientServices(Boolean notifyClientServices) {
this.notifyClientServices = notifyClientServices;
}
public Boolean getIsActive() {
return isActive;
}
public void setIsActive(Boolean isActive) {
this.isActive = isActive;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
public String getPostalCode() {
return postalCode;
}
public void setPostalCode(String postalCode) {
this.postalCode = postalCode;
}
public Courier getCourier() {
return courier;
}
public void setCourier(Courier courier) {
this.courier = courier;
}
public Client getClient() {
return client;
}
public void setClient(Client client) {
this.client = client;
}
public Set<Invoice> getInvoices() {
return invoices;
}
public void setInvoices(Set<Invoice> invoices) {
this.invoices = invoices;
}
#Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Account shipper = (Account) o;
if (!Objects.equals(id, shipper.id))
return false;
return true;
}
#Override
public int hashCode() {
return Objects.hashCode(id);
}
#Override
public String toString() {
return "Account{" + "id=" + id + ", number='" + number + "'" + ", name='" + name + "'" + ", currencyCode='"
+ currencyCode + "'" + ", ediType='" + ediType + "'" + ", isFedexExpressGsr='" + isFedexExpressGsr + "'"
+ ", isFedexGroundGsr='" + isFedexGroundGsr + "'" + ", isUpsGsr='" + isUpsGsr + "'"
+ ", electronicVoiding='" + electronicVoiding + "'" + ", activateSignatureService='"
+ activateSignatureService + "'" + ", rejectInvoices='" + rejectInvoices + "'"
+ ", notifyClientServices='" + notifyClientServices + "'" + ", isActive='" + isActive + "'"
+ ", address='" + address + "'" + ", city='" + city + "'" + ", state='" + state + "'" + ", postalCode='"
+ postalCode + "'" + '}';
}
}
Invoice.java
/**
* A Invoice.
*/
#Entity
#Table(name = "invoice")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class Invoice implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "account_number")
private String accountNumber;
#Column(name = "invoice_number")
private String invoiceNumber;
#Column(name = "invoice_amount")
private Double invoiceAmount;
#Column(name = "status")
private String status;
#Column(name = "edi_number")
private String ediNumber;
#Column(name = "bill_date")
private Date billDate;
#Column(name = "courier_id")
private Long courierId;
#ManyToOne
#JoinColumn(name="account_number", nullable=true)
private Account account;
#OneToMany(mappedBy = "invoice")
//#JsonIgnore
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
private Set<InvoiceDetails> invoiceDetailss = new HashSet<>();
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getAccountNumber() {
return accountNumber;
}
public void setAccountNumber(String accountNumber) {
this.accountNumber = accountNumber;
}
public String getInvoiceNumber() {
return invoiceNumber;
}
public void setInvoiceNumber(String invoiceNumber) {
this.invoiceNumber = invoiceNumber;
}
public Double getInvoiceAmount() {
return invoiceAmount;
}
public void setInvoiceAmount(Double invoiceAmount) {
this.invoiceAmount = invoiceAmount;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public String getEdiNumber() {
return ediNumber;
}
public void setEdiNumber(String ediNumber) {
this.ediNumber = ediNumber;
}
public Date getBillDate() {
return billDate;
}
public void setBillDate(Date billDate) {
this.billDate = billDate;
}
public Long getCourierId() {
return courierId;
}
public void setCourierId(Long courierId) {
this.courierId = courierId;
}
public Account getAccount() {
return account;
}
public void setAccount(Account account) {
this.account = account;
}
public Set<InvoiceDetails> getInvoiceDetailss() {
return invoiceDetailss;
}
public void setInvoiceDetailss(Set<InvoiceDetails> invoiceDetailss) {
this.invoiceDetailss = invoiceDetailss;
}
#Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Invoice invoice = (Invoice) o;
if ( ! Objects.equals(id, invoice.id)) return false;
return true;
}
#Override
public int hashCode() {
return Objects.hashCode(id);
}
#Override
public String toString() {
return "Invoice{" +
"id=" + id +
", accountNumber='" + accountNumber + "'" +
", invoiceNumber='" + invoiceNumber + "'" +
", invoiceAmount='" + invoiceAmount + "'" +
", ediNumber='" + ediNumber + "'" +
", status='" + status + "'" +
", billDate='" + billDate + "'" +
", courierId='" + courierId + "'" +
'}';
}
}
As the error message says, there are two different mappings for the same column: account_number. And indeed, you have
#Column(name = "account_number")
private String accountNumber;
in your entity, which is redundant with the account field.
You just need to remove that field.

unbale to form proper join query in spring data jpa using #Query in Reporsitory

I am new to Spring Data Jpa and able to get the result using proper function names on a single table. But now I am facing problem to get the result based on join. I have two tables Invoice Table (columns : accountNumber, courierId), Account Table (columns : number, clinetId). Now I need to join these two tables and get Invoice results based on courierId and clientId. So in the repository I have formed the query as shown below:
#Query("select Invoice from Invoice i left join Account a on i.accountNumber = a.number where i.courierId=?1 and a.clientId=?2")
List<Invoice> findByCourierIdAndClientId(Long courierId, Long clientId);
But I am getting the following error in my debugging log:
[ERROR] org.hibernate.hql.internal.ast.ErrorCounter - Path expected for join!
[ERROR] org.hibernate.hql.internal.ast.ErrorCounter - Path expected for join!
antlr.SemanticException: Path expected for join!
[ERROR] org.hibernate.hql.internal.ast.ErrorCounter - Invalid path: 'a.clientId'
[ERROR] org.hibernate.hql.internal.ast.ErrorCounter - Invalid path: 'a.clientId'
org.hibernate.hql.internal.ast.InvalidPathException: Invalid path: 'a.clientId'
[ERROR] org.hibernate.hql.internal.ast.ErrorCounter - left-hand operand of a binary operator was null
[ERROR] org.hibernate.hql.internal.ast.ErrorCounter - left-hand operand of a binary operator was null
antlr.SemanticException: left-hand operand of a binary operator was null
In Account Table I have client_id field in mysql table and in Account.java, I have
#ManyToOne
private Client client;
public Client getClient() {
return client;
}
public void setClient(Client client) {
this.client = client;
}
if I use the suggested solution,
#Query("SELECT i from Invoice i WHERE i.courierId =?1 AND i.clientId =?2")
List<Invoice> findByCourierIdAndClientId(Long courierId, Long clientId);
I am getting the following error:
java.lang.IllegalArgumentException: org.hibernate.QueryException: could not resolve property: clientId of: com.trace.domain.Invoice [SELECT i from com.trace.domain.Invoice i WHERE i.courierId =?1 AND i.clientId =?2]
The following are my mappings:
In Account.java,
#OneToMany(mappedBy = "account")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
private Set<Invoice> invoices = new HashSet<>();
And in Invoice.java,
#ManyToOne
private Account account;
Is there any changes I need to make in these mappings. Moreover in the solution
#Query("SELECT i from Invoice i WHERE i.courierId =?1 AND i.clientId =?2")
I don't follow how invoice table is gets only the joined details with Account, moreover there is no i.clientId in Invoice Table. clientId is present in Account Table only.
The following is my Invoice.java
/**
* A Invoice.
*/
#Entity
#Table(name = "invoice")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class Invoice implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "account_number")
private String accountNumber;
#Column(name = "invoice_number")
private String invoiceNumber;
#Column(name = "invoice_amount")
private Double invoiceAmount;
#Column(name = "status")
private String status;
#Column(name = "edi_number")
private String ediNumber;
#Column(name = "bill_date")
private Date billDate;
#Column(name = "courier_id")
private Long courierId;
// #JoinColumn(name="owner_id", nullable=false)
#ManyToOne
private Account account;
#OneToMany(mappedBy = "invoice")
//#JsonIgnore
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
private Set<InvoiceDetails> invoiceDetailss = new HashSet<>();
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getAccountNumber() {
return accountNumber;
}
public void setAccountNumber(String accountNumber) {
this.accountNumber = accountNumber;
}
public String getInvoiceNumber() {
return invoiceNumber;
}
public void setInvoiceNumber(String invoiceNumber) {
this.invoiceNumber = invoiceNumber;
}
public Double getInvoiceAmount() {
return invoiceAmount;
}
public void setInvoiceAmount(Double invoiceAmount) {
this.invoiceAmount = invoiceAmount;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public String getEdiNumber() {
return ediNumber;
}
public void setEdiNumber(String ediNumber) {
this.ediNumber = ediNumber;
}
public Date getBillDate() {
return billDate;
}
public void setBillDate(Date billDate) {
this.billDate = billDate;
}
public Long getCourierId() {
return courierId;
}
public void setCourierId(Long courierId) {
this.courierId = courierId;
}
public Account getAccount() {
return account;
}
public void setAccount(Account account) {
this.account = account;
}
public Set<InvoiceDetails> getInvoiceDetailss() {
return invoiceDetailss;
}
public void setInvoiceDetailss(Set<InvoiceDetails> invoiceDetailss) {
this.invoiceDetailss = invoiceDetailss;
}
#Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Invoice invoice = (Invoice) o;
if ( ! Objects.equals(id, invoice.id)) return false;
return true;
}
#Override
public int hashCode() {
return Objects.hashCode(id);
}
#Override
public String toString() {
return "Invoice{" +
"id=" + id +
", accountNumber='" + accountNumber + "'" +
", invoiceNumber='" + invoiceNumber + "'" +
", invoiceAmount='" + invoiceAmount + "'" +
", ediNumber='" + ediNumber + "'" +
", status='" + status + "'" +
", billDate='" + billDate + "'" +
", courierId='" + courierId + "'" +
'}';
}
}
And the following is my Account.java,
/**
* Account.
*/
#Entity
#Table(name = "account")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class Account implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#NotNull
#Column(name = "number", nullable = false)
private String number;
#NotNull
#Column(name = "name")
private String name;
#Column(name = "currency_code")
private String currencyCode;
#Column(name = "edi_type")
private String ediType;
#Column(name = "is_fedex_express_gsr")
private Boolean isFedexExpressGsr;
#Column(name = "is_fedex_ground_gsr")
private Boolean isFedexGroundGsr;
#Column(name = "is_ups_gsr")
private Boolean isUpsGsr;
#Column(name = "electronic_voiding")
private Boolean electronicVoiding;
#Column(name = "activate_signature_service")
private Boolean activateSignatureService;
#Column(name = "reject_invoices")
private Boolean rejectInvoices;
#Column(name = "notify_client_services")
private Boolean notifyClientServices;
#Column(name = "is_active")
private Boolean isActive;
#Column(name = "address")
private String address;
#Column(name = "city")
private String city;
#Column(name = "state")
private String state;
#Column(name = "postal_code")
private String postalCode;
#OneToMany(mappedBy = "account")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
private Set<Invoice> invoices = new HashSet<>();
#ManyToOne
private Courier courier;
#ManyToOne
private Client client;
#ManyToMany
#JoinTable(
name = "account_group_members",
joinColumns = {#JoinColumn(name = "account_id", referencedColumnName = "id")},
inverseJoinColumns = {#JoinColumn(name = "group_id", referencedColumnName = "id")})
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
private Set<AccountGroup> accountGroups = new HashSet<>();
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCurrencyCode() {
return currencyCode;
}
public void setCurrencyCode(String currencyCode) {
this.currencyCode = currencyCode;
}
public String getEdiType() {
return ediType;
}
public void setEdiType(String ediType) {
this.ediType = ediType;
}
public Boolean getIsFedexExpressGsr() {
return isFedexExpressGsr;
}
public void setIsFedexExpressGsr(Boolean isFedexExpressGsr) {
this.isFedexExpressGsr = isFedexExpressGsr;
}
public Boolean getIsFedexGroundGsr() {
return isFedexGroundGsr;
}
public void setIsFedexGroundGsr(Boolean isFedexGroundGsr) {
this.isFedexGroundGsr = isFedexGroundGsr;
}
public Boolean getIsUpsGsr() {
return isUpsGsr;
}
public void setIsUpsGsr(Boolean isUpsGsr) {
this.isUpsGsr = isUpsGsr;
}
public Boolean getElectronicVoiding() {
return electronicVoiding;
}
public void setElectronicVoiding(Boolean electronicVoiding) {
this.electronicVoiding = electronicVoiding;
}
public Boolean getActivateSignatureService() {
return activateSignatureService;
}
public void setActivateSignatureService(Boolean activateSignatureService) {
this.activateSignatureService = activateSignatureService;
}
public Boolean getRejectInvoices() {
return rejectInvoices;
}
public void setRejectInvoices(Boolean rejectInvoices) {
this.rejectInvoices = rejectInvoices;
}
public Boolean getNotifyClientServices() {
return notifyClientServices;
}
public void setNotifyClientServices(Boolean notifyClientServices) {
this.notifyClientServices = notifyClientServices;
}
public Boolean getIsActive() {
return isActive;
}
public void setIsActive(Boolean isActive) {
this.isActive = isActive;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
public String getPostalCode() {
return postalCode;
}
public void setPostalCode(String postalCode) {
this.postalCode = postalCode;
}
public Courier getCourier() {
return courier;
}
public void setCourier(Courier courier) {
this.courier = courier;
}
public Client getClient() {
return client;
}
public void setClient(Client client) {
this.client = client;
}
public Set<Invoice> getInvoices() {
return invoices;
}
public void setInvoices(Set<Invoice> invoices) {
this.invoices = invoices;
}
#Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Account shipper = (Account) o;
if (!Objects.equals(id, shipper.id))
return false;
return true;
}
#Override
public int hashCode() {
return Objects.hashCode(id);
}
#Override
public String toString() {
return "Shipper{" + "id=" + id + ", number='" + number + "'" + ", name='" + name + "'" + ", currencyCode='"
+ currencyCode + "'" + ", ediType='" + ediType + "'" + ", isFedexExpressGsr='" + isFedexExpressGsr + "'"
+ ", isFedexGroundGsr='" + isFedexGroundGsr + "'" + ", isUpsGsr='" + isUpsGsr + "'"
+ ", electronicVoiding='" + electronicVoiding + "'" + ", activateSignatureService='"
+ activateSignatureService + "'" + ", rejectInvoices='" + rejectInvoices + "'"
+ ", notifyClientServices='" + notifyClientServices + "'" + ", isActive='" + isActive + "'"
+ ", address='" + address + "'" + ", city='" + city + "'" + ", state='" + state + "'" + ", postalCode='"
+ postalCode + "'" + '}';
}
}
After making the following entry in InvoiceRepository.java,
#Query("from Invoice i " + " where i.courierId = :courierId " + " and i.account.client.id = :clientId ")
List<Invoice> findByCourierIdAndClientId(#Param("courierId") Long courierId, #Param("clientId") Long clientId);
I am not getting any error but I am getting any result set from mysql database. My debugging log is as follows:
[DEBUG] com.sample.aop.logging.LoggingAspect - Enter: com.sample.web.rest.InvoiceResource.getInvoicesByCourierIdAndClientId() with argument[s] = [1, 1]
[DEBUG] com.sample.web.rest.InvoiceResource - REST request to get Invoices By Courier Id and Client Id 1 1
[DEBUG] com.sample.aop.logging.LoggingAspect - Enter: com.sample.service.InvoiceService.findByCourierIdAndClientId() with argument[s] = [1, 1]
[DEBUG] com.sample.aop.logging.LoggingAspect - Exit: com.sample.service.InvoiceService.findByCourierIdAndClientId() with result = []
[DEBUG] com.sample.aop.logging.LoggingAspect - Exit: com.sample.web.rest.InvoiceResource.getInvoicesByCourierIdAndClientId() with result = []
In InvoiceResource.java, I have the following mapping:
// Get Invoices By Courier Id and CustomerId
#RequestMapping(value = "/invoices/byCourierAndClient", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
#Timed
public List<Invoice> getInvoicesByCourierIdAndClientId(#RequestParam(value = "courierId") Long courierId,
#RequestParam(value = "clientId") Long clientId) {
log.debug("REST request to get Invoices By Courier Id and Client Id " + courierId + " " + clientId);
return invoiceService.findByCourierIdAndClientId(courierId, clientId);
}
The Id field in Client.java is as follows:
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
The following query I have used to verify my data manually on mysql database tables which is returning result set containing on record.
select * from invoice i, account a where i.account_number = a.number
and i.courier_id = 1
and a.client_id = 1
A brief glance on your code seems suggested that you already have proper relationship mapping between Invoice and Account. In JPQL/HQL, you do not join like SQL by providing the join criteria. So your query should be
#Query("from Invoice i "
+ " where i.courierId = :courierId "
+ " and i.account.client.id = :clientId ")
List<Invoice> findByCourierIdAndClientId(#Param("courierId") Long courierId,
#Param("clientId") Long clientId);
(Assume the ID field in Client is called id)
Looks intuitive right?
And, given your query is really straight-forward, you can even let Spring Data generate your query by properly naming your finder method:
// no more #Query needed, works magically
List<Invoice> findByCourierIdAndAccountClientId(Long courierId, Long clientId);
You can try something like this. If you have the relation mapped correctly (#OneToMany/ManyToOne), you don't need to explicitly join the tables.
#Query("SELECT i from Invoice i WHERE i.courierId =?1 AND i.clientId =?2")
List<Invoice> findByCourierIdAndClientId(Long courierId, Long clientId);
You can even pass objects to a query directly, e.g.
#Query("SELECT i from Invoice i WHERE i.courierId= :courier AND i.clientId = :client")
List<Invoice> findByCourierAndClient(#Param("courier") Courier courier, #Param("client") Client client);

Resources