Mapping self associated bidirectional entity issue - java

I have the following Parameter entity which has a self-association, I m trying to make a unit test to check my implementation, but it feels that I m doing some errors in my mapping and I can't detect where I m being mistaken.
My entity is as below
import java.io.Serializable;
import java.time.OffsetDateTime;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.Column;
import javax.persistence.Convert;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
import com.tuto.common.enums.ParameterCategory;
import com.tuto.common.enums.ParameterType;
import com.tuto.common.enums.converter.ParameterTypePersistenceConverter;
/**
* This class represents the PARAMETERS SQL table as a java entity.
*
*/
#Entity
#Table(name = "PARAMETERS")
public class Parameter implements Serializable {
/**
* serialVersionUID.
*/
private static final long serialVersionUID = -732987999122243011L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SQ_PARAMETER_ID")
#SequenceGenerator(name = "SQ_PARAMETER_ID", sequenceName = "SQ_PARAMETER_ID", allocationSize = 1)
#Column(name = "PARAMETER_ID", unique = true)
private long id;
#Column(name = "PARAMETER_NAME")
private String name;
#Column(name = "PARAMETER_LABEL")
private String label;
#Column(name = "PARAMETER_COMMENT")
private String comment;
#JoinColumn(name = "ES_ID")
#OneToOne(fetch = FetchType.LAZY)
private ExpertSystem expertSystem;
#Column(name = "PARAMETER_CATEGORY")
#Enumerated(EnumType.ORDINAL)
private ParameterCategory category;
#Column(name = "PARAMETER_TYPE")
#Convert(converter = ParameterTypePersistenceConverter.class)
private ParameterType type;
#Column(name = "PARAMETER_CREATION_DATE",columnDefinition = "TIMESTAMP")
private OffsetDateTime creationDate;
#JoinColumn(name = "PARAMETER_CREATION_USER",referencedColumnName = "USER_FIRSTNAME")
#OneToOne(fetch = FetchType.LAZY)
private User creationUser;
#Column(name = "PARAMETER_UPDATE_DATE",columnDefinition = "TIMESTAMP")
private OffsetDateTime updateDate;
#JoinColumn(name = "PARAMETER_UPDATE_USER",referencedColumnName = "USER_FIRSTNAME")
#OneToOne(fetch = FetchType.LAZY)
private User updateUser;
#Column(name = "MULTIVALUE_SIZE")
private int multivalueSize;
#OneToMany(fetch = FetchType.LAZY)
#JoinTable(name = "dependences",
joinColumns = {#JoinColumn(name ="PARAMETER_ID")},
inverseJoinColumns = {#JoinColumn(name ="DEPENDANCE_ID")})
private List<Parameter> dependences = new ArrayList<>();
#OneToMany(fetch = FetchType.LAZY)
#JoinTable(name = "dependences",
joinColumns = {#JoinColumn(name ="DEPENDANCE_ID")},
inverseJoinColumns = {#JoinColumn(name ="PARAMETER_ID")})
private List<Parameter> consequences = new ArrayList<>();
public Parameter() {}
public Parameter(long id, String name, String label, String comment, ExpertSystem expertSystem,
ParameterCategory category, ParameterType parameterType, OffsetDateTime creationDate, User creationUser,
OffsetDateTime updateDate, User updateUser, int multivalueSize,List<Parameter> dependences) {
super();
this.id = id;
this.name = name;
this.label = label;
this.comment = comment;
this.expertSystem = expertSystem;
this.category = category;
this.type = parameterType;
this.creationDate = creationDate;
this.creationUser = creationUser;
this.updateDate = updateDate;
this.updateUser = updateUser;
this.multivalueSize = multivalueSize;
this.dependences = dependences;
}
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 getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
public String getComment() {
return comment;
}
public void setComment(String comment) {
this.comment = comment;
}
public ExpertSystem getExpertSystem() {
return expertSystem;
}
public void setExpertSystem(ExpertSystem expertSystem) {
this.expertSystem = expertSystem;
}
public ParameterCategory getCategory() {
return category;
}
public void setCategory(ParameterCategory category) {
this.category = category;
}
public ParameterType getType() {
return type;
}
public void setType(ParameterType parameterType) {
this.type = parameterType;
}
public OffsetDateTime getCreationDate() {
return creationDate;
}
public List<Parameter> getDependences() {
return dependences;
}
public List<Parameter> getConsequences() {
return consequences;
}
public void setCreationDate(OffsetDateTime creationDate) {
this.creationDate = creationDate;
}
public User getCreationUser() {
return creationUser;
}
public void setCreationUser(User creationUser) {
this.creationUser = creationUser;
}
public OffsetDateTime getUpdateDate() {
return updateDate;
}
public void setUpdateDate(OffsetDateTime updateDate) {
this.updateDate = updateDate;
}
public User getUpdateUser() {
return updateUser;
}
public void setUpdateUser(User updateUser) {
this.updateUser = updateUser;
}
public int getMultivalueSize() {
return multivalueSize;
}
public void setMultivalueSize(int multivalueSize) {
this.multivalueSize = multivalueSize;
}
public void setDependences(List<Parameter> dependences) {
this.dependences = dependences;
}
public void setConsequences(List<Parameter> consequences) {
this.consequences = consequences;
}
public void addDependence(Parameter dependence) {
if(dependence != null ) {
removeConsequence(dependence);
this.dependences.add(dependence);
dependence.getConsequences().add(this);
}
}
public void removeDependence(Parameter dependence) {
if(dependence != null && this.dependences.contains(dependence) ) {
final int indexOfConsequence = this.dependences.indexOf(dependence);
Parameter parameter = this.dependences.get(indexOfConsequence) ;
parameter.getDependences().remove(this);
}
}
public void addConsequence(Parameter consequence) {
if(consequence != null ) {
removeConsequence(consequence);
this.consequences.add(consequence);
consequence.getDependences().add(this);
}
}
public void removeConsequence(Parameter consequence) {
if(consequence != null && this.consequences.contains(consequence) ) {
final int indexOfConsequence = this.consequences.indexOf(consequence);
Parameter parameter = this.consequences.get(indexOfConsequence) ;
parameter.getDependences().remove(this);
}
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (int) (id ^ (id >>> 32));
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Parameter other = (Parameter) obj;
if (id != other.id)
return false;
return true;
}
#Override
public String toString() {
return "Parameter [id=" + id + ", name=" + name + ", label=" + label + ", comment=" + comment
+ ", expertSystem=" + expertSystem + ", category=" + category + ", type=" + type
+ ", creationDate=" + creationDate + ", creationUser=" + creationUser + ", updateDate=" + updateDate
+ ", updateUser=" + updateUser + ", multivalueSize=" + multivalueSize + ", dependences=" + dependences + "]";
}
}
My repository is like below
import java.util.List;
import org.springframework.data.jpa.repository.EntityGraph;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import com.tuto.entity.Parameter;
/**
* This interface defines the contract of the possible operations to manage the parameter entity in the database.
*
*/
#Repository
public interface IParameterRepository extends JpaRepository<Parameter, Long> {
/**
* This method fetches the list of all available parameters. It could return an empty list.
*
* #return List<Parameter> The list of all Parameters found
*/
#Transactional
#Modifying(clearAutomatically = false)
#Query(value = "SELECT param FROM Parameter param")
List<Parameter> getAllParameters();
/**
* This method fetches the dependencies list (as Parameter list) for the given Param id.
* It could return an empty list.
*
* #param idParam The id of Parameter.
* #return List<Parameter> The dependencies list (as Parameter list) found for the given Param id.
*/
#Transactional
#Modifying(clearAutomatically = false)
#EntityGraph(attributePaths = {"dependences","expertSystem"})
#Query(value = "SELECT param.dependences FROM Parameter param join fetch param.dependences where param.id = :#{#idParam} ", nativeQuery = false)
List<Parameter> getDependancesByParamId(final long idParam);
/**
* This method fetches the consequences list (as Parameter list) for the given Param id.
* It could return an empty list.
*
* #param idParam The id of Parameter.
* #return List<Parameter> The dependencies list (as Parameter list) found for the given Param id.
*/
#Transactional
#Modifying(clearAutomatically = false)
#EntityGraph(attributePaths = {"consequences","expertSystem"})
#Query(value = "SELECT param.consequences FROM Parameter param join fetch param.consequences where param.id = :#{#idParam} ", nativeQuery = false)
List<Parameter> getConsequencesByParamId(final long idParam);
}
My unit test is as below
import static org.junit.Assert.assertEquals;
import java.util.List;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager;
import org.springframework.test.context.ActiveProfiles;
import com.tuto.common.enums.ParameterCategory;
import com.tuto.common.enums.ParameterType;
import com.tuto.entity.Parameter;
import com.tuto.core.profile.PfSpringProfiles;
import com.tuto.test.Tags;
#DataJpaTest(showSql = true)
#Tag(Tags.COMPONENT)
#ActiveProfiles(PfSpringProfiles.TEST)
public class ParameterRepositoryTest {
#Autowired
private TestEntityManager entityManager;
#Autowired
private IParameterRepository parameterRepository;
#BeforeEach
public void intTests() {
final Parameter parameterInput = new Parameter();
parameterInput.setCategory(ParameterCategory.INPUT);
parameterInput.setType(ParameterType.DECIMAL);
parameterInput.setId(1L);
final Parameter parameterIntermediate = new Parameter();
parameterIntermediate.setCategory(ParameterCategory.INTERMEDIATE);
parameterIntermediate.setType(ParameterType.DECIMAL);
parameterIntermediate.setId(2L);
final Parameter parameterOutput = new Parameter();
parameterOutput.setCategory(ParameterCategory.OUTPUT);
parameterOutput.setType(ParameterType.DECIMAL);
parameterOutput.setId(3L);
entityManager.merge(parameterInput);
entityManager.merge(parameterIntermediate);
entityManager.merge(parameterOutput);
// parameterInput.getConsequences().add(parameterIntermediate);
parameterInput.addConsequence(parameterIntermediate);
parameterIntermediate.addConsequence(parameterOutput);
// parameterIntermediate.getConsequences().add(parameterOutput);
// parameterOutput.getDependences().add(parameterIntermediate);
// parameterIntermediate.getDependences().add(parameterInput);
entityManager.merge(parameterOutput);
entityManager.merge(parameterIntermediate);
entityManager.merge(parameterInput);
}
#Test
public void checkConsequencesAndDependencesAreNotEmpty() {
List<Parameter> parameter = parameterRepository.getDependancesByParamId(2L);
assertEquals(parameter.size(),1);
assertEquals(parameter.get(0).getId(),1L);
}
}
I m facing two exceptions: the first is the famous persist detached entity the other is query specified join fetching, but the owner of the fetched association was not present in the select list
Fullstacktrace
Caused by: java.lang.IllegalArgumentException: org.hibernate.QueryException: query specified join fetching, but the owner of the fetched association was not present in the select list [FromElement{explicit,not a collection join,fetch join,fetch non-lazy properties,classAlias=null,role=com.tuto.entity.Parameter.consequences,tableName=parameters,tableAlias=parameter2_,origin=parameters parameter0_,columns={parameter0_.parameter_id,className=com.tuto.entity.Parameter}}] [SELECT param.consequences FROM com.tuto.entity.Parameter param join fetch param.consequences where param.id = :__$synthetic$__1 ]
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:138)
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:181)
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:188)
at org.hibernate.internal.AbstractSharedSessionContract.createQuery(AbstractSharedSessionContract.java:757)
at org.hibernate.internal.AbstractSharedSessionContract.createQuery(AbstractSharedSessionContract.java:114)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.orm.jpa.ExtendedEntityManagerCreator$ExtendedEntityManagerInvocationHandler.invoke(ExtendedEntityManagerCreator.java:362)
at com.sun.proxy.$Proxy140.createQuery(Unknown Source)
at org.springframework.data.jpa.repository.query.SimpleJpaQuery.validateQuery(SimpleJpaQuery.java:90)
... 88 common frames omitted
Caused by: org.hibernate.QueryException: query specified join fetching, but the owner of the fetched association was not present in the select list [FromElement{explicit,not a collection join,fetch join,fetch non-lazy properties,classAlias=null,role=com.tuto.entity.Parameter.consequences,tableName=parameters,tableAlias=parameter2_,origin=parameters parameter0_,columns={parameter0_.parameter_id,className=com.tuto.entity.Parameter}}] [SELECT param.consequences FROM com.tuto.entity.Parameter param join fetch param.consequences where param.id = :__$synthetic$__1 ]
at org.hibernate.QueryException.generateQueryException(QueryException.java:120)
at org.hibernate.QueryException.wrapWithQueryString(QueryException.java:103)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:220)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.compile(QueryTranslatorImpl.java:144)
at org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:113)
at org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:73)
at org.hibernate.engine.query.spi.QueryPlanCache.getHQLQueryPlan(QueryPlanCache.java:162)
at org.hibernate.internal.AbstractSharedSessionContract.getQueryPlan(AbstractSharedSessionContract.java:636)
at org.hibernate.internal.AbstractSharedSessionContract.createQuery(AbstractSharedSessionContract.java:748)
... 96 common frames omitted
Caused by: org.hibernate.QueryException: query specified join fetching, but the owner of the fetched association was not present in the select list [FromElement{explicit,not a collection join,fetch join,fetch non-lazy properties,classAlias=null,role=com.tuto.entity.Parameter.consequences,tableName=parameters,tableAlias=parameter2_,origin=parameters parameter0_,columns={parameter0_.parameter_id,className=com.tuto.entity.Parameter}}]
at org.hibernate.hql.internal.ast.tree.SelectClause.initializeExplicitSelectClause(SelectClause.java:215)
at org.hibernate.hql.internal.ast.HqlSqlWalker.useSelectClause(HqlSqlWalker.java:1028)
at org.hibernate.hql.internal.ast.HqlSqlWalker.processQuery(HqlSqlWalker.java:796)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.query(HqlSqlBaseWalker.java:694)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.selectStatement(HqlSqlBaseWalker.java:330)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.statement(HqlSqlBaseWalker.java:278)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.analyze(QueryTranslatorImpl.java:276)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:192)
... 102 common frames omitted

