Im kinda new to JPA, My question is, if I have the following parent- child relationship between two entities. with this setting(as show below), is it okay to delete a child using just a named query ("delete from child where parent.id:id) and then not remove the from the parent children collection? I have tested this approach of just using named query and not deleting the children from the parent collection and it works just fine, but im trying to see if there are any major impacts when i delete them this way. The reason why im not removing them to the collection objects is because, Children is set to have NOT nullable field parent id. Thank you very much, and I look forward for your answers :)
public class Parent {
ID.....
parentName...
#OneToMany(mappedBy = "parent", cascade = CascadeType.ALL,
fetch = FetchType.EAGER, orphanRemoval = true)
private List<Child> children;
}
public class Child {
id;
#ManyToOne(optional = false)
#JoinColumns({
#JoinColumn(name = "PARENT_ID", referencedColumnName = "ID", nullable = false)
})
private Parent parent;
}
Looks like it's perfectly OK to delete objects like this. orphanRemoval manages the rest for you.
Related
In the context of a Spring Boot project using Spring Data JPA, I have defined the following entities:
Ent1 contains a list of Ent2 elements
Ent2 contains a list of Ent3 elements
When fetching a top-level Ent1 object through a repository, I'm seeing that every Ent2 which has more than one child appears multiple times in the Ent1.ent2 list. For example, an Ent2 with two childs will appear twice.
So instead of getting this:
I'm getting this:
Notes:
There are no duplicates in the database
If I delete ent3b in the database, the duplicated ent2 disappears
Here's a simplified version of the code:
```java
#Entity
public class Ent1 {
#OneToMany(mappedBy="parent", fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true)
private List<Ent2> ent2 = new ArrayList<Ent2>();
}
#Entity
public class Ent2 {
#ManyToOne
#JoinColumn(name = "PARENT_ID", nullable = false)
protected Ent1 parent;
#OneToMany(mappedBy="parent", fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true)
private List<Ent3> ent3 = new ArrayList<Ent3>();
}
#Entity
public class Ent3 {
#ManyToOne
#JoinColumn(name = "PARENT_ID", nullable = false)
protected Ent2 parent;
}
```
Solution was to convert Lists into Sets. Lists in JPA require additional data (i.e. an ordering column) to extract a total ordering of elements from the relationship. It can be done but typically the average user only needs Set and it's a reflection of the relationship that most people model.
OP also commented that the previous provider didn't have this requirement so if you were previously using EclipseLink and switching ORM providers this may be a problem for you too.
I have entities "ZakladProdukcyjny" and "MiejsceProwadzeniaDzialnosci".
There is an unidirectional relation #OneToMany with a join table.
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
#JoinTable(name = "ZAKLAD_PRODUKCYJNY_MIEJSCE_PROWADZENIA_DZIALALNOSCI",
joinColumns = {
#JoinColumn(name = "zakladProdukcyjny_ID")},
inverseJoinColumns = {
#JoinColumn(name = "miejsceProwadzeniaDzialalnosci_ID")})
private List<MiejsceProwadzeniaDzialalnosci> miejscaProwadzeniaDzialalnosci = new ArrayList<>();
I am using Spring JPARepositories
public interface ZakladProdukcyjnyRepository extends JpaRepository<ZakladProdukcyjny, Long>,
Everytime i am saving the parent entity with zakladProdukcyjnyRepository.save(zakladProdukcyjny), children entities are being persised into DB so everytime save is executed on the JPARepository i am having duplicated entries.
The child entity uses a lombok for generating equals and hashcode.
#EqualsAndHashCode(callSuper=false)
public class MiejsceProwadzeniaDzialalnosci extends BaseEntity {
I have no idea what may be wrong here.
This should have beed fixed long time ago:
https://hibernate.atlassian.net/browse/HHH-5855
https://hibernate.atlassian.net/browse/HHH-6776
Try changing the List to a Set or remove CascadeType.ALL and leave just CascadeType.MERGE.
I have solved the problem. The issue was an equals functionality. Somewhere in the code i had:
for (MiejsceProwadzeniaDzialalnosci mpd : uaktualnioneMiejscaProwadzeniaDzialalnosciZBDO) {
if (!(zaklad.getMiejscaProwadzeniaDzialalnosci().contains(mpd))) {
zaklad.getMiejscaProwadzeniaDzialalnosci().add(mpd);
}
}
after ovveriding the equals method there is no duplicates.
I have a list with 25 MyApplication objects that I want to save using hibernate/JPA. This is done with the following method:
MyApplicationRepository.saveAll(myAppList);
However I noticed that hibernate creates over 60.000 MyApplication objects (close to the total amount of records already in database for this entity) while inserting/updating this list of 25 in the database. I don't have a lot of hibernate experience which leads me to believe I created a inefficient entity relations. A part of the MyApplication class:
public class MyApplication {
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(name = "APPLICATION_CATEGORY", joinColumns = {
#JoinColumn(name = "applicationid", nullable = false, updatable = false) },
inverseJoinColumns = { #JoinColumn(name = "categoryid",
nullable = false, updatable = false) })
private Set<Category> categorySet;
#OneToMany(mappedBy = "myApplication",
cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Set<Screenshot> screenshotSet;
}
Category class (one example of multiple of the many to many relations of MyApplication):
public class Category {
#ManyToMany(fetch = FetchType.LAZY, mappedBy = "categorySet")
private Set<MyApplication> myApplicationSet;
}
Screenshot class:
public class Screenshot {
#ManyToOne
#JoinColumn(name = "applicationid")
private MyApplication myApplication;
}
What did I do wrong that resulted in Hibernate creating so many instances of MyApplication when saving?
Note 1: In the end all of the information of MyApplication and the information of it's categories and screenshots is saved correctly in the database.
Note 2: It's important that not only MyApplication is saved but also everything from all its categories and screenshots as well.
I was able to fix the issue. The problem was caused by the bidirectional nature of the manyToMany relation. This resulted in the category also querying all the applications from the database before saving. As this is not what I want, I resolved the issue by turning it into a unidirectional relation by removing the myApplicationSet from the Category class. Now only 25 MyApplication instances are constructed to save 25 applications and my memory usage remains stable.
I am developing an REST API to a pizzeria store. And here i'm trying to delete a Flavor and all data related to it. Further explained below:
Classes:
Flavor have at least one Filling, each one taking a position on it.
i.e: Souce (at pos. 1), mozzarela (at pos. 2) tomato (at pos. 3)
Flavors must have a price to each Size
With that in mind, we can conclude that exist two many-to-many relationships:
Flavor to many Filling
Flavor to many Size
Class diagram of actual implementation
The requirement is to: delete a Flavor, and automatically delete all the FillingPositionFlavor and FlavorPriceSize.
But,I'm confused on use of CascadeType.REMOVE and orphanRemoval = true:
When I use Cascade and OrphanRemoval on Flavor.sizePrices, get a HibernateException when trying to edit a Flavor, exclusion works fine:
A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance: com.pkg.Flavor.sizePrices
When I use Cascade on Flavor.sizePrices, get a PSQLException when excluding a Flavor, editing works fine:
ERROR: update or delete on table "tb_flavor" violates foreign key constraint "fk9orw0yhtc0e06ka84dbcd2c82" on table "tb_flavor_size_price"
I'm doing unit testing of services in Spring Boot to test all the CRUD operations.
Below is the actual code, I hid properties like id and others to facilitate the read.
#Entity
#Table(name = "tb_flavor")
class Flavor {
#OneToMany(cascade = {CascadeType.PERSIST,CascadeType.REMOVE},orphanRemoval = true)
private Set<FlavorPositionFilling> flavors = new HashSet<FlavorPositionFilling>();
#OneToMany(cascade = {CascadeType.PERSIST, CascadeType.REMOVE},orphanRemoval = true)
private Set<FlavorPriceSize> priceSizes;
// other properties and methods
}
#Entity
#Table(name = "tb_flavor_price_size")
class FlavorPriceSize {
#EmbeddedId
private FlavorPriceSizeEmbeddeId id;
private float price;
// other properties and methods
}
#Embeddable
class FlavorPriceSizeEmbeddeId implements Serializable {
#ManyToOne(cascade = { CascadeType.ALL })
#JoinColumn(name = "ID_FLAVOR_FK", referencedColumnName = "id_flavor")
private Flavor flavor;
#ManyToOne(cascade = { CascadeType.ALL })
#JoinColumn(name = "ID_SIZE_FK", referencedColumnName = "id_size")
private Size size;
}
#Entity
#Table(name = "tb_flabor_position_filling")
class FlaborPositionFilling {
#EmbeddedId
private FlaborPositionFillingEmbeddedId id;
private Integer position;
}
#Embeddable
class FlaborPositionFillingEmbeddedId implements Serializable {
#ManyToOne(cascade = CascadeType.REMOVE)
#JoinColumn(name="ID_FLAVOR_FK", referencedColumnName="id_flavor")
private Flavor sabor;
#ManyToOne()
#JoinColumn(name="ID_FILLING_FK", referencedColumnName="id_filling")
private Filling filling;
}
I've read a lot about both, but still not understand the right use of each and their effect on operations. Can anyone explain it to me? Show videos, images, code...
Let's assume that you have a parent -> child relationship.
If you set CacadeType.REMOVE on the relationship every EntityManager.remove call on the parent will also remove the children.
orphanRemoval = true is used to delete orphan children.
So if remove a child from the parent reference or collection and save the parent the child will be deleted because its no longer attached to the parent.
I'm trying to remove list of parent without removing children
The parent :
#Entity
public class Parent {
#Id
#Column(name = "PARENTID")
private Long id;
#OneToMany(cascade = {CascadeType.ALL}, mappedBy = "parent")
private Set<Child> childs = new HashSet<Child>();
...
}
The child:
#Entity
public class Child {
#Id
#Column(name = "CHILDID")
private Long id;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name="PARENTID", nullable = false)
private Parent parent;
...
}
What i did is to update all children using HQL query, and then delete the list of parents using HQL query as well.
The problem is that this way is too heavy, is there any simple solution using JPA ?
you could set your Cascade in the following section to not delete
#OneToMany(cascade = {CascadeType.ALL}, mappedBy = "parent")
private Set<Child> childs = new HashSet<Child>();
by editing the annotation as follows:
#OneToMany(cascade = {CascadeType.MERGE, CascadeType.PERSIST}, mappedBy = "parent")
and whatever other CascadeType options you need ( see CascadeType Enums). This will make it so that when you delete the parents, the children won't be deleted as well.
The mapping as it is does not allow for a simple deletion of parents with their children. It does not support having a Child without a Parent (nullable = false).
You either need to
set the parent id to a 'surrogate' Parent before removal of the parents. You can do it by a bulk update or by fetching the parents that are about to be deleted, iterate over the children and reset the parent references. Whether you use bulk updates or object manipulation depends on how you would remove the parents. If you remove the parents with a bulk query, use a bulk query for the children as well. In general I would use the object approach as the safer one. The bulk query is more compact.
drop the nullability constraint and change the provided cascade. Remove the REMOVE cascade from the #OneToMany mapping and you can remove parents as you like.