Joins on composite key using hibernate criteria - java

I am not getting how to write Hibernate criteria query to achieve the result similar to the result obtained by below SQL query. Please suggest me what are all steps need to be followed to achieve the result.
SELECT PRODUCT.PRODUCTNAME, ITEM.ITEMNAME
FROM PRODUCT_ITEM
JOIN PRODUCT
ON PRODUCT_ITEM.ID = PRODUCT.ID
JOIN ITEM
ON PRODUCT_ITEM.ID = ITEM.ID
Above is my Sql Query to fetch the product_name and item_name. It is working correctly.
I tried get the same result using HIBERNATE CRITERIA QUERY.
Criteria criteria = session.createCriteria(ProductItem.class,"pi");
criteria.createAlias("pi.pk.product", "pip");
criteria.createAlias("pi.pk.item", "pii");
criteria.setProjection(Projections.projectionList().add(Projections.property("pip.id")).add(Projections.property("pii.id")));
List<Object[]> list = criteria.list();
i am getting error saying
Exception in thread "main" org.hibernate.exception.SQLGrammarException: could not execute query
at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:67)
at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:43)
at org.hibernate.loader.Loader.doList(Loader.java:2147)
at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2028)
at org.hibernate.loader.Loader.list(Loader.java:2023)
at org.hibernate.loader.criteria.CriteriaLoader.list(CriteriaLoader.java:95)
at org.hibernate.impl.SessionImpl.list(SessionImpl.java:1569)
at org.hibernate.impl.CriteriaImpl.list(CriteriaImpl.java:283)
at checkComposite.main(checkComposite.java:38)
Caused by: org.postgresql.util.PSQLException: ERROR: missing FROM-clause entry for table "pip1_"
Position: 8
at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2198)
at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:1927)
at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:255)
at org.postgresql.jdbc2.AbstractJdbc2Statement.execute(AbstractJdbc2Statement.java:561)
at org.postgresql.jdbc2.AbstractJdbc2Statement.executeWithFlags(AbstractJdbc2Statement.java:419)
at org.postgresql.jdbc2.AbstractJdbc2Statement.executeQuery(AbstractJdbc2Statement.java:304)
at org.hibernate.jdbc.AbstractBatcher.getResultSet(AbstractBatcher.java:186)
at org.hibernate.loader.Loader.getResultSet(Loader.java:1668)
at org.hibernate.loader.Loader.doQuery(Loader.java:662)
at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:224)
at org.hibernate.loader.Loader.doList(Loader.java:2144)
Here my ENTITYS are as below.
#Entity
#Table(name = "item")
public class Item {
private Integer id;
private String name;
private List<ProductItem> productItems = new LinkedList<ProductItem>();
public Item() {
}
#Id
#GenericGenerator(name = "generator", strategy = "increment")
#GeneratedValue(generator = "generator")
#Column(name = "item_id", nullable = false)
public Integer getId() {
return this.id;
}
public void setId(Integer id) {
this.id = id;
}
#Column(name = "name")
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
#OneToMany(fetch = FetchType.LAZY, mappedBy = "pk.item")
public List<ProductItem> getProductItems() {
return this.productItems;
}
public void setProductItems(List<ProductItem> productItems) {
this.productItems = productItems;
}
}
Product Entity
#Entity
#Table(name = "product")
public class Product {
private Integer id;
private String name;
private List<ProductItem> productItems = new LinkedList<ProductItem>();
public Product() {
}
#Id
#GenericGenerator(name = "generator", strategy = "increment")
#GeneratedValue(generator = "generator")
#Column(name = "product_id", nullable = false)
public Integer getId() {
return this.id;
}
public void setId(Integer id) {
this.id = id;
}
#Column(name = "name")
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
#OneToMany(fetch = FetchType.LAZY, mappedBy = "pk.product")
public List<ProductItem> getProductItems() {
return this.productItems;
}
public void setProductItems(List<ProductItem> productItems) {
this.productItems = productItems;
}
}
PRODUCT_ITEM entity.
#Entity
#Table(name = "product_item")
#AssociationOverrides({
#AssociationOverride(name = "pk.item", joinColumns = #JoinColumn(name = "item_id")),
#AssociationOverride(name = "pk.product", joinColumns = #JoinColumn(name = "product_id"))
})
public class ProductItem {
private ProductItemPk pk = new ProductItemPk();
#EmbeddedId
private ProductItemPk getPk() {
return pk;
}
private void setPk(ProductItemPk pk) {
this.pk = pk;
}
#Transient
public Item getItem() {
return getPk().getItem();
}
public void setItem(Item item) {
getPk().setItem(item);
}
#Transient
public Product getProduct() {
return getPk().getProduct();
}
public void setProduct(Product product) {
getPk().setProduct(product);
}
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ProductItem that = (ProductItem) o;
if (getPk() != null ? !getPk().equals(that.getPk()) : that.getPk() != null) return false;
return true;
}
public int hashCode() {
return (getPk() != null ? getPk().hashCode() : 0);
}
}
Embedable Class is as below.
#Embeddable
public class ProductItemPk implements Serializable {
private Item item;
private Product product;
#ManyToOne
public Item getItem() {
return item;
}
public void setItem(Item item) {
this.item = item;
}
#ManyToOne
public Product getProduct() {
return product;
}
public void setProduct(Product product) {
this.product = product;
}
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ProductItemPk that = (ProductItemPk) o;
if (item != null ? !item.equals(that.item) : that.item != null) return false;
if (product != null ? !product.equals(that.product) : that.product != null)
return false;
return true;
}
public int hashCode() {
int result;
result = (item != null ? item.hashCode() : 0);
result = 31 * result + (product != null ? product.hashCode() : 0);
return result;
}
}

