JPA query ManyToOne Exception - jpa

I have the following entity class (excerpt):
#NamedQueries({#NamedQuery(name = "Orphans.findAll", query = "SELECT c FROM ReferencePeak c WHERE c.libraryPeak IS EMPTY")})
#Access(AccessType.PROPERTY)
#Entity(name = "ReferencePeak")
public class ReferencePeakEntity extends PeakEntity {
private final ObjectProperty<LibraryPeakEntity> libraryPeak = new SimpleObjectProperty<>();
private final ObjectProperty<SampleEntity> sample = new SimpleObjectProperty<>();
public ReferencePeakEntity() {
}
#ManyToOne(targetEntity = LibraryPeakEntity.class)
public final LibraryPeakEntity getLibraryPeak() {
return this.libraryPeakProperty().get();
}
#ManyToOne(cascade = { CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH })
public final SampleEntity getSample() {
return this.sampleProperty().get();
}
public final ObjectProperty<LibraryPeakEntity> libraryPeakProperty() {
return this.libraryPeak;
}
public final void setLibraryPeak(final LibraryPeakEntity libraryPeak) {
this.libraryPeakProperty().set(libraryPeak);
}
public final ObjectProperty<SubstanceEntity> substanceProperty() {
return this.substance;
}
#ManyToOne(cascade = { CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH })
public final SubstanceEntity getSubstance() {
return this.substanceProperty().get();
}
public final void setSubstance(final SubstanceEntity substance) {
this.substanceProperty().set(substance);
}
}
The named query "Orphans.findAll" throws the following Exception:
"The collection-valued path 'c.libraryPeak' must resolve to an association field."
I would like to query for all entities, that do not have any 'libraryPeak' set, i.e, where getLibraryPeak() will return null.

Related

Spring Boot JPA Bulk insert