I did fix the issue but I m still facing some interrogations as below :
The Fix
#Transactional
#Modifying(clearAutomatically = false)
// #EntityGraph(attributePaths = {"dependences","expertSystem"})
#Query(value = "SELECT param.dependences FROM Parameter param where param.id = :#{#idParam} ", nativeQuery = false)
List<Parameter> getDependancesByParamId(final long idParam);
/**
* This method fetches the consequences list (as Parameter list) for the given Param id.
* It could return an empty list.
*
* #param idParam The id of Parameter.
* #return List<Parameter> The dependances list (as Parameter list) found for the given Param id .
*/
#Transactional
#Modifying(clearAutomatically = false)
// #EntityGraph(attributePaths = {"consequences","expertSystem"})
#Query(value = "SELECT param.consequences FROM Parameter param where param.id = :#{#idParam} ", nativeQuery = false)
List<Parameter> getConsequencesByParamId(final long idParam);
I believe that maybe selecting the entity element of param.consequences will help me avoid to fetch this collection, however what about every ExpertSystem entity element, this is using a lasy loading mechanism, shouldn't I be able to fetch join on it from the collection.
Also shouldn't the entity graph help me avoid this issue by auto fetching collection or child elements !!!
You can reproduce this experiment based on this link

Related

How to recover a product entity with this query below?