Try changing the query to:
Criteria criteria = session.createCriteria(ProductItem.class,"pi");
criteria.createAlias("pi.pk", "pipk");
criteria.createAlias("pipk.product", "pip");
criteria.createAlias("pipk.item", "pii");
criteria.setProjection(Projections.projectionList().add(Projections.property("pip.id")).add(Projections.property("pii.id")));
List<Object[]> list = criteria.list();

Related

Detached entity passed to persist: JPA OneToMany relations

I'm trying to set up save entity with children together, but I can't find how it is works, I tried to do like in all tutorials but always had a error detached entity passed to persist
this is my entities:
#Entity
#Table(name = "CurriculumVitaes")
public class CurriculumVitae {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#NotBlank
private String title;
private String summary;
#OneToMany(mappedBy = "curriculumVitae", targetEntity = Education.class, cascade={CascadeType.ALL})
private List<Education> educations = new ArrayList<>();
public CurriculumVitae(){}
public CurriculumVitae(Long id, String title, String summary){
this.id = id;
this.title = title;
this.summary = summary;
}
public void addEducation(Education education) {
if(this.educations.contains(education)){
return;
}
this.educations.add(education);
education.setCurriculumVitae(this);
}
public void removeEducation(Education education){
if(this.educations.contains(education)){
return;
}
this.educations.remove(education);
education.setCurriculumVitae(null);
}
public boolean equals(Object object) {
if (object == this)
return true;
if ((object == null) || !(object instanceof CurriculumVitae))
return false;
final CurriculumVitae a = (CurriculumVitae)object;
if (id != null && a.getId() != null) {
return id.equals(a.getId());
}
return false;
}
}
and child entity:
#Entity
#Table(name = "Educations")
public class Education {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#NotBlank
private String title;
private String areaOfStudy;
private String description;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "curriculumVitaeId", referencedColumnName = "id")
private CurriculumVitae curriculumVitae;
public Education(){}
public Education(String title, String areaOfStudy, String description){
this.title = title;
this.areaOfStudy = areaOfStudy;
this.description = description;
}
public CurriculumVitae getCurriculumVitae() {
return curriculumVitae;
}
public void setCurriculumVitae(CurriculumVitae curriculumVitae) {
//prevent endless loop
if (sameAsFormer(curriculumVitae))
return ;
//set new owner
CurriculumVitae oldOwner = this.curriculumVitae;
this.curriculumVitae = curriculumVitae;
//remove from the old owner
if (oldOwner!=null)
oldOwner.removeEducation(this);
//set myself into new owner
if (curriculumVitae!=null)
curriculumVitae.addEducation(this);
}
private boolean sameAsFormer(CurriculumVitae newOwner) {
return this.curriculumVitae==null? newOwner == null : this.curriculumVitae.equals(newOwner);
}
public boolean equals(Object object) {
if (object == this)
return true;
if ((object == null) || !(object instanceof Education))
return false;
final Education a = (Education)object;
if (id != null && a.getId() != null) {
return id.equals(a.getId());
}
return false;
}
}
How can I set up save entities all together with one repository method save?
without cascade it will not save inner entities.
#PostMapping
public #ResponseBody CurriculumVitaeViewModel addCurriculumVitae(#RequestBody SaveCurriculumVitaeRequest model){
CurriculumVitae newCv = new CurriculumVitae();
curriculumVitaeModelMapper.Map(model, newCv);
for (EducationViewModel ed:model.getEducations()
) {
Education newEd = new Education();
educationModelMapper.Map(ed, newEd);
newCv.addEducation(newEd);
}
CurriculumVitae savedCv = curriculumVitaeRepository.save(newCv);