I have 3 Entities Parent,Child,SubChild. Parent is a parent of Child and Child is a parent of SubChild. I need to insert around 700 objects of Parent. Parent can have 50 Objects of Child. Child can have 50 objects of SubChild.
I tried normal repository.save(ListOfObjects) it takes approx 4mins.
Then I tried using entity manager's persist, flush and clear based on batch size(500). This also took approx 4 mins.
There wasn't much difference in performance. Please suggest a best way to insert such a high amount of data efficiently.
Parent
#Entity
public class Parent {
#Id #GeneratedValue(strategy= GenerationType.AUTO)
private Long parentId;
private String aaa;
private String bbb;
private String ccc;
#Version
private Long version;
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "parent", fetch = FetchType.LAZY)
#JoinColumnsOrFormulas({
#JoinColumnOrFormula(column=#JoinColumn(name="parentId",referencedColumnName="parentId",nullable=false))})
private List<Child> childs = new ArrayList<>();
public Long getParentId() {
return parentId;
}
public void setParentId(Long parentId) {
this.parentId = parentId;
}
public String getAaa() {
return aaa;
}
public void setAaa(String aaa) {
this.aaa = aaa;
}
public String getBbb() {
return bbb;
}
public void setBbb(String bbb) {
this.bbb = bbb;
}
public String getCcc() {
return ccc;
}
public void setCcc(String ccc) {
this.ccc = ccc;
}
public Long getVersion() {
return version;
}
public void setVersion(Long version) {
this.version = version;
}
public List<Child> getChilds() {
return childs;
}
public void setChilds(List<Child> childs) {
this.childs = childs;
}
}
Child
#Entity
public class Child {
#Id #GeneratedValue(strategy= GenerationType.AUTO)
private Long childId;
private String ddd;
private String ccc;
private Integer eee;
#OneToMany(cascade = CascadeType.ALL,orphanRemoval = true, mappedBy = "child", fetch = FetchType.LAZY)
#JoinColumnsOrFormulas({
#JoinColumnOrFormula(column = #JoinColumn(name = "childId", referencedColumnName = "childId", nullable = false)) })
private List<SubChild> subChilds = new ArrayList<>();
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumnsOrFormulas({
#JoinColumnOrFormula(column= #JoinColumn( name="parentId",referencedColumnName="parentId",nullable=false))
})
private Parent parent;
public Long getChildId() {
return childId;
}
public void setChildId(Long childId) {
this.childId = childId;
}
public String getDdd() {
return ddd;
}
public void setDdd(String ddd) {
this.ddd = ddd;
}
public String getCcc() {
return ccc;
}
public void setCcc(String ccc) {
this.ccc = ccc;
}
public Integer getEee() {
return eee;
}
public void setEee(Integer eee) {
this.eee = eee;
}
public List<SubChild> getSubChilds() {
return subChilds;
}
public void setSubChilds(List<SubChild> subChilds) {
this.subChilds = subChilds;
}
public Parent getParent() {
return parent;
}
public void setParent(Parent parent) {
this.parent = parent;
}
}
SubChild
#Entity
public class SubChild {
#Id #GeneratedValue(strategy= GenerationType.AUTO)
private Long subChildId;
private String fff;
private String ggg;
private Integer hhh;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumnsOrFormulas({
#JoinColumnOrFormula(column= #JoinColumn( name="childId",referencedColumnName="childId",nullable=false))
})
private Child child;
public Long getSubChildId() {
return subChildId;
}
public void setSubChildId(Long subChildId) {
this.subChildId = subChildId;
}
public String getFff() {
return fff;
}
public void setFff(String fff) {
this.fff = fff;
}
public String getGgg() {
return ggg;
}
public void setGgg(String ggg) {
this.ggg = ggg;
}
public Integer getHhh() {
return hhh;
}
public void setHhh(Integer hhh) {
this.hhh = hhh;
}
public Child getChild() {
return child;
}
public void setChild(Child child) {
this.child = child;
}
}
Repository method used for persisting the list of Parent Entity
#Value("${spring.jpa.hibernate.jdbc.batch_size}")
private int batchSize;
public <T extends Parent> Collection<T> bulkSave(Collection<T> entities) {
final List<T> savedEntities = new ArrayList<T>(entities.size());
int i = 0;
for (T t : entities) {
savedEntities.add(persistOrMerge(t));
i++;
if (i % batchSize == 0) {
// Flush a batch of inserts and release memory.
entityManager.flush();
entityManager.clear();
}
}
return savedEntities;
}
private <T extends Parent> T persistOrMerge(T t) {
if (t.getTimeSlotId() == null) {
entityManager.persist(t);
return t;
} else {
return entityManager.merge(t);
}
}
application.yml
spring:
application:
name: sample-service
jpa:
database: MYSQL
show-sql: true
hibernate:
ddl-auto: update
dialect: org.hibernate.dialect.MySQL5Dialect
naming_strategy: org.hibernate.cfg.ImprovedNamingStrategy
jdbc:
batch_size: 100
jackson:
date-format: dd/MM/yyyy
thymeleaf:
cache: false
spring.datasource.url : jdbc:mysql://${dbhost}/sample?createDatabaseIfNotExist=true
spring.datasource.username : root
spring.datasource.password : root
spring.datasource.driver-class-name : com.mysql.cj.jdbc.Driver
To enable batch insert you need the batch_size property which you have in your configuration.
Also since a jdbc batch can target one table only you need the spring.jpa.hibernate.order_inserts=true property to order the insert between parent and child or else the statement are unordered and you will see a partial batch (new batch anytime an insert in a different table is called)

update of an object containing a polymorphic set in MongoDB using SpringBoot

