JPA delete a record while updating - jpa

Can somebody help me with following scenario?
I have a requirement where I need to delete the membership record and child table when the child is not associated with any other parent record..else I just need to dissociate the membership record.
I'm using JPA Eclipse link...
Below are my three entities.. Please note that Parent and Child are linked via Parent_Child many-to-many relationship. I'm using Parent_Child table on both sides as we have extra columns in it..
//Parent
#Entity
#Table(name = "PARENT")
public class Parent implements java.io.Serializable {
#Id
#Column(name = "parent_id")
private String parentId;
private Set<ParentChild> parentChildSet = new HashSet<ParentChild>(0);
public String getParentId() {
return parentId;
}
public void setParentId(String parentId) {
this.parentId = parentId;
}
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "parent")
public Set<ParentChild> getParentChildSet() {
return parentChildSet;
}
public void setParentChildSet(Set<ParentChild> parentChildSet) {
this.parentChildSet = parentChildSet;
}
}
//Child
#Entity
#Table(name = "CHILD")
public class Child implements java.io.Serializable {
private String ChildId;
private Set<ParentChild> parentChildSet = new HashSet<ParentChild>(0);
#Id
#Column(name = "child_id")
public String getChildId() {
return ChildId;
}
public void setChildId(String childId) {
ChildId = childId;
}
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "child")
public Set<ParentChild> getParentChildSet() {
return parentChildSet;
}
public void setParentChildSet(Set<ParentChild> parentChildSet) {
this.parentChildSet = parentChildSet;
}
}
//Parent-Child
#Entity
#Table(name = "PARENT_CHILD")
public class ParentChild implements java.io.Serializable{
private String parentChildId;
private Parent parent;
private Child child;
#Id
#Column(name = "parent_child_id")
public String getParentChildId() {
return parentChildId;
}
public void setParentChildId(String parentChildId) {
this.parentChildId = parentChildId;
}
public Parent getParent() {
return parent;
}
public void setParent(Parent parent) {
this.parent = parent;
}
public Child getChild() {
return child;
}
public void setChild(Child child) {
this.child = child;
}
}
Relationships: Parent - Praent_Child (one-to-many)
Child - Parent_Child (one-to-many)
I'm trying it do it as below...but, it gives me integrity constraint exception as i'm deleting the child and then updating the parent
public void ParentChildServiceDAO{
........
.........
if(parentChildSet().size() > 1){
parent.getParentChildSet().remove(parentChildRecord);
child.getParentChildSet().remove(parentChildRecord);
} else{
parent.getParentChildSet().remove(parentChildRecord);
child.getParentChildSet().remove(parentChildRecord);
getJpaTemplate().remove(child);
}
update(parent);
}
Your help is greatly appreciated...

Related

Spring Data JPA. Parent table data is not getting rolled back when exception occurred while inserting record in child table

I have 2 tables one to many relationship between Employee and Department table, Employee table are having column Id as PK, Name and Sal whereas Department table having column Dept_ID,Dept_Name & Dept_Loc and primary key is (Dept_ID,Dept_Name) i.e composite key and Dept_ID is foreign key ref from Employee table's Id column. The issue is when I save record in parent table i.e Employee it get saved but if in case I get exception while inserting record for child table i.e Department table,,data is not getting rolled back for EMployee table. Please help I m struggling and I am attaching my code.
public class GlEmployee implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "emp_seq")
#Column(name = "EMP_ID")
private long empId;
#Column(name = "EMP_CITY")
private String empCity;
#Column(name = "EMP_NAME")
private String empName;
#Column(name = "EMP_SALARY")
private BigDecimal empSalary;
// bi-directional many-to-one association to EmpDepartment
#OneToMany(mappedBy = "glEmployee",cascade = CascadeType.ALL)
private List<EmpDepartment> empDepartments = new ArrayList<>();
public GlEmployee() {
}
public long getEmpId() {
return this.empId;
}
public void setEmpId(long empId) {
this.empId = empId;
}
public String getEmpCity() {
return this.empCity;
}
public void setEmpCity(String empCity) {
this.empCity = empCity;
}
public String getEmpName() {
return this.empName;
}
public void setEmpName(String empName) {
this.empName = empName;
}
public BigDecimal getEmpSalary() {
return this.empSalary;
}
public void setEmpSalary(BigDecimal empSalary) {
this.empSalary = empSalary;
}
public List<EmpDepartment> getEmpDepartments() {
return this.empDepartments;
}
public void setEmpDepartments(List<EmpDepartment> empDepartments) {
this.empDepartments = empDepartments;
}
public EmpDepartment addEmpDepartment(EmpDepartment empDepartment) {
getEmpDepartments().add(empDepartment);
empDepartment.setGlEmployee(this);
return empDepartment;
}
public EmpDepartment removeEmpDepartment(EmpDepartment empDepartment) {
getEmpDepartments().remove(empDepartment);
empDepartment.setGlEmployee(null);
return empDepartment;
}
}
#Entity
#Table(name = "EMP_DEPARTMENT")
public class EmpDepartment implements Serializable {
private static final long serialVersionUID = 1L;
#EmbeddedId
private EmpDepartmentPK id;
#Column(name = "DEP_LOC")
private String depLoc;
public EmpDepartment(EmpDepartment id, String dep) {
}
// bi-directional many-to-one association to GlEmployee
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "DEP_ID", insertable = false, updatable = false)
private GlEmployee glEmployee;
public EmpDepartment() {
}
public EmpDepartmentPK getId() {
return this.id;
}
public void setId(GlEmployee glEmployee, String deptName) {
EmpDepartmentPK empDepartment = new
EmpDepartmentPK(glEmployee.getEmpId(), deptName);
this.id = empDepartment;
}
public String getDepLoc() {
return this.depLoc;
}
public void setDepLoc(String depLoc) {
this.depLoc = depLoc;
}
public GlEmployee getGlEmployee() {
return this.glEmployee;
}
public void setGlEmployee(GlEmployee glEmployee) {
this.glEmployee = glEmployee;
}
}
#Embeddable
public class EmpDepartmentPK implements Serializable {
// default serial version id, required for serializable classes.
private static final long serialVersionUID = 1L;
#Column(name = "DEP_ID")
private long depId;
#Column(name = "DEP_NAME")
private String depName;
public EmpDepartmentPK() {
}
public EmpDepartmentPK(long depId, String depName) {
super();
this.depId = depId;
this.depName = depName;
}
public long getDepId() {
return this.depId;
}
public void setDepId(long depId) {
this.depId = depId;
}
public String getDepName() {
return this.depName;
}
public void setDepName(String depName) {
this.depName = depName;
}
#Service
public class EmployeeService {
#Autowired
private EmployeeRepository employeeRepository;
#Transactional
public void createEmp() {
GlEmployee employee = new GlEmployee();
employee.setEmpCity("Pune");
employee.setEmpName("Ankush");
employee.setEmpSalary(new BigDecimal(200));
employeeRepository.save(employee);
EmpDepartment department = new EmpDepartment();
department.setId(employee, "ME");
department.setDepLoc(null);
department.setGlEmployee(employee);
employee.addEmpDepartment(department);
employeeRepository.save(employee);
}
}

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)

spring data JPA deletion

I am getting a foreign key violation when I try to delete a record.
I have this record:
#Entity
public class Parent {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long parentId;
#OneToOne(cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name = "childId")
private Child child;
and
#Entity
#Table(name = "Child")
public class Child {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long childId;
public Long getOperatoryId() {
return id;
}
When I try to delete the child, I get a key violation because there are some parent records that point to the children. I thought I could delete the parent first, then go delete the children as:
parentRepository.delete(parent)
but I get an error that the property id doesn't exist on child. Is this because the child id is named childId and not id?
Here I worked on similar example what did you asked for. Customize it based on your need.
SQL Server table create query
CREATE TABLE "DBO"."Parent"(
Parent_Id INT PRIMARY KEY NOT NULL,
Parent_Name VARCHAR(255) NOT NULL)
CREATE TABLE "DBO"."Child"(
Child_Id INT PRIMARY KEY NOT NULL,
Child_Name VARCHAR(255) NOT NULL,
Parent_Ref_Id int not null,
CONSTRAINT FK_Parent_Ref_Id FOREIGN KEY (Parent_Ref_Id) REFERENCES Parent(Parent_Id)
)
Spring data JPA code
Parent Entity
#Entity(name = "Parent")
#Table(name = "Parent")
public class Parent {
#Id
#Column(name = "Parent_Id")
private long parentId;
#Column(name = "Parent_Name")
private String parentName;
//cascade = {CascadeType.REMOVE} OR orphanRemoval = true
#OneToOne(cascade = {CascadeType.ALL},orphanRemoval = true,fetch = FetchType.LAZY,mappedBy="parentInfo")
private Child childInfo;
public long getParentId() {
return parentId;
}
public void setParentId(long parentId) {
this.parentId = parentId;
}
public String getParentName() {
return parentName;
}
public void setParentName(String parentName) {
this.parentName = parentName;
}
public Child getChildInfo() {
return childInfo;
}
public void setChildInfo(Child childInfo) {
this.childInfo = childInfo;
}
}
Child Entity
#Entity(name = "Child")
#Table(name = "Child")
public class Child {
#Id
#Column(name = "Child_Id")
private int childId;
#Column(name = "Child_Name")
private String childName;
#OneToOne
#JoinColumn(name = "Parent_Ref_Id", referencedColumnName = "Parent_Id")
private Parent parentInfo;
public int getChildId() {
return childId;
}
public void setChildId(int childId) {
this.childId = childId;
}
public String getChildName() {
return childName;
}
public void setChildName(String childName) {
this.childName = childName;
}
public Parent getParentInfo() {
return parentInfo;
}
public void setParentInfo(Parent parentInfo) {
this.parentInfo = parentInfo;
}
}
Child Repo Code
public interface ChildRepo extends CrudRepository<Child,Long> {
}
Parent Repo Code
public interface ParentRepo extends CrudRepository<Parent,Long> {
Parent findByParentId(long id);
}
Controller Code
#Autowired
private final ParentRepo parentRepo;
....
private void save() {
//Save the parent
Parent p = new Parent();
p.setParentId(1);
p.setParentName("Parent1");
Child c = new Child();
c.setChildId(1);
c.setChildName("Child1");
c.setParentInfo(p);
p.setChildInfo(c);
parentRepo.save(p);
//delete the parent and child as well
Parent p1 = parentRepo.findByParentId(1);
parentRepo.delete(p1);
}

AnnotationException: mappedBy reference an unknown target entity property

I have one exception, which yold what I have no mapping on table. But I have this
Exeption is : \
AnnotationException: mappedBy reference an unknown target entity property: Relative.people in Person.relations
Relative entity is here:
#Entity
#Table(name = "relation")
public class Relative extends AbstractModel<UUID> implements Model<UUID> {
private UUID id;
private Person person;
private RelationTypeEnum relation;
public Relative() {
}
#Override
public void assignId() {
id = UUID.randomUUID();
}
#Override
#Id
#Column(name = "id")
public UUID getId() {
return id;
}
#ManyToOne
#JoinColumn(name="person_id", nullable=false)
public Person getPerson() {
return person;
}
#Column(name = "relation")
public RelationTypeEnum getRelation() {
return relation;
}
public void setId(UUID id) {
this.id = id;
}
public void setPerson(Person person) {
this.person = person;
}
public void setRelation(RelationTypeEnum relation) {
this.relation = relation;
}
}
And Person entity is here:
#Entity
#Table(name = "people")
public class Person extends AbstractModel<UUID> implements Model<UUID> {
private UUID id;
private String name;
private List<Relative> relations;
#Override
public void assignId() {
id = UUID.randomUUID();
}
#Override
#Id
#Column(name = "id")
public UUID getId() {
return id;
}
#Column(name = "name")
public String getName() {
return name;
}
#OneToMany(targetEntity=Relative.class, cascade=CascadeType.ALL,
mappedBy="people")
public List<Relative> getRelations() {
return relations;
}
public void setId(UUID id) {
this.id = id;
}
public void setName(String username) {
this.name = username;
}
public void setRelations(List<Relative> relations) {
this.relations = relations;
}
}
Solved.
Just changed
#Table(name = "people")
to
#Table(name = "person")
In my case there was a project which included a copy of the jar causing this issue. It was a web project which is including the jar inside its lib i.e. 2 copies of the same jar one with a different class version. Only discovered this when I physically opened the main ear and found the 2nd jar inside a web project.

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