JPA Criteria Query - How to implement Join on two tables to get desired result in single Query

I have 2 classes mapped with db Tables.
Composite Primary Key Class :
#Embeddable
public class Pk implements Serializable, Cloneable {
#Column(name = "dataId")
private String dataId;
#Column(name = "occurrenceTime")
private Timestamp occurrenceTime;
public String getDataId() {
return dataId;
}
public Pk setDataId(String dataId) {
this.dataId = dataId;
return this;
}
public Timestamp getOccurrenceTime() {
return occurrenceTime;
}
public Pk setOccurrenceTime(Timestamp occurrenceTime) {
this.occurrenceTime = occurrenceTime;
return this;
}
#Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Pk pk = (Pk) o;
return Objects.equals(getDataId(), pk.getDataId()) &&
Objects.equals(getOccurrenceTime(), pk.getOccurrenceTime());
}
#Override
public int hashCode() {
return Objects.hash(getDataId(), getOccurrenceTime());
}
}
1 : LoadProfile
#Entity
#Table(name = "energy")
public class LoadProfile implements Serializable, Cloneable {
public LoadProfile() {
}
#EmbeddedId
private Pk pk;
#Column(name = "RECEIVE_TIME")
private Timestamp reportingTime;
#Column(name = "DATA1")
private Double DATA1;
#OneToOne
#JoinColumns({
#JoinColumn(name = "dataId", insertable = false, updatable = false, referencedColumnName = "dataId"),
#JoinColumn(name = "occurrenceTime", insertable = false, updatable = false, referencedColumnName = "occurrenceTime")
})
private ForwardPower forwardPower;
public Pk getPk() {
return pk;
}
public LoadProfile setPk(Pk pk) {
this.pk = pk;
return this;
}
public Timestamp getReportingTime() {
return reportingTime;
}
public LoadProfile setReportingTime(Timestamp reportingTime) {
this.reportingTime = reportingTime;
return this;
}
public Double getDATA1() {
return DATA1;
}
public LoadProfile setDATA1(Double DATA1) {
this.DATA1 = DATA1;
return this;
}
public ForwardPower getForwardPower() {
return forwardPower;
}
public LoadProfile setForwardPower(
ForwardPower forwardPower) {
this.forwardPower = forwardPower;
return this;
}
#Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
LoadProfile that = (LoadProfile) o;
return Objects.equals(getPk(), that.getPk());
}
#Override
public int hashCode() {
return Objects.hash(getPk());
}
}
2 : ForwardPower
#Entity
#Table(name = "forward_power")
public class ForwardPower implements Serializable, Cloneable {
public ForwardPower() {
}
#EmbeddedId
private Pk pk;
#Column(name = "RECEIVE_TIME")
private Timestamp reportingTime;
#Column(name = "DATA2")
private Double DATA2;
public Pk getPk() {
return pk;
}
public ForwardPower setPk(Pk pk) {
this.pk = pk;
return this;
}
public Timestamp getReportingTime() {
return reportingTime;
}
public ForwardPower setReportingTime(Timestamp reportingTime) {
this.reportingTime = reportingTime;
return this;
}
public Double getDATA2() {
return DATA2;
}
public ForwardPower setDATA2(Double DATA2) {
this.DATA2= DATA2;
return this;
}
#Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
ForwardPower that = (ForwardPower) o;
return Objects.equals(getPk(), that.getPk());
}
#Override
public int hashCode() {
return Objects.hash(getPk());
}
}
I want to execute a query
Select * From energy e
Left join forward_power fp
on fp.dataId== e.dataId and fp.occurrenceTime == e.occurrenceTime
where e.occurrenceTime >= '2017-12-28 00:00:00'
and e.occurrenceTime <= '2018-01-02 00:00:00'
Limit 1000;
I wrote a equivalent Query in java using JPA criteria Query
CriteriaBuilder cb = session.getCriteriaBuilder();
CriteriaQuery<LoadProfile> cq = cb.createQuery(LoadProfile.class);
Root<LoadProfile> loadProfileRoot = cq.from(LoadProfile.class);
Join<LoadProfile, ForwardPower> join = loadProfileRoot.join(LoadProfile_.forwardPower);
List<Predicate> conditions = new ArrayList();
conditions.add(cb.equal(loadProfileRoot.get(LoadProfile_.pk).get(Pk_.dataId), join.get(
ForwardPower_.pk).get(Pk_.dataId)));
conditions.add(cb.equal(loadProfileRoot.get(LoadProfile_.pk).get(Pk_.occurrenceTime),
join.get(ForwardPower_.pk).get(Pk_.occurrenceTime)));
conditions.add(
cb.greaterThanOrEqualTo(loadProfileRoot.get(LoadProfile_.pk).get(Pk_.occurrenceTime),
config.getDataStartTime()));
conditions.add(
cb.lessThanOrEqualTo(loadProfileRoot.get(LoadProfile_.pk).get(Pk_.occurrenceTime),
config.getDataEndTime()));
cq.select(loadProfileRoot);
cq.where(conditions.toArray(new Predicate[]{}));
Query query = session.createQuery(cq);
List list = query.setFirstResult(0).setMaxResults(1000).getResultList();
I set the Option hibernate.show_sql = true.
Now that query gives me exact 1000 desired result.
when i see the hibernate query which is generated by ORM by above code.
ORM create 1 query for energy table and 1000 queries for forwardpower table which cause performance issue and query take too much time aproximately 55 - 60 seconds for fetching 1000 records.
How i can create a criteria Query so that ORM generate exactly 1 query for that code?
Thanks in advance.
You can add a fetch type eager instructions on your relation, and the ForwardPower will be load with LoadProfile with any LoadProfile.find
#OneToOne(fetch=FetchType.EAGER)
#JoinColumns({
#JoinColumn(name = "dataId", insertable = false, updatable = false, referencedColumnName = "dataId"),
#JoinColumn(name = "occurrenceTime", insertable = false, updatable = false, referencedColumnName = "occurrenceTime")
})
private ForwardPower forwardPower;
Or you can add the fetch instruction in your query.
I'm not familiar with it but it's probably something like that
//instead of loadProfileRoot.join(LoadProfile_.forwardPower)
Join<LoadProfile, ForwardPower> join = (Join<LoadProfile, ForwardPower>) loadProfileRoot.fetch(LoadProfile_.forwardPower);
See JPA 2 Criteria Fetch Path Navigation for more information about fetch with CriteriaBuilder.