I'm waiting for a product type entity object with the quantity and total cahmps containing the values ​​of SUM (lc.quantity) as quantite, SUM (lc.montant) as total, but I get an object of type java.lang.object
This is my query
#NamedQuery(name = "Produit.findAllBuyPorductGroupByProduct",
query = "SELECT p, SUM(lc.quantite) quantite, SUM(lc.montant) total
FROM LigneCommande lc JOIN lc.produit p
GROUP BY p")
This is my product entity
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package com.instantech.stogeg.data.base.entity;
import java.io.Serializable;
import java.util.Collection;
import java.util.Date;
import javax.persistence.Basic;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Transient;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;
/**
*
* #author instantech
*/
#Entity
#Table(name = "produit")
#XmlRootElement
#NamedQueries({
#NamedQuery(name = "Produit.findAll", query = "SELECT p FROM Produit p")
, #NamedQuery(name = "Produit.findById", query = "SELECT p FROM Produit p WHERE p.id = :id")
, #NamedQuery(name = "Produit.findByReference", query = "SELECT p FROM Produit p WHERE p.reference = :reference")
, #NamedQuery(name = "Produit.findByDesignation", query = "SELECT p FROM Produit p WHERE p.designation = :designation")
, #NamedQuery(name = "Produit.findByStock", query = "SELECT p FROM Produit p WHERE p.stock = :stock")
, #NamedQuery(name = "Produit.findAllBuyPorductGroupByProduct", query = "SELECT p, SUM(lc.quantite) quantite, SUM(lc.montant) total FROM LigneCommande lc JOIN lc.produit p GROUP BY p")
, #NamedQuery(name = "Produit.findBy_", query = "SELECT p FROM Produit p WHERE CAST(p.id as CHAR) LIKE :numeroProduit OR CAST(p.pu as CHAR) LIKE :pu OR CAST(p.stock as CHAR) LIKE :stock OR p.reference LIKE :reference OR p.designation LIKE :designation ORDER BY p.designation")
, #NamedQuery(name = "Produit.findByPu", query = "SELECT p FROM Produit p WHERE p.pu = :pu")
, #NamedQuery(name = "Produit.findByDate", query = "SELECT p FROM Produit p WHERE p.date = :date")
, #NamedQuery(name = "Produit.findByHeure", query = "SELECT p FROM Produit p WHERE p.heure = :heure")})
public class Produit 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)
#Column(name = "reference")
private String reference;
#Basic(optional = false)
#Column(name = "designation")
private String designation;
#Basic(optional = false)
#Column(name = "stock")
private int stock;
#Basic(optional = false)
#Column(name = "pu")
private float pu;
#Basic(optional = false)
#Column(name = "date")
#Temporal(TemporalType.DATE)
private Date date;
#Basic(optional = false)
#Column(name = "heure")
#Temporal(TemporalType.TIME)
private Date heure;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "produit")
private Collection<LigneCommande> ligneCommandeCollection;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "produit")
private Collection<HistoryStock> historyStockCollection;
#Transient
private int quantite;
#Transient
private double total;
public Produit() {
}
public Produit(Integer id) {
this.id = id;
}
public Produit(Integer id, String reference, String designation, int stock, float pu, Date date, Date heure) {
this.id = id;
this.reference = reference;
this.designation = designation;
this.stock = stock;
this.pu = pu;
this.date = date;
this.heure = heure;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getReference() {
return reference;
}
public void setReference(String reference) {
this.reference = reference;
}
public String getDesignation() {
return designation;
}
public void setDesignation(String designation) {
this.designation = designation;
}
public int getStock() {
return stock;
}
public void setStock(int stock) {
this.stock = stock;
}
public float getPu() {
return pu;
}
public void setPu(float pu) {
this.pu = pu;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
public Date getHeure() {
return heure;
}
public void setHeure(Date heure) {
this.heure = heure;
}
#XmlTransient
public Collection<LigneCommande> getLigneCommandeCollection() {
return ligneCommandeCollection;
}
public void setLigneCommandeCollection(Collection<LigneCommande> ligneCommandeCollection) {
this.ligneCommandeCollection = ligneCommandeCollection;
}
#XmlTransient
public Collection<HistoryStock> getHistoryStockCollection() {
return historyStockCollection;
}
public void setHistoryStockCollection(Collection<HistoryStock> historyStockCollection) {
this.historyStockCollection = historyStockCollection;
}
#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 Produit)) {
return false;
}
Produit other = (Produit) object;
return !((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id)));
}
#Override
public String toString() {
return this.getDesignation() + " ( "+this.getStock()+ " ) ";
}
public Object[] getProduit(){
return new Object [] {
this.getId(), this.getReference(), this.getDesignation(), this.getStock(), this.getPu()
};
}
public int updateStock(int quantite) throws Exception{
if(quantite > this.getStock())
throw new Exception("Le stock du produit est inférieur à la quantité commandé");
else
return (this.getStock() - quantite);
}
public int adQuantity(int quantite) throws Exception{
if(quantite > 0){
return (this.getStock() + quantite);
}else{
throw new Exception("Le stock du produit est ne peut pas être null ou négatif");
}
}
public int getQuantite() {
return quantite;
}
public void setQuantite(int quantite) {
this.quantite = quantite;
}
public double getTotal() {
return total;
}
public void setTotal(double total) {
this.total = total;
}
}
This is my line command entity
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package com.instantech.stogeg.data.base.entity;
import java.io.Serializable;
import java.util.Collection;
import java.util.Date;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;
/**
*
* #author instantech
*/
#Entity
#Table(name = "ligne_commande")
#XmlRootElement
#NamedQueries({
#NamedQuery(name = "LigneCommande.findAll", query = "SELECT l FROM LigneCommande l")
, #NamedQuery(name = "LigneCommande.findById", query = "SELECT l FROM LigneCommande l WHERE l.id = :id")
, #NamedQuery(name = "LigneCommande.findByQuantite", query = "SELECT l FROM LigneCommande l WHERE l.quantite = :quantite")
, #NamedQuery(name = "LigneCommande.findByMontant", query = "SELECT l FROM LigneCommande l WHERE l.montant = :montant")})
public class LigneCommande implements Serializable {
#Basic(optional = false)
#Column(name = "date")
#Temporal(TemporalType.DATE)
private Date date;
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "id")
private Integer id;
#Basic(optional = false)
#Column(name = "quantite")
private int quantite;
#Basic(optional = false)
#Column(name = "montant")
private double montant;
#ManyToMany(mappedBy = "ligneCommandeCollection")
private Collection<Commande> commandeCollection;
#JoinColumn(name = "produit", referencedColumnName = "id")
#ManyToOne(optional = false)
private Produit produit;
public LigneCommande() {
}
public LigneCommande(Integer id) {
this.id = id;
}
public LigneCommande(Integer id, int quantite, double montant, Date date) {
this.id = id;
this.quantite = quantite;
this.montant = montant;
this.date = date;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public int getQuantite() {
return quantite;
}
public void setQuantite(int quantite) {
this.quantite = quantite;
}
public double getMontant() {
return montant;
}
public void setMontant(double montant) {
this.montant = montant;
}
#XmlTransient
public Collection<Commande> getCommandeCollection() {
return commandeCollection;
}
public void setCommandeCollection(Collection<Commande> commandeCollection) {
this.commandeCollection = commandeCollection;
}
public Produit getProduit() {
return produit;
}
public void setProduit(Produit produit) {
this.produit = produit;
}
#Override
public int hashCode() {
int hash = 0;
hash += (id != null ? id.hashCode() : 0);
return hash;
}
#Override
public boolean equals(Object object) {
if (!(object instanceof LigneCommande)) {
return false;
}
LigneCommande other = (LigneCommande) object;
return !((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id)));
}
#Override
public String toString() {
return "com.instantech.entity.LigneCommande[ id=" + id + " ]";
}
public void addQuantite(int quantite){
this.quantite += quantite;
}
public double getTotal() {
return this.getQuantite() * this.getProduit().getPu();
}
public Object[] getLigneCommande(){
return new Object[] {
this.getProduit().getReference(),
this.getProduit().getDesignation(),
this.getQuantite(),
String.format("%, .0f",this.getProduit().getPu()),
String.format("%, .0f",this.getTotal())
};
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
}
The function that retrieves the result of my query
/**
* Return list of product that grouped by product
*
* #return
*/
public List<Produit> findProductGroupBy() {
TypedQuery<Produit> query = this.getEntitymanager().createNamedQuery("Produit.findAllBuyPorductGroupByProduct", Produit.class);
return query.getResultList();
}
Test of my query
public class test {
private static ProduitManager pm;
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
pm = new ProduitManager();
pm.findProductGroupBy().forEach((p)->{
System.out.println(p);
});
pm.close();
}
}
This is the result after execution
Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to com.instantech.stogeg.data.base.entity.Produit
at java.util.Vector.forEach(Vector.java:1275)
at test.main(test.java:22)
NB: In my product table in the database, the quantity and total field that are in my product entity are not there.
1) Create some sort of a TO:
public class ResultDTO{
private Product product;
private Integer quantity;
private Double total;
public ResultDTO(Product product, Integer quantity, Double total){
// set fields
}
2) Alter the query to use that class:
#NamedQuery(name = "Produit.findAllBuyPorductGroupByProduct",
query = "SELECT new com.mypackage.ResultDTO(
p as product, SUM(lc.quantite) as quantity, SUM(lc.montant) as total)
FROM LigneCommande lc JOIN lc.produit p
GROUP BY p")
3) Change generic type to ResultDTO:
public List<ResultDTO> findProductGroupBy() {
TypedQuery<ResultDTO> query = this.getEntitymanager().createNamedQuery(
"Produit.findAllBuyPorductGroupByProduct", ResultDTO.class);
return query.getResultList();
}
What you are looking for is called projections. How to use them is very thoroughly explained over here. They work better than custom objects (via new keyword) because they support native queries and are generally easier to understand.
Step 1: Declare a simple bean data transfer object (dto)
#Data
#NoArgsConstructor
#AllArgsConstructor
public class ProductSummaryDto {
private Product product;
private Integer quantity;
private Double total;
}
Step 2: Create custom spring data query in your repository
#Query("SELECT p product, SUM(lc.quantite) quantity, SUM(lc.montant) total FROM LigneCommande lc JOIN lc.produit p GROUP BY p")
List<ProductSummaryDto> getProductSummaries();