I have a "Fonction" class persisted in my Mongo data base which contain a set of objects extending "ItemAccesFonction" class :
#Document
public class Fonction {
#Id
private String _id;
#Indexed
private String name;
private Set<ItemAccesFonction> access;
public Fonction() {
}
public Fonction(String name, Set<ItemAccesFonction> acces) {
this.name = name;
this.access = acces;
this._id = name;
}
public Fonction(String name) {
this.name = name;
this.access = new HashSet<>();
this._id = name;
}
public String getId() {
return _id;
}
public void setId(String id) {
this._id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set<ItemAccesFonction> getAccess() {
return access;
}
public void setAccess(Set<ItemAccesFonction> access) {
this.access = access;
}
}
Here is my "ItemAccesFonction" class and those which extend it :
#JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "_class")
public class ItemAccesFonction {
private String _id;
private String _class;
public ItemAccesFonction() {
this._class = getClass().getCanonicalName();
}
public ItemAccesFonction(final String id) {
this._id = id;
this._class = getClass().getCanonicalName().toString();
}
public String getId() {
return _id;
}
public void setId(final String idItemAcces) {
this._id = idItemAcces;
}
public String get_class() {
return _class;
}
public void set_class(final String _class) {
this._class = _class;
}
}
public class ApplicationFonction extends ItemAccesFonction{
private List<String> listeGroupes;
public ApplicationFonction() {
super();
this.listeGroupes = new ArrayList<String>();
}
public ApplicationFonction(String id, List<String> listeGroupes) {
super(id);
this.listeGroupes = listeGroupes;
}
public List<String> getListeGroupes() {
return listeGroupes;
}
public void setListeGroupes(final List<String> listeGroupes) {
this.listeGroupes = listeGroupes;
}
}
public class RepertoireFonction extends ItemAccesFonction{
private String droitSelection;
public RepertoireFonction() {
super();
}
public RepertoireFonction(String id ,String droitSelection) {
super(id);
this.droitSelection = droitSelection;
}
public String getDroitSelection() {
return droitSelection;
}
public void setDroitSelection(final String droitSelection) {
this.droitSelection = droitSelection;
}
}
I encounter an issue when I try to update a Fonction. I get the following error, but not everytime :
{
"cause": {
"cause": {
"cause": null,
"message": "Target bean of type com.wps.gp.datamongo.model.acces.ItemAccesFonction is not of type of the persistent entity (com.wps.gp.datamongo.model.acces.RepertoireFonction)!"
},
"message": "Could not read payload!; nested exception is java.lang.IllegalArgumentException: Target bean of type com.wps.gp.datamongo.model.acces.ItemAccesFonction is not of type of the persistent entity (com.wps.gp.datamongo.model.acces.RepertoireFonction)!"
},
"message": "Could not read an object of type class com.wps.gp.datamongo.model.Fonction from the request!; nested exception is org.springframework.http.converter.HttpMessageNotReadableException: Could not read payload!; nested exception is java.lang.IllegalArgumentException: Target bean of type com.wps.gp.datamongo.model.acces.ItemAccesFonction is not of type of the persistent entity (com.wps.gp.datamongo.model.acces.RepertoireFonction)!"
}
Does someone have an idea of what I am doing wrong?
Thanks

Delete a record using Spring data jpa

I have an entity 'Competence', this entity has OneToMany relation with two other entities : CandidatCompetence and OffreCompetence, and a ManyToOne relation with GroupCompetence.
And I have a rest delete service with will take the id of a Competence entity as following :
#Secured("ROLE_ADMIN")
#RequestMapping(value="/competences/{id}",method= RequestMethod.DELETE)
public void deleteCompetence(#PathVariable Long id) {
competenceMetier.deleteCompetence(id);
}
Then the deleteCompetence function will call a delete function from the Competence Repository which extends JpaRepository<Competence, Long> as following :
public void deleteCompetence(Long id) {
competenceRepository.delete(id);
}
The problem is that when I call the rest delete method, I get 200 as an http response, but nothing in the body, the same for the log I can't see the DELETE sql query anywhere, and the entity still exists in the database.
here are my entities :
Competence :
#Entity
public class Competence implements Serializable {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Long codeCompetence;
private String titre;
private Boolean activated = true;
#OneToMany(mappedBy="competence",cascade = CascadeType.ALL)
private Collection<CandidatCompetence> candidatCompetences;
#OneToMany(mappedBy="competence",cascade = CascadeType.ALL)
private Collection<OffreCompetence> offreCompetences;
#ManyToOne
#JoinColumn(name = "groupCompetence")
private GroupCompetence groupCompetence;
public Long getCodeCompetence() {
return codeCompetence;
}
public void setCodeCompetence(Long codeCompetence) {
this.codeCompetence = codeCompetence;
}
public String getTitre() {
return titre;
}
public void setTitre(String titre) {
this.titre = titre;
}
#JsonIgnore
#XmlTransient
public Collection<CandidatCompetence> getCandidatCompetences() {
return candidatCompetences;
}
#JsonSetter
public void setCandidatCompetences(Collection<CandidatCompetence> candidatCompetences) {
this.candidatCompetences = candidatCompetences;
}
#JsonIgnore
#XmlTransient
public Collection<OffreCompetence> getOffreCompetences() {
return offreCompetences;
}
public void setOffreCompetences(Collection<OffreCompetence> offreCompetences) {
this.offreCompetences = offreCompetences;
}
public Competence(String titre) {
super();
this.titre = titre;
}
public Competence() {
super();
// TODO Auto-generated constructor stub
}
#JsonIgnore
#XmlTransient
public GroupCompetence getGroupCompetence() {
return groupCompetence;
}
#JsonSetter
public void setGroupCompetence(GroupCompetence groupCompetence) {
this.groupCompetence = groupCompetence;
}
public Boolean getActivated() {
return activated;
}
public void setActivated(Boolean activated) {
this.activated = activated;
}
}
OffreCompetence :
#Entity
public class OffreCompetence implements Serializable {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Long codeOffreCompetence;
private String niveauRequis;
#ManyToOne
#JoinColumn(name = "competence")
private Competence competence;
#ManyToOne
#JoinColumn(name="offre")
private Offre offre;
public Long getCodeOffreCompetence() {
return codeOffreCompetence;
}
public void setCodeOffreCompetence(Long codeOffreCompetence) {
this.codeOffreCompetence = codeOffreCompetence;
}
public String getNiveauRequis() {
return niveauRequis;
}
public void setNiveauRequis(String niveauRequis) {
this.niveauRequis = niveauRequis;
}
public Competence getCompetence() {
return competence;
}
public void setCompetence(Competence competence) {
this.competence = competence;
}
#JsonIgnore
public Offre getOffre() {
return offre;
}
#JsonSetter
public void setOffre(Offre offre) {
this.offre = offre;
}
public OffreCompetence(String niveauRequis) {
super();
this.niveauRequis = niveauRequis;
}
public OffreCompetence() {
super();
// TODO Auto-generated constructor stub
}
}
CandidatCompetence :
#Entity
public class CandidatCompetence implements Serializable {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Long codeCandidatCompetence;
private String niveauExperience;
#ManyToOne
#JoinColumn(name = "candidat")
private Candidat candidat;
#ManyToOne
#JoinColumn(name = "competence")
private Competence competence;
public Long getCodeCandidatCompetence() {
return codeCandidatCompetence;
}
public void setCodeCandidatCompetence(Long codeCandidatCompetence) {
this.codeCandidatCompetence = codeCandidatCompetence;
}
public String getNiveauExperience() {
return niveauExperience;
}
public void setNiveauExperience(String niveauExperience) {
this.niveauExperience = niveauExperience;
}
public Candidat getCandidat() {
return candidat;
}
public void setCandidat(Candidat candidat) {
this.candidat = candidat;
}
public Competence getCompetence() {
return competence;
}
public void setCompetence(Competence competence) {
this.competence = competence;
}
public CandidatCompetence(String niveauExperience) {
super();
this.niveauExperience = niveauExperience;
}
public CandidatCompetence() {
super();
// TODO Auto-generated constructor stub
}
}
GroupCompetence :
#Entity
public class GroupCompetence implements Serializable {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
private Long codeGroupCompetence;
private String titre;
private Boolean activated = true;
#OneToMany(mappedBy="groupCompetence",cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private Collection<Competence> competences;
public Long getCodeGroupCompetence() {
return codeGroupCompetence;
}
public void setCodeGroupCompetence(Long codeGroupCompetence) {
this.codeGroupCompetence = codeGroupCompetence;
}
public String getTitre() {
return titre;
}
public void setTitre(String titre) {
this.titre = titre;
}
public GroupCompetence(String titre) {
this.titre = titre;
}
public GroupCompetence() {
}
public Boolean getActivated() {
return activated;
}
public void setActivated(Boolean activated) {
this.activated = activated;
}
public Collection<Competence> getCompetences() {
return competences;
}
public void setCompetences(Collection<Competence> competences) {
this.competences = competences;
}
}
You should annotate your Service Method deleteCompetence with #Transactional.

JavaFX properties fail to persist

I'm using some JavaFX properties in my app:
#Entity(name = "Klanten")
#Table(name = "Klanten")
#NamedQueries({
#NamedQuery(name = "Klanten.findAll", query = "select k from Klanten k")
})
public class Klant implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int klantId;
#Transient
private final SimpleStringProperty naam = new SimpleStringProperty();
//private String naam;
//private String straat;
#Transient
private final SimpleStringProperty straat = new SimpleStringProperty();
private String telefoon;
private String huisnummer;
private String gsm;
private String woonplaats;
private String email;
private String postcode;
#OneToMany(mappedBy = "Klant", cascade = CascadeType.REMOVE)
private List<Raam> ramen;
public Klant() {
}
public Klant(String naam) {
this.naam.set(naam);
}
#Override
public String toString() {
return this.naam.get();
}
#Access(AccessType.PROPERTY)
#Column(name="naam")
public String getNaam() {
return this.naam.get();
}
public void setNaam(String naam){
this.naam.set(naam);
}
public List<Raam> getRamen() {
return this.ramen;
}
#Id
public int getKlantId() {
return klantId;
}
public void setKlantId(int klantId) {
this.klantId = klantId;
}
#Access(AccessType.PROPERTY)
#Column(name="straat")
public String getStraat() {
return straat.get();
}
public void setStraat(String straat) {
this.straat.set(straat);
}
public String getTelefoon() {
return telefoon;
}
public void setTelefoon(String telefoon) {
this.telefoon = telefoon;
}
public String getHuisnummer() {
return huisnummer;
}
public void setHuisnummer(String huisnummer) {
this.huisnummer = huisnummer;
}
public String getGsm() {
return gsm;
}
public void setGsm(String gsm) {
this.gsm = gsm;
}
public String getWoonplaats() {
return woonplaats;
}
public void setWoonplaats(String woonplaats) {
this.woonplaats = woonplaats;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPostcode() {
return postcode;
}
public void setPostcode(String postcode) {
this.postcode = postcode;
}
public StringProperty naamProperty() {
return naam;
}
public StringProperty straatProperty() {
return straat;
}
}
However when I let JPA generate my database, the column "naam" and "straat" aren't generated. I get no error. How can I resolve this?
I tried all the things listed here:
Possible solution 1
Possible solution 2
These didn't work.
You can try to use regular properties and then have another get method which returns a new SimpleStringProperty, i.e.:
public StringProperty naamProperty() {
return new SimpleStringProperty(naam);
}
public StringProperty straatProperty() {
return new SimpleStringProperty(straat);
}