Many to Many relationships using Spring Boot, Jackson and Hibernate

I'm working on a rest project using Spring Boot and Hibernate and am currently trying to figure out how to handle my json-serialization.
The schema shown in the ERD above is mapped by Hibernate and works fine.
The problem arises when I make a get request to a controller. My understanding is that Spring now tries to serialize the object-chain using Jackson. Because both the parent and child objects have one another as an attribute, we find ourselves hitting an infinite recursion loop.
Now I've looked into #JsonIgnore, #JsonView, #JsonManagedReference and #JsonBackReference but these only seem to work for one-to-many relationships.
What I'm looking for is a situation where when I for instance make a GET request to /users/{id}, I get the user object including all it's relationship attributes (let's call it the full object), but the relationship attributes themselves don't show their relationship-attributes (minimized objects). This works fine with the annotations mentioned above, but how do I make this work the other way as well?
Desired response for: /users/{id}
{ // full user object
id: 1,
username: 'foo',
// password can be JsonIgnored because of obvious reasons
role: { // minimized role object
id: 1,
name: 'bar'
// NO USERS LIST
}
area: { //minimized area object
id: 2,
name: 'some val'
// NO USERS LIST
// NO TABLES LIST
}
}
Desired response for /userrole/{id}
{ // full role object
id: 1,
name: 'waiter'
users: [
{ // minmized user object
id: 1,
username: 'foo'
// password can be JsonIgnored because of obvious reasons
// NO ROLE OBJECT
// NO AREA OBJECT
},
{ // minmized user object
id: 1,
username: 'foo'
// password can be JsonIgnored because of obvious reasons
// NO ROLE OBJECT
// NO AREA OBJECT
}
]
}
In general: I'd like a full object when the request is made to the entity directly and a minimized object when requested indirectly.
Any Ideas? I hope my explanation is clear enough.
UPDATE
The Area, User and UserRole POJO's as requested in the comment sections.
User
#Entity
#Table(name = "users", schema = "public", catalog = "PocketOrder")
public class User {
private int id;
private String username;
private String psswrd;
private List<Area> areas;
private UserRole Role;
#Id
#Column(name = "id", nullable = false)
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
#Basic
#Column(name = "username", nullable = false, length = 20)
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
#Basic
#JsonIgnore
#Column(name = "psswrd", nullable = true, length = 40)
public String getPsswrd() {
return psswrd;
}
public void setPsswrd(String psswrd) {
this.psswrd = psswrd;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
if (id != user.id) return false;
if (username != null ? !username.equals(user.username) : user.username != null) return false;
if (psswrd != null ? !psswrd.equals(user.psswrd) : user.psswrd != null) return false;
return true;
}
#Override
public int hashCode() {
int result = id;
result = 31 * result + (username != null ? username.hashCode() : 0);
result = 31 * result + (psswrd != null ? psswrd.hashCode() : 0);
return result;
}
#ManyToMany(mappedBy = "users")
public List<Area> getAreas() {
return areas;
}
public void setAreas(List<Area> areas) {
this.areas = areas;
}
#ManyToOne
#JoinColumn(name = "role_fk", referencedColumnName = "id", nullable = false)
public UserRole getRole() {
return Role;
}
public void setRole(UserRole role) {
Role = role;
}
}
UserRole
#Entity
#javax.persistence.Table(name = "userroles", schema = "public", catalog = "PocketOrder")
public class UserRole {
private int id;
private String name;
private List<User> users;
#Id
#Column(name = "id", nullable = false)
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
#Basic
#Column(name = "name", nullable = false, length = 20)
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
UserRole userRole = (UserRole) o;
if (id != userRole.id) return false;
if (name != null ? !name.equals(userRole.name) : userRole.name != null) return false;
return true;
}
#Override
public int hashCode() {
int result = id;
result = 31 * result + (name != null ? name.hashCode() : 0);
return result;
}
#OneToMany(mappedBy = "role")
public List<User> getUsers() {
return users;
}
public void setUsers(List<User> users) {
users = users;
}
}
Area
#Entity
#javax.persistence.Table(name = "areas", schema = "public", catalog = "PocketOrder")
public class Area {
private int id;
private String name;
private List<User> users;
private List<Table> tables;
#Id
#Column(name = "id", nullable = false)
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
#Basic
#Column(name = "name", nullable = false, length = 20)
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Area area = (Area) o;
if (id != area.id) return false;
if (name != null ? !name.equals(area.name) : area.name != null) return false;
return true;
}
#Override
public int hashCode() {
int result = id;
result = 31 * result + (name != null ? name.hashCode() : 0);
return result;
}
#ManyToMany
#JoinTable(name = "areas_users", catalog = "PocketOrder", schema = "public", joinColumns = #JoinColumn(name = "area_fk", referencedColumnName = "id", nullable = false), inverseJoinColumns = #JoinColumn(name = "user_fk", referencedColumnName = "id", nullable = false))
public List<User> getUsers() {
return users;
}
public void setUsers(List<User> users) {
this.users = users;
}
#OneToMany(mappedBy = "area")
public List<Table> getTables() {
return tables;
}
public void setTables(List<Table> tables) {
this.tables = tables;
}
}
Try use #JsonSerialize on specific points:
For sample:
1 - Map your field
#JsonSerialize(using = ExampleSampleSerializer.class)
#ManyToOne
private Example example;
2 - Create custom jackson serializer (Here you can control the serialization)
public class ExampleSampleSerializer extends JsonSerializer<Example> {
#Override
public void serialize(Example value, JsonGenerator jsonGenerator, SerializerProvider serializers) throws IOException, JsonProcessingException {
jsonGenerator.writeStartObject();
jsonGenerator.writeFieldName("first");
jsonGenerator.writeNumber(value.getFirstValue());
jsonGenerator.writeFieldName("second");
jsonGenerator.writeNumber(value.getSecondValue());
jsonGenerator.writeFieldName("third");
jsonGenerator.writeNumber(value.getAnyAnotherClass().getThirdValue());
jsonGenerator.writeEndObject();
}
}
The way I worked around this on a Many-to-Many relationship is by using
#JsonIgnore
On one of the Entities.
For example we have Person and Child entities. One Person can have many children and vice-versa.
On Person we have :
public class Person
{
//Other fields ommited
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
#JoinTable(name = "person_child",
joinColumns = {
#JoinColumn(name = "person_id", referencedColumnName = "id", nullable = false,
updatable = false)
},
inverseJoinColumns = {
#JoinColumn(name = "child_id", referencedColumnName = "id", nullable =
false, updatable = false)
})
private Set<Child> children = new HashSet<>() ;
}
And on Child we have :
public class Child
{
#JsonIgnore
#ManyToMany(mappedBy = "children", fetch = FetchType.LAZY)
private Set<Person> people = new HashSet<>() ;
}
Now when we get a Person, we also get all his connected children. But when we get a Child then we don't get all People because we have #JsonIgnore annotation on it.
This fixes the Infinite Recursion problem, and raises this one.
My workaround was by writing a query to get me all the People connected to a specific child_id.
Below you may see my code:
public interface PersonDAO extends JpaRepository<Person, Long>
{
#Query(value = "SELECT * " +
" FROM person p INNER JOIN person_child j " +
"ON p.id = j.person_id WHERE j.child_id = ?1 ", nativeQuery = true)
public List<Person> getPeopleViaChildId(long id);
}
And i use it whenever I want to get all People from a child.