JPQL query with left join

I'm trying to create a JPQL query that would join two tables together and I could set up data in them.
public void uploadSaveGame(User user, String saveData)
{
EntityTransaction entr=em.getTransaction();
entr.begin();
try{
Query query = em.createQuery("UPDATE Saves s SET s.save = :saveData" +
"WHERE s.login = :user in (select sa.saveid,sa.Users.login, sa.savedata from Saves sa "
+ "LEFT JOIN sa.Users m WHERE sa.saves = m.users.saveid)", Save.class);
query.setParameter("login", user);
query.setParameter("saveData", saveData);
query.executeUpdate();
entr.commit();
}catch(Exception e){
entr.rollback();
}
}
I have this entites:
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package com.dke.ps.Tables;
import java.io.Serializable;
import java.util.Collection;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;
/**
*
* #author michal
*/
#Entity
#Table(name = "saves")
#XmlRootElement
#NamedQueries({
#NamedQuery(name = "Saves.findAll", query = "SELECT s FROM Saves s")
, #NamedQuery(name = "Saves.findBySaveid", query = "SELECT s FROM Saves s WHERE s.saveid = :saveid")
, #NamedQuery(name = "Saves.findBySavedata", query = "SELECT s FROM Saves s WHERE s.savedata = :savedata")})
public class Saves implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "saveid")
private Integer saveid;
#Column(name = "savedata")
private String savedata;
#JoinColumn(name = "userid", referencedColumnName = "userid")
#ManyToOne(optional = false)
private Users userid;
#OneToMany(mappedBy = "saveid")
private Collection<Users> usersCollection;
public Saves() {
}
public Saves(Integer saveid) {
this.saveid = saveid;
}
public Integer getSaveid() {
return saveid;
}
public void setSaveid(Integer saveid) {
this.saveid = saveid;
}
public String getSavedata() {
return savedata;
}
public void setSavedata(String savedata) {
this.savedata = savedata;
}
public Users getUserid() {
return userid;
}
public void setUserid(Users userid) {
this.userid = userid;
}
#XmlTransient
public Collection<Users> getUsersCollection() {
return usersCollection;
}
public void setUsersCollection(Collection<Users> usersCollection) {
this.usersCollection = usersCollection;
}
#Override
public int hashCode() {
int hash = 0;
hash += (saveid != null ? saveid.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 Saves)) {
return false;
}
Saves other = (Saves) object;
if ((this.saveid == null && other.saveid != null) || (this.saveid != null && !this.saveid.equals(other.saveid))) {
return false;
}
return true;
}
#Override
public String toString() {
return "com.dke.ps.Tables.Saves[ saveid=" + saveid + " ]";
}
}
And Users class:
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package com.dke.ps.Tables;
import java.io.Serializable;
import java.util.Collection;
import java.util.Date;
import javax.persistence.Basic;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;
/**
*
* #author michal
*/
#Entity
#Table(name = "users")
#XmlRootElement
#NamedQueries({
#NamedQuery(name = "Users.findAll", query = "SELECT u FROM Users u")
, #NamedQuery(name = "Users.findByUserid", query = "SELECT u FROM Users u WHERE u.userid = :userid")
, #NamedQuery(name = "Users.findByLogin", query = "SELECT u FROM Users u WHERE u.login = :login")
, #NamedQuery(name = "Users.findByPassword", query = "SELECT u FROM Users u WHERE u.password = :password")
, #NamedQuery(name = "Users.findByEmail", query = "SELECT u FROM Users u WHERE u.email = :email")
, #NamedQuery(name = "Users.findByDate", query = "SELECT u FROM Users u WHERE u.date = :date")})
public class Users implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Basic(optional = false)
#Column(name = "userid")
private Integer userid;
#Column(name = "login")
private String login;
#Column(name = "password")
private String password;
#Column(name = "email")
private String email;
#Column(name = "date")
#Temporal(TemporalType.DATE)
private Date date;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "userid")
private Collection<Saves> savesCollection;
#JoinColumn(name = "saveid", referencedColumnName = "saveid")
#ManyToOne
private Saves saveid;
public Users() {
}
public Users(Integer userid) {
this.userid = userid;
}
public Integer getUserid() {
return userid;
}
public void setUserid(Integer userid) {
this.userid = userid;
}
public String getLogin() {
return login;
}
public void setLogin(String login) {
this.login = login;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
#XmlTransient
public Collection<Saves> getSavesCollection() {
return savesCollection;
}
public void setSavesCollection(Collection<Saves> savesCollection) {
this.savesCollection = savesCollection;
}
public Saves getSaveid() {
return saveid;
}
public void setSaveid(Saves saveid) {
this.saveid = saveid;
}
#Override
public int hashCode() {
int hash = 0;
hash += (userid != null ? userid.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 Users)) {
return false;
}
Users other = (Users) object;
if ((this.userid == null && other.userid != null) || (this.userid != null && !this.userid.equals(other.userid))) {
return false;
}
return true;
}
#Override
public String toString() {
return "com.dke.ps.Tables.Users[ userid=" + userid + " ]";
}
}
These two classes were automatically generated by the database connection. So, in that query, I think I should work with them and not directly with the tables in the database. It is so?
Unfortunately, I do not edit any column data, and I do not know where the problem is
My Users entity has Many To Many relationship with Saves entity.
If you printed the exception you would have probably noticed that you have a :user parameter but you are instead trying to set a login parameter
query.setParameter("login", user);