Persist a graph with JPA

I try to persist a graph with JPA, but it doesn't work. I have Leaf(s) which are Node(s), which can have multiple Parent(s) and a Parent can have multiple children (Node).
#Entity
#Table(name = "NODES")
#Inheritance(strategy = InheritanceType.JOINED)
#DiscriminatorColumn(name = "KIND", discriminatorType = DiscriminatorType.STRING, length = 1)
public abstract class Node implements Serializable {
#Column(unique = true, name = "NAME", nullable = false, length = 50)
private String name;
#ManyToMany(mappedBy = "children", fetch = FetchType.LAZY)
#JoinTable(name = "NODE_PARENTS", joinColumns = { #JoinColumn(name = "NODEID", referencedColumnName = "ID") }, inverseJoinColumns = { #JoinColumn(name = "PARENTID", referencedColumnName = "ID") })
private Set<Parent> parents;
public Node() {
this.parents = new HashSet<Parent>();
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public void setParents(Collection<Parent> parentList) {
for(Parent parent : this.parents) {
removeParent(parent);
}
for (Parent parent : parentList) {
addParent(parent);
}
}
public void addParent(Parent parent) {
this.parents.add(parent);
if (!parent.getMembers().contains(this)) {
parent.getMembers().add(this);
}
}
public void removeParent(ACLGroup parent) {
this.parents.remove(parent);
if (parent.getMembers().contains(this)) {
parent.getMembers().remove(this);
}
}
public Set<ACLGroup> getParents() {
return Collections.unmodifiableSet(this.parents);
}
}
#Entity
#Table(name = "LEAFS")
#DiscriminatorValue("L")
public class Leaf extends Node {
// some fields to persist
}
#Entity
#Table(name = "INNER_NODES")
#DiscriminatorValue("P")
public class InnerNodes extends Node implements Parent {
// some fields to persist
#ManyToMany(fetch = FetchType.LAZY)
private Set<Node> children;
public InnerNodes() {
this.children = new HashSet<Node>();
}
public Set<Node> getChildren() {
return Collections.unmodifiableSet(this.children);
}
public void setChildren(Set<Node> childList) {
for (Node child : this.children) {
removeChild(child);
}
for (Node child : childList) {
addChild(child);
}
}
public void addChild(Node child) {
this.children.add(child);
if (!child.getParents().contains(this)) {
child.addParent(this);
}
}
public void removeChild(Node child) {
this.children.remove(child);
if (child.getParents().contains(this)) {
child.removeParent(this);
}
}
}
public interface Parent {
Set<Node> getChildren();
void setChildren(Set<Node> childList);
void addChild(Node child);
void removeChild(Node child);
}
The problem is, I can not persist the interface Parent, but I need it to prevent circles. Every Leaf or InnerNode can have multiple Parents and every Parent can have multiple children.
Any idea to implement that?
Thanks a lot.
Andre