Deployment Error caused by Entity (Ecommerce)

I'm really stuck here and I'd love to get some help right about now.
Everytime I try to deploy, it keeps saying that it failed.
EDIT:
Okay so I've realized that the main issue seems to be:
Caused by: Exception [EclipseLink-7154] (Eclipse Persistence Services - 2.5.2.v20140319-9ad6abd): org.eclipse.persistence.exceptions.ValidationException
Exception Description: The attribute [productCollection] in entity class [class entity.Category] has a mappedBy value of [category] which does not exist in its owning entity class [class entity.Product]. If the owning entity class is a #MappedSuperclass, this is invalid, and your attribute should reference the correct subclass.
The entity classes are as follows:
Category.java
#Entity
#Table(name = "category")
#NamedQueries({
#NamedQuery(name = "Category.findAll", query = "SELECT c FROM Category c"),
#NamedQuery(name = "Category.findById", query = "SELECT c FROM Category c WHERE c.id = :id"),
#NamedQuery(name = "Category.findByName", query = "SELECT c FROM Category c WHERE c.name = :name")})
public class Category implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "id")
private Short id;
#Basic(optional = false)
#Column(name = "name")
private String name;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "category")
private Collection<Product> productCollection;
public Category() {
}
public Category(Short id) {
this.id = id;
}
public Category(Short id, String name) {
this.id = id;
this.name = name;
}
public Short getId() {
return id;
}
public void setId(Short id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Collection<Product> getProductCollection() {
return productCollection;
}
public void setProductCollection(Collection<Product> productCollection) {
this.productCollection = productCollection;
}
#Override
public int hashCode() {
int hash = 0;
hash += (id != null ? id.hashCode() : 0);
return hash;
}
#Override
public boolean equals(Object object) {
// TODO: Warning - this method won't work in the case the id fields are not set
if (!(object instanceof Category)) {
return false;
}
Category other = (Category) object;
if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
return false;
}
return true;
}
#Override
public String toString() {
return "entity.Category[id=" + id + "]";
}
}
Product.java
#Entity
#Table(name = "product")
#XmlRootElement
#NamedQueries({
#NamedQuery(name = "Product.findAll", query = "SELECT p FROM Product p"),
#NamedQuery(name = "Product.findById", query = "SELECT p FROM Product p WHERE p.id = :id"),
#NamedQuery(name = "Product.findByName", query = "SELECT p FROM Product p WHERE p.name = :name"),
#NamedQuery(name = "Product.findByPrice", query = "SELECT p FROM Product p WHERE p.price = :price"),
#NamedQuery(name = "Product.findByDescription", query = "SELECT p FROM Product p WHERE p.description = :description"),
#NamedQuery(name = "Product.findByLastUpdate", query = "SELECT p FROM Product p WHERE p.lastUpdate = :lastUpdate")})
public class Product implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "id")
private Integer id;
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 45)
#Column(name = "name")
private String name;
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 45)
#Column(name = "price")
private BigDecimal price;
#Size(max = 255)
#Column(name = "description")
private String description;
#Basic(optional = false)
#NotNull
#Column(name = "last_update")
#Temporal(TemporalType.TIMESTAMP)
private Date lastUpdate;
#JoinColumn(name = "category_id", referencedColumnName = "id")
#ManyToOne(optional = false)
private Category categoryId;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "product")
private Collection<OrderedProduct> orderedProductCollection;
public Product() {
}
public Product(Integer id) {
this.id = id;
}
public Product(Integer id, String name, BigDecimal price, Date lastUpdate) {
this.id = id;
this.name = name;
this.price = price;
this.lastUpdate = lastUpdate;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public BigDecimal getPrice() {
return price;
}
public void setPrice(BigDecimal price) {
this.price = price;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Date getLastUpdate() {
return lastUpdate;
}
public void setLastUpdate(Date lastUpdate) {
this.lastUpdate = lastUpdate;
}
public Category getCategoryId() {
return categoryId;
}
public void setCategoryId(Category categoryId) {
this.categoryId = categoryId;
}
#XmlTransient
public Collection<OrderedProduct> getOrderedProductCollection() {
return orderedProductCollection;
}
public void setOrderedProductCollection(Collection<OrderedProduct> orderedProductCollection) {
this.orderedProductCollection = orderedProductCollection;
}
#Override
public int hashCode() {
int hash = 0;
hash += (id != null ? id.hashCode() : 0);
return hash;
}
#Override
public boolean equals(Object object) {
// TODO: Warning - this method won't work in the case the id fields are not set
if (!(object instanceof Product)) {
return false;
}
Product other = (Product) object;
if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
return false;
}
return true;
}
#Override
public String toString() {
return "entity.Product[ id=" + id + " ]";
}
}
Please help me as soon as possible.
Thank you.
I figured out the solution.
All i had to do was change:
#OneToMany(cascade = CascadeType.ALL, mappedBy = "category")
to
#OneToMany(cascade = CascadeType.ALL, mappedBy = "categoryId")
Because in my Product.java, the category is mentioned as:
public Category getCategoryId() {
return categoryId;
}