How to persist a many to one, bidirectional entity on the owning side in Java JPA?

I am getting a null value for persisting of a one to many bidirectional entity causing a constraint error.
I have two entities set up as follows below and I have an 'abstract facade' which handles the entity manager that simply persists, flushes, merges etc the entities to the database. I have a Restful backend to handle interaction between ejbs and web clients (mobile inc). All is working in order apart from when I try to persist a "token" in this example adding an existing user to it. The code below should explain what I am trying to achieve.
User
package ejb;
import java.io.Serializable;
import java.util.Collection;
import javax.persistence.Basic;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;
import javax.persistence.PrePersist;
import java.util.UUID;
import javax.persistence.UniqueConstraint;
#Entity
#Table(name = "users", catalog = "test", schema = "public", uniqueConstraints = #UniqueConstraint(columnNames = {"username", "email"}))
#XmlRootElement
#NamedQueries({
#NamedQuery(name = "Users.findAll", query = "SELECT u FROM Users u")
})
public class Users implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 36)
#Column(name = "userid")
private String userid;
#Size(max = 100)
#Column(name = "username")
private String username;
#Size(max = 108)
#Column(name = "email")
private String email;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "users")
private Collection<Tokens> tokensCollection;
public Users() {
}
public Users(String userid) {
this.userid = userid;
}
public String getUserid() {
return userid;
}
public void serUserid(String userid) {
this.userid= userid;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
#XmlTransient
public Collection<Tokens> getTokensCollection() {
return tokensCollection;
}
public void setTokensCollection(Tokens token) {
token.setUser(this);
tokensCollection.add(token);
}
#Override
public String toString() {
return "ejb.Users[ userid =" + userid + " ]";
}
#PrePersist
protected void setUUID(){
String uuid = UUID.randomUUID().toString();
this.userid = uuid;
}
}
Tokens
package ejb;
import java.io.Serializable;
import java.sql.Timestamp;
import java.util.Date;
import java.util.UUID;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.PrePersist;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import javax.xml.bind.annotation.XmlRootElement;
#Entity
#Table(name = "tokens", catalog = "test", schema = "public")
#XmlRootElement
#NamedQueries({
#NamedQuery(name = "Tokens.findAll", query = "SELECT t FROM Tokens t"),
#NamedQuery(name = "Tokens.findByTokenid", query = "SELECT t FROM Tokens t WHERE t.tokenid = :tokenid"),
#NamedQuery(name = "Tokens.findByDevice", query = "SELECT t FROM Tokens t WHERE t.device = :device"),
#NamedQuery(name = "Tokens.findByJWT", query = "SELECT t FROM Tokens t WHERE t.jwt = :jwt"),
#NamedQuery(name = "Tokens.findByCreatedTime", query = "SELECT t FROM Tokens t WHERE t.createdTime = :createdTime"),
#NamedQuery(name = "Tokens.findByExpires", query = "SELECT t FROM Tokens t WHERE t.expires = :expires")
})
public class Tokens implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 36)
#Column(name = "tokenid")
private String tokenid;
#Column(name = "device")
private String device;
#Basic(optional = false)
#NotNull
#Column(name = "jwt")
private String jwt;
#Basic(optional = false)
#NotNull
#Column(name = "createdTime")
#Temporal(TemporalType.TIMESTAMP)
private Date createdTime;
#Basic(optional = false)
#NotNull
#Column(name = "expires")
#Temporal(TemporalType.TIMESTAMP)
private Date expires;
#JoinColumn(name = "userid", referencedColumnName = "userid", insertable = false, updatable = false)
#ManyToOne(optional = false)
#NotNull
private Users users;
public String getTokenid() {
return tokenid;
}
public void setTokenid(String tokenid) {
this.tokenid = tokenid;
}
public String getDevice() {
return device;
}
public void setDevice(String device) {
this.device = device;
}
public String getJWT() {
return jwt;
}
public void setJWT(String JWT) {
this.jwt = JWT;
}
public Date getCreatedTime() {
return createdTime;
}
public void setCreatedTime(Date createdTime) {
this.createdTime = createdTime;
}
public Date getExpiryTime() {
return expires;
}
public void setExpiryTime(Date expires) {
this.expires = expires;
}
public Users getUsers() {
return users;
}
public void setUsers(Users users) {
this.users = users;
}
#Override
public int hashCode() {
int hash = 0;
hash += (tokenid != null ? tokenid.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 Tokens)) {
return false;
}
Tokens other = (Tokens) object;
if ((this.tokenid == null && other.tokenid != null) || (this.tokenid != null && !this.tokenid.equals(other.tokenid))) {
return false;
}
return true;
}
#Override
public String toString() {
return "ejb.tokens[ id=" + tokenid + " ]";
}
#PrePersist
protected void setUUID() {
String uuid = UUID.randomUUID().toString();
this.tokenid = uuid;
this.createdTime = new Timestamp(new Date().getTime());
long expTime = 60 * 60 * 24 * 30;
this.expires = new Timestamp(new Date().getTime() + expTime);
}
}
Abstract Facade
package ejb;
import java.util.List;
import javax.persistence.EntityManager;
public abstract class AbstractFacade<T> {
private Class<T> entityClass;
public AbstractFacade(Class<T> entityClass) {
this.entityClass = entityClass;
}
protected abstract EntityManager getEntityManager();
public void create(T entity) {
getEntityManager().persist(entity);
getEntityManager().flush();
}
public void edit(T entity) {
getEntityManager().merge(entity);
}
public void remove(T entity) {
getEntityManager().remove(getEntityManager().merge(entity));
}
public T find(Object id) {
return getEntityManager().find(entityClass, id);
}
public List<T> findAll() {
javax.persistence.criteria.CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery();
cq.select(cq.from(entityClass));
return getEntityManager().createQuery(cq).getResultList();
}
public List<T> findRange(int[] range) {
javax.persistence.criteria.CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery();
cq.select(cq.from(entityClass));
javax.persistence.Query q = getEntityManager().createQuery(cq);
q.setMaxResults(range[1] - range[0] + 1);
q.setFirstResult(range[0]);
return q.getResultList();
}
public int count() {
javax.persistence.criteria.CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery();
javax.persistence.criteria.Root<T> rt = cq.from(entityClass);
cq.select(getEntityManager().getCriteriaBuilder().count(rt));
javax.persistence.Query q = getEntityManager().createQuery(cq);
return ((Long) q.getSingleResult()).intValue();
}
}
Chunk of code to create token
Users user = ufl.getUserByUsername(username);
//test the user isn't null => outputs expected result of username
System.out.println("===WHO? " + user.getUsername() + "===");
Tokens tkn = new Tokens();
tkn.setJWT(token);
tkn.setUsers(user);
tfl.create(tkn);
Error
Caused by: org.postgresql.util.PSQLException: ERROR: null value in column "userid" violates not-null constraint
Detail: Failing row contains (5aa5fefa-4744-4acf-a640-338ea9b35758, 2015-08-04 20:28:08.578, null, 2015-08-04 21:11:20.578, eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiJiNmY0N2ViMS1iMDdmLTQyNTctYWI0OS0..., null).
at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2157)
at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:1886)
at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:255)
at org.postgresql.jdbc2.AbstractJdbc2Statement.execute(AbstractJdbc2Statement.java:559)
at org.postgresql.jdbc2.AbstractJdbc2Statement.executeWithFlags(AbstractJdbc2Statement.java:417)
at org.postgresql.jdbc2.AbstractJdbc2Statement.executeUpdate(AbstractJdbc2Statement.java:363)
at org.jboss.jca.adapters.jdbc.WrappedPreparedStatement.executeUpdate(WrappedPreparedStatement.java:493)
at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:208) [hibernate-core-4.3.7.Final.jar:4.3.7.Final]
... 98 more
Conclusion
It is trying to persist the entity but is inserting a null value for the userid even though in the chunk of code I have setUser to user. The user is being found by getting the user by username. Not sure what is causing the issue.

Issue with StoreController servlet in Spring MVC

I'm working through a tutorial on Spring MVC and I am getting an error in my StoreController that I cant seem to sort. The three code snippets I have added are StoreController.java (servlet), Album.java and Genre.java which are Entity classes. I am getting an error # line 76 of StoreController as follows: The method getName() is undefined for type Integer. I will add the code just below and I hope someone can help me understand my problem:
Thanks and regards
Gus
Code: storeController.java
package com.MVCMusicStore.Controllers;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Resource;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import com.MVCMusicStore.Entities.Album;
import com.MVCMusicStore.Entities.Genre;
import com.MVCMusicStore.Models.AlbumModel;
import com.MVCMusicStore.Models.GenreModel;
import com.MVCMusicStore.Models.ArtistModel;
import com.MVCMusicStore.Entities.Artist;
#Controller
#RequestMapping("/Store")
public class StoreController {
#Resource(name = "genreService")
private GenreModel genreModel;
#Resource(name = "albumService")
private AlbumModel albumModel;
/**
* Map Root of Store Page
* #param model
* #return
*/
#RequestMapping(value = "/", method = RequestMethod.GET)
public String getStoreIndexPage(ModelMap model) {
// This is for the side menu
List<Genre> myGenreList = genreModel.findAllGenres();
model.put("genreList" , myGenreList);
model.put("genreCount", myGenreList.size());
// This is for the albums
//List<Genre> myGenreList = genreModel.findAllGenres();
//model.put("allAlbums", allAlbums);
return "storeindex";
}
/**
* Map the Browse page
*
* #param genreName
* #param model
* #return
*/
#RequestMapping(value = "/Browse", method = RequestMethod.GET)
public String getStoreBrowsePage(
#RequestParam(value = "genre", required = false) String genreName,
ModelMap model) {
// It's for albums
List<Album> foundAlbum = new ArrayList<Album>();
if (genreName == null) {
model.put("genre", "Empty");
} else {
List<Album> myAlbum = albumModel.findAllAlbums();
for (Album tempAlbum : myAlbum) {
if (tempAlbum.getGenreid().getName().equals(genreName))
{
foundAlbum.add(tempAlbum);
}
}
model.put("genre", genreName);
model.put("foundAlbum", foundAlbum);
}
// "genre" - should have the same name as value="genre"
//model.addAttribute("genre", genre);
return "Browse";
}
/**
* Map the Browse Page
* #param genre
* #param model
* #return
*/
//#RequestMapping(value = "/Browse", method = RequestMethod.GET)
//public String getStoreBrowsePage(#RequestParam(value="genre", required=false)
//String genre, ModelMap model) {
//model.addAttribute("genre",genre);
//return "Browse";
//}
}
Album.java
package com.MVCMusicStore.Entities;
/**
* #author Gus
*
*/
import java.io.Serializable;
import java.math.BigDecimal;
import java.math.BigInteger;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
import javax.xml.bind.annotation.XmlRootElement;
/**
*
* #author Gus
*/
#Entity
#Table(name = "ALBUM")
#XmlRootElement
#NamedQueries({
#NamedQuery(name = "Album.findAll", query = "SELECT a FROM Album a"),
#NamedQuery(name = "Album.findByAlbumid", query = "SELECT a FROM Album a WHERE a.albumid = :albumid"),
#NamedQuery(name = "Album.findByGenreid", query = "SELECT a FROM Album a WHERE a.genreid = :genreid"),
#NamedQuery(name = "Album.findByArtistid", query = "SELECT a FROM Album a WHERE a.artistid = :artistid"),
#NamedQuery(name = "Album.findByTitle", query = "SELECT a FROM Album a WHERE a.title = :title"),
#NamedQuery(name = "Album.findByPrice", query = "SELECT a FROM Album a WHERE a.price = :price"),
#NamedQuery(name = "Album.findByAlbumarturl", query = "SELECT a FROM Album a WHERE a.albumarturl = :albumarturl")})
public class Album implements Serializable {
private static final long serialVersionUID = 1L;
// #Max(value=?) #Min(value=?)//if you know range of your decimal fields consider using these annotations to enforce field validation
#Id
#Basic(optional = false)
#Column(name = "ALBUMID")
private Integer albumid;
#Column(name = "GENREID")
private Integer genreid;
#Column(name = "ARTISTID")
private Integer artistid;
#Column(name = "TITLE")
private String title;
#Column(name = "PRICE")
private Integer price;
#Column(name = "ALBUMARTURL")
private String albumarturl;
public Album() {
}
public Album(Integer albumid) {
this.albumid = albumid;
}
public Integer getAlbumid() {
return albumid;
}
public void setAlbumid(Integer albumid) {
this.albumid = albumid;
}
public Integer getGenreid() {
return genreid;
}
public void setGenreid(Integer genreid) {
this.genreid = genreid;
}
public Integer getArtistid() {
return artistid;
}
public void setArtistid(Integer artistid) {
this.artistid = artistid;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public Integer getPrice() {
return price;
}
public void setPrice(Integer price) {
this.price = price;
}
public String getAlbumarturl() {
return albumarturl;
}
public void setAlbumarturl(String albumarturl) {
this.albumarturl = albumarturl;
}
#Override
public int hashCode() {
int hash = 0;
hash += (albumid != null ? albumid.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 Album)) {
return false;
}
Album other = (Album) object;
if ((this.albumid == null && other.albumid != null) || (this.albumid != null && !this.albumid.equals(other.albumid))) {
return false;
}
return true;
}
#Override
public String toString() {
return "com.MVCMusicStore.Entities.Album[ albumid=" + albumid + " ]";
}
}
Genre.java
package com.MVCMusicStore.Entities;
import java.io.Serializable;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
import javax.xml.bind.annotation.XmlRootElement;
import com.MVCMusicStore.Entities.Artist;
import com.MVCMusicStore.Entities.Album;
/**
*
* #author Gus
*/
#Entity
#Table(name = "GENRE")
#XmlRootElement
#NamedQueries({
#NamedQuery(name = "Genre.findAll", query = "SELECT g FROM Genre g"),
#NamedQuery(name = "Genre.findByGenreid", query = "SELECT g FROM Genre g WHERE g.genreid = :genreid"),
#NamedQuery(name = "Genre.findByName", query = "SELECT g FROM Genre g WHERE g.name = :name"),
#NamedQuery(name = "Genre.findByDescription", query = "SELECT g FROM Genre g WHERE g.description = :description")})
public class Genre implements Serializable {
private static final long serialVersionUID = 1L;
// #Max(value=?) #Min(value=?)//if you know range of your decimal fields consider using these annotations to enforce field validation
#Id
#Basic(optional = false)
#Column(name = "GENREID")
private Integer genreid;
#Column(name = "NAME")
private String name;
#Column(name = "DESCRIPTION")
private String description;
public Genre() {
}
public Genre(Integer genreid) {
this.genreid = genreid;
}
public Integer getGenreid() {
return genreid;
}
public void setGenreid(Integer genreid) {
this.genreid = genreid;
}
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;
}
#Override
public int hashCode() {
int hash = 0;
hash += (genreid != null ? genreid.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 Genre)) {
return false;
}
Genre other = (Genre) object;
if ((this.genreid == null && other.genreid != null) || (this.genreid != null && !this.genreid.equals(other.genreid))) {
return false;
}
return true;
}
#Override
public String toString() {
return "com.MVCMusicStore.Entities.Genre[ genreid=" + genreid + " ]";
}
}
The error is telling you exactly what's wrong here. When you run this:
tempAlbum.getGenreid().getName().equals(genreName)
The method getGenreid() is returning an Integer, you are then attempting to get the name of the integer when what you actually want to do is run the getName() method on an a genre object.
Album Entity
#Column(name = "GENREID")
private Integer genreid;
StoreController
for (Album tempAlbum : myAlbum) {
if (tempAlbum.getGenreid().getName().equals(genreName)) <- does it even compile ? Don't think so
If you want to make it works you need to change Album entity:
package com.MVCMusicStore.Entities;
/**
* #author Gus
*
*/
import java.io.Serializable;
import java.math.BigDecimal;
import java.math.BigInteger;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
import javax.xml.bind.annotation.XmlRootElement;
/**
*
* #author Gus
*/
#Entity
#Table(name = "ALBUM")
#XmlRootElement
#NamedQueries({
#NamedQuery(name = "Album.findAll", query = "SELECT a FROM Album a"),
#NamedQuery(name = "Album.findByAlbumid", query = "SELECT a FROM Album a WHERE a.albumid = :albumid"),
//#NamedQuery(name = "Album.findByGenreid", query = "SELECT a FROM Album a WHERE a.genreid = :genreid"),
#NamedQuery(name = "Album.findByArtistid", query = "SELECT a FROM Album a WHERE a.artistid = :artistid"),
#NamedQuery(name = "Album.findByTitle", query = "SELECT a FROM Album a WHERE a.title = :title"),
#NamedQuery(name = "Album.findByPrice", query = "SELECT a FROM Album a WHERE a.price = :price"),
#NamedQuery(name = "Album.findByAlbumarturl", query = "SELECT a FROM Album a WHERE a.albumarturl = :albumarturl")})
public class Album implements Serializable {
private static final long serialVersionUID = 1L;
// #Max(value=?) #Min(value=?)//if you know range of your decimal fields consider using these annotations to enforce field validation
#Id
#Basic(optional = false)
#Column(name = "ALBUMID")
private Integer albumid;
//#Column(name = "GENREID")
//private Integer genreid;
#OneToOne(cascade=CascadeType.ALL,fetch = FetchType.EAGER)
#JoinColumn(name="genreId")
private Genre genre;//Added new field with mapping to Genre
#Column(name = "ARTISTID")
private Integer artistid;
#Column(name = "TITLE")
private String title;
#Column(name = "PRICE")
private Integer price;
#Column(name = "ALBUMARTURL")
private String albumarturl;
public Album() {
}
public Album(Integer albumid) {
this.albumid = albumid;
}
public Integer getAlbumid() {
return albumid;
}
public void setAlbumid(Integer albumid) {
this.albumid = albumid;
}
//public Integer getGenreid() {
// return genreid;
//}
//public void setGenreid(Integer genreid) {
// this.genreid = genreid;
//}
public Integer getArtistid() {
return artistid;
}
public void setArtistid(Integer artistid) {
this.artistid = artistid;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public Integer getPrice() {
return price;
}
public void setPrice(Integer price) {
this.price = price;
}
public String getAlbumarturl() {
return albumarturl;
}
public void setAlbumarturl(String albumarturl) {
this.albumarturl = albumarturl;
}
public Genre getGenre() {
return genre;
}
public void setGenre(Genre genre) {
this.genre = genre;
}
#Override
public int hashCode() {
int hash = 0;
hash += (albumid != null ? albumid.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 Album)) {
return false;
}
Album other = (Album) object;
if ((this.albumid == null && other.albumid != null) || (this.albumid != null && !this.albumid.equals(other.albumid))) {
return false;
}
return true;
}
#Override
public String toString() {
return "com.MVCMusicStore.Entities.Album[ albumid=" + albumid + " ]";
}
}
And then in StoreController change these lines:
for (Album tempAlbum : myAlbum) {
if (tempAlbum.getGenre().getName().equals(genreName)) <- See the changes ?
{
foundAlbum.add(tempAlbum);
}
}
Your Album entity need to have "grip" to Genre entity. If you want to do this you need to make #OneToOne between these two entities.
One more thing. When this changes are up you dont need genreId in your Album entity.

JPA remove constraints at run time

I'm deleting entities from a table with a one to many relationship to the same entity (representing node hierarchy). If I make the xincoCoreNodeId relationship a cascade all it works but I don't want that in the real application.
I don't want removing a leaf removing its parent. Is there a way to modify this relationship at run time or disable the constraints so I can delete the whole table contents without getting constraint complains?
package com.bluecubs.xinco.core.server.persistence;
import com.bluecubs.xinco.core.server.AuditedEntityListener;
import com.bluecubs.xinco.core.server.XincoAuditedObject;
import java.io.Serializable;
import java.util.List;
import javax.persistence.Basic;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EntityListeners;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import org.eclipse.persistence.annotations.PrivateOwned;
/**
*
* #author Javier A. Ortiz Bultrón <javier.ortiz.78#gmail.com>
*/
#Entity
#Table(name = "xinco_core_node")
#EntityListeners(AuditedEntityListener.class)
#NamedQueries({
#NamedQuery(name = "XincoCoreNode.findAll", query = "SELECT x FROM XincoCoreNode x"),
#NamedQuery(name = "XincoCoreNode.findById", query = "SELECT x FROM XincoCoreNode x WHERE x.id = :id"),
#NamedQuery(name = "XincoCoreNode.findByDesignation", query = "SELECT x FROM XincoCoreNode x WHERE x.designation = :designation"),
#NamedQuery(name = "XincoCoreNode.findByStatusNumber", query = "SELECT x FROM XincoCoreNode x WHERE x.statusNumber = :statusNumber")})
public class XincoCoreNode extends XincoAuditedObject implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Basic(optional = false)
#Column(name = "id", nullable = false)
private Integer id;
#Basic(optional = false)
#Column(name = "designation", nullable = false, length = 255)
private String designation;
#Basic(optional = false)
#Column(name = "status_number", nullable = false)
private int statusNumber;
#JoinColumn(name = "xinco_core_language_id", referencedColumnName = "id", nullable = false)
#ManyToOne(optional = false, fetch = FetchType.LAZY)
private XincoCoreLanguage xincoCoreLanguageId;
#OneToMany(cascade = CascadeType.PERSIST, mappedBy = "xincoCoreNodeId", fetch = FetchType.LAZY)
private List<XincoCoreNode> xincoCoreNodeList;
#JoinColumn(name = "xinco_core_node_id", referencedColumnName = "id")
#PrivateOwned
#ManyToOne(fetch = FetchType.LAZY)
private XincoCoreNode xincoCoreNodeId;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "xincoCoreNodeId", fetch = FetchType.LAZY)
private List<XincoCoreAce> xincoCoreAceList;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "xincoCoreNodeId", fetch = FetchType.LAZY)
private List<XincoCoreData> xincoCoreDataList;
public XincoCoreNode() {
}
public XincoCoreNode(Integer id) {
this.id = id;
}
public XincoCoreNode(Integer id, String designation, int statusNumber) {
this.id = id;
this.designation = designation;
this.statusNumber = statusNumber;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getDesignation() {
return designation;
}
public void setDesignation(String designation) {
this.designation = designation;
}
public int getStatusNumber() {
return statusNumber;
}
public void setStatusNumber(int statusNumber) {
this.statusNumber = statusNumber;
}
public XincoCoreLanguage getXincoCoreLanguageId() {
return xincoCoreLanguageId;
}
public void setXincoCoreLanguageId(XincoCoreLanguage xincoCoreLanguageId) {
this.xincoCoreLanguageId = xincoCoreLanguageId;
}
public List<XincoCoreNode> getXincoCoreNodeList() {
return xincoCoreNodeList;
}
public void setXincoCoreNodeList(List<XincoCoreNode> xincoCoreNodeList) {
this.xincoCoreNodeList = xincoCoreNodeList;
}
public XincoCoreNode getXincoCoreNodeId() {
return xincoCoreNodeId;
}
public void setXincoCoreNodeId(XincoCoreNode xincoCoreNodeId) {
this.xincoCoreNodeId = xincoCoreNodeId;
}
public List<XincoCoreAce> getXincoCoreAceList() {
return xincoCoreAceList;
}
public void setXincoCoreAceList(List<XincoCoreAce> xincoCoreAceList) {
this.xincoCoreAceList = xincoCoreAceList;
}
public List<XincoCoreData> getXincoCoreDataList() {
return xincoCoreDataList;
}
public void setXincoCoreDataList(List<XincoCoreData> xincoCoreDataList) {
this.xincoCoreDataList = xincoCoreDataList;
}
#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 XincoCoreNode)) {
return false;
}
XincoCoreNode other = (XincoCoreNode) 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 "com.bluecubs.xinco.core.server.persistence.XincoCoreNode[id=" + id + "]";
}
}
If you are using Hibernate, you can use its support for batch processing to delete many rows in a table efficiently. Basically, the query gets translated from HQL to SQL and runs against the database directly. There are limitations with this approach, though. For example, in-memory state won't be updated if you are holding references to any of the affected objects.
I don't know if JPA itself supports batch processing. Probably not. But if you expect to be affecting a lot of rows (hundreds or thousands), then this is one of those cases where ORM is not really the right tool for the job, anyway. It might make more sense to do it in straight SQL or perhaps a stored procedure. Either way, you can isolate the code inside a Data Access Object so that high-level business logic need not care how the operation is implemented.
I ended doing an special case in Java to handle this.
#Override
public void clearTable() {
try {
/**
* All nodes are linked except the root. So we need to start deleting the leaves first.
*/
while (new XincoCoreNodeJpaController().findXincoCoreNodeEntities().size() > 0) {
for (com.bluecubs.xinco.core.server.persistence.XincoCoreNode xcn : getLeaves()) {
new XincoCoreNodeJpaController().destroy(xcn.getId());
}
}
} catch (XincoException ex) {
Logger.getLogger(XincoCoreNodeServer.class.getName()).log(Level.SEVERE, null, ex);
}
}
private Vector<com.bluecubs.xinco.core.server.persistence.XincoCoreNode> getLeaves() throws XincoException {
Vector<com.bluecubs.xinco.core.server.persistence.XincoCoreNode> leaves =
new Vector<com.bluecubs.xinco.core.server.persistence.XincoCoreNode>();
result = XincoDBManager.protectedCreatedQuery("select x from XincoCoreNode x " +
"where x.id not in (select y.xincoCoreNodeId.id from XincoCoreNode y " +
"where y.xincoCoreNodeId is not null)",null,true);
if (result.size() == 0) {
//Check if the root is there
for (Object o : new XincoCoreNodeJpaController().findXincoCoreNodeEntities()) {
leaves.add((com.bluecubs.xinco.core.server.persistence.XincoCoreNode) o);
}
}
for (Object o : result) {
leaves.add((com.bluecubs.xinco.core.server.persistence.XincoCoreNode) o);
}
return leaves;
}

Categories