Are there case with is usefull to not use cascade in a parent - child relation?
Actually I use alway cascade and i would like to know if that could be interesting to avoid to use them.
#Entity
public class Lodger{
#OneToMany
private List<Bail> bailList;
...
}
#Entity
public class Bail {
#OneToMany
private List<Rent> rents;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "lodger_id")
private Lodger lodger;
#OneToOne
private Room room;
}
#Entity
private class Rent{
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "bail_id")
private Bail bail;
#OneToMany
private List<RoomPayment> roomPayment;
}
#Entity
private class RoomPayment{
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "rent_id")
private Rent rent;
}
So all relation seem to be composition. If i create a new RoomPayment, is it better to do:
roomPayment.setRent(rent);
roomPaymentDao.save(roomPayment);
rent.getRoomPayment().add(roomPayment);
rentDao.save(rent);
I think you are mixing up compositions and parent - child relationships.
Composition
From the wikipedia definition:
It is ... called composite, if the objects it refers to are really its parts, i.e. have no independent existence
Composite relationships are always modeled with a CascadeType.ALL or [CascadeType.PERSIST, CascadeType.REMOVE] (the later for huge objects that you would like to update only if necessary). If you don't use a cascade here, you don't have a composition.
Parent - child relationships
There are different relationships that could be called parent - child relation. I guess you are talking about something like a directory association:
#Entity
public Directory {
#ManyToOne
private Directory parent;
#OneToMany(mappedBy = "parent")
private Collection<Directory> children;
}
In that case you will often need to manage the association manually, because you don't want to update the whole tree if you change an attribute of the root directory.
You could add CascadeType.REMOVE to the children association - but it is better to delete subdirectories explicitly to prevent harmful mistakes (like the explicit -r for rm).
Related
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 was looking for documentation or answer how will the cascade work for child of child , example :
public class Parent{
#OneToMany(fetch = FetchType.EAGER,mappedBy = "parent",cascade = CascadeType.ALL)
private List<Child> child;
}
public class Child{
#OneToMany(mappedBy="child")
private List<AnotherChild> anohterChild;
}
#ManyToOne
private Parent parent;
}
now the question, will the cascade operation applied on "Child" from parent class apply to "AnotherChild" ?
in other words if I persist "Parent" Object will it persist "AnotherChild" ?
If you persist your parent, only those childs which are in the child-list of your parent-class get persisted but not the list of AnotherChilds in your child-class.
If you wish to persist them too just cascade it too:
public class Child{
#OneToMany(mappedBy="child", cascade = CascadeType.PERSIST)
private List<AnotherChild> anohterChild;
#ManyToOne
private Parent parent;
}
And just use CascadeType.ALLwhen you really need it, because this cascade-type includes more than just persisting. As it is explained in the following picture, CascadeType.ALL includes all other cascade-types including the cascade-type "remove" which means that, when your parent-object gets removed, all other child-objects get removed too.
Here are my entities:
#Entity
public class Parent {
#Id
#Column(name = "ID")
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 = "ID")
private Long id;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name="PARENTID", nullable = false)
private Parent parent;
...
}
I want to perform following operations:
Delete child entity from parent (not the parent itself).
Add new child entity to parent (parent.setChild(child)).
Now save the child entity in to DB and update parent accordingly.
This is what I tried but it raises ConstraintViolationexception for parent:
entityManager.remove(parent.getChild())
parent.setChild(new Child())
entityManager.merge(parent);
How can I fix this?
The 'old' child probably still references the parent, while the new child does not. Both is an issue.
In addition to removing an old child, you should set the parent reference of the child instance to null.
In addition to adding the new child to the parent, you will need to add the parent to the child in order to provide the foreign key.
Do not cascade from the many side (child) to the one side (parent). The behavior for this type of cascades is undefined and might work in an unexpected way.
EDIT: what the JPA 2.0 spec has to say:
Note that it is the application that bears responsibility for maintaining
the consistency of runtime relationships—for example, for
insuring that the “one” and the “many” sides of a bidirectional
relationship are consistent with one another when the application
updates the relationship at runtime.
Modify the relation in the parent as follows:
#OneToMany(cascade = {CascadeType.ALL}, orphanRemoval=true, mappedBy = "parent")
Just set the new child to the parent and merge the parent. Now the children referencing earlier becomes orphans and JPA automatically deletes those while committing the transaction.
Thanks,
JK
I would like to know if there exists some way to use generics in JPA 2.0?
Consider this scenario:
#Entity
public class GenericPhoto<T> implements Serializable {
#Id
#GeneratedValue
private long id;
#NotNull
private byte[] file;
#ManyToOne(cascade = { CascadeType.DETACH })
#JoinColumn(name = "PARENTID", nullable = false)
#NotNull
private T parent;
//...
}
#Entity
public Car {
#OneToMany(mappedBy = "parent")
private Set<GenericPhoto<Car>> photos;
//...
}
#Entity
public Truck {
#OneToMany(mappedBy = "parent")
private Set<GenericPhoto<Truck>> photos;
//...
}
I hope the code explains it all. I simply want to make a generic class for photo, which I think makes it easier to implement services etcetera.
Best regards
When you use a generic, it is similar to not typing the field (i.e. Object parent), so you need to tell JPA how to map the relationship. For this you can use targetEntity in JPA.
For this to work, you will need a common superclass to Car and Truck i.e. Auto, and set the targetEntity in the #ManyToOne to Auto (you may also consider moving photos up to Auto).
If you can't use inheritance for some reason, (it is best to use inheritance). Then if you use EclipseLink you could use a #VariableOneToOne relationship.
I've read the documentation and thought I'd be able to do the following....
map my classes as so (which does work)
#Entity
public class ParentEntity
{
...
#OneToMany(mappedBy = "parent")
private List<ChildEntity> children;
...
}
#Entity
public class ChildEntity
{
...
#Id
#Column
private Long id;
...
#ManyToOne
#NotFound(action = NotFoundAction.IGNORE)
#JoinColumn(name = "parent_id")
private ParentEntity parent;
...
}
.. but i want to be able to insert into both tables in one go and thought this would work:
parent = new ParentEntity();
parent.setChildren(new ArrayList<ChildEntity>());
ChildEntity child = new ChildEntity();
child.setParent(parent);
parent.getChildren().add(child);
session.persist(parent);
Can anyone tell me what i'm missing?
Do i need to save the parent first, then add the child and save it again?
thanks.
You have to add #OneToMany(cascade=CascadeType.PERSIST). You can also have CascadeType.ALL which includes persist, merge, delete...
Cascading is the setting that tells hibernate what to do with collection elements when the owning entity is persisted/merged/deleted.
By default it does nothing with them. If the respective cascade type is set, it invokes the same operation for the collection elements that were invoked for the parent.