Issue with cascade persisting of the composite primary key mapping based on UUID identity

So here is short description of my problem. I have two tables, first contains Person details:
#Entity
#Converter(name = "uuidConverter", converterClass = UUIDConverter.class)
public class Person {
private UUID mId;
private String mLogin;
#Id
#UuidGenerator(name = "uuid")
#GeneratedValue(generator = "uuid")
#Column(name = "id", nullable = false)
#Convert("uuidConverter")
public UUID getId() {
return mId;
}
#Column(name = "login", nullable = false)
public String getLogin() {
return mLogin;
}
public Person setId(UUID id) {
mId = id;
return this;
}
public Person setLogin(String login) {
mLogin = login;
return this;
}
}
Second table contains multiple person preferences:
#IdClass(PersonPreference.Pk.class)
#Table(name = "person_preference")
#Entity
#Converter(name = "uuidConverter", converterClass = UUIDConverter.class)
public class PersonPreference {
private Person mPerson;
private String mComponentUid;
private String mComponentProperties;
#SuppressWarnings("UnusedDeclaration")
static class Pk implements Serializable {
private String mComponentUid;
private UUID mPerson;
public String getComponentUid() {
return mComponentUid;
}
public void setComponentUid(String componentUid) {
mComponentUid = componentUid;
}
#Convert("uuidConverter")
public UUID getPerson() {
return mPerson;
}
public void setPerson(UUID person) {
mPerson = person;
}
#Override
public int hashCode() {
return Objects.hashCode(mComponentUid, mPerson);
}
#Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Pk other = (Pk) obj;
return Objects.equal(this.mComponentUid, other.mComponentUid) && Objects.equal(this.mPerson, other.mPerson);
}
}
#Id
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "person_id", nullable = false)
public Person getPerson() {
return mPerson;
}
#Id
#Column(name = "component_uid", nullable = false)
public String getComponentUid() {
return mComponentUid;
}
#Column(name = "component_properties", nullable = false)
public String getComponentProperties() {
return mComponentProperties;
}
public PersonPreference setPerson(Person person) {
mPerson = person;
return this;
}
public PersonPreference setComponentUid(String componentUid) {
mComponentUid = componentUid;
return this;
}
public PersonPreference setComponentProperties(String componentProperties) {
mComponentProperties = componentProperties;
return this;
}
#Override
public int hashCode() {
return Objects.hashCode(mPerson, mComponentUid, mComponentProperties);
}
#Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final PersonPreference other = (PersonPreference) obj;
return Objects.equal(this.mPerson, other.mPerson)
&& Objects.equal(this.mComponentUid, other.mComponentUid)
&& Objects.equal(this.mComponentProperties, other.mComponentProperties);
}
}
I also wrote a simple test:
Person person = new Person()
.setLogin("PersonPreferencePersistenceTestLogin");
PersonPreference personPreference = new PersonPreference()
.setPerson(person)
.setComponentUid("4028808C3AA49ABB013AA49ABB2B0000")
.setComponentProperties("{123}");
mPersonPreferenceService.save(personPreference);
Optional<PersonPreference> newPersonPreference = mPersonPreferenceService.getByPersonAndComponentUid(
person,
"4028808C3AA49ABB013AA49ABB2B0000"
);
Assert.assertEquals(personPreference.getComponentProperties(), newPersonPreference.get().getComponentProperties());
We used previously Hibernate as JPA provider and that code works perfectly. Unfortunately, after switching JPA provider to Eclipselink, cascade persisting dosen't work correctly. From log I found out:
--INSERT INTO PERSON (id, login) VALUES (?, ?)
bind => [f2ce518c-8f37-4fac-bf5b-c8225d228b28, PersonPreferencePersistenceTestLogin]
--INSERT INTO person_preference (component_uid, component_properties, person_id) VALUES (?, ?, ?)
bind => [4028808C3AA49ABB013AA49ABB2B0000, {123}, f2ce518c-8f37-4fac-bf5b-c8225d228b28]
--SELECT component_uid, component_properties, person_id FROM person_preference WHERE ((person_id = ?) AND (component_uid = ?))
bind => [null, 4028808C3AA49ABB013AA49ABB2B0000]
But person.getId() returns null value. I don't know why? Other mappings works fine with cascade persisting, but that fails in Eclipselink.

Categories

Resources