Hibernate. Keep reference to last child - java

I have simple Parent-Child relationship with #OneToMany and #ManyToOne annotations.
#Entity
public class Parent {
#OneToMany(cascade = CascadeType.ALL, mappedBy = "parent", fetch = FetchType.LAZY)
private List<Child> children = new ArrayList<>();
}
public class Child {
#ManyToOne( fetch = FetchType.LAZY, optional = false)
#JoinColumn( name = "parent_id" )
#ForeignKey( name = "fk_child_parent" )
private Parent parent;
}
But I also want to keep reference to current(last) child inside Parent entity.
How to do it in right way? Should I introduce new undirectional #OneToOnerelationship in Parent? Like this:
#OneToOne( optional = true, cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinColumn( name = "current_child_id", nullable = false )
#ForeignKey( name = "FK_parent_child" )
private Child currentChild;

So you are always gonna have the issue of determining the insertion order of child elements for existing parent's and children on application start. You should therefore have this data preserved in your persistence layer.
Add a created (or updated) timestamp to your child class, you can determine for yourself how you want to handle the initialization of this field (i.e. with column defaults or triggers etc.). Now the required information should be available to you in your code, just add a 'compareTo' method to your Child (to sort) and add the following a getMostRecentChild method to your Parent class.
#Entity
public class Parent {
#OneToMany(cascade = CascadeType.ALL, mappedBy = "parent", fetch = FetchType.LAZY)
public List<Child> children = new ArrayList<>();
public Child getMostRecentChild() {
if(children == null || children.isEmpty()) {
return null;
}
return Collections.sort(children).get(0);
}
}
public class Child {
#ManyToOne( fetch = FetchType.LAZY, optional = false)
#JoinColumn( name = "parent_id" )
#ForeignKey( name = "fk_child_parent" )
public Parent parent;
#Column( name = "created_ts" )
public createdTs;
public int compareTo(Child other) {
return created_ts.compareTo(other);
}
}
The implementation details for how you find the currentChild are irrelevant. You could also use a TreeSet or just a loop to find the currentChild.
EDIT: Argument why this is better than a currentChild column.
If you don't like the above answer consider the following
Adding a currentChild field provides two references to the same object. It is therefore not normalized, whereas, adding a timestamp adds a normalized column to your schema from which the information you require (and more) can easily be derived in a way that is directly coupled to the logic you are attempting to encapsulate in your currentChild column.
In order to ensure that your currentChild doesn't mismatch from it's intended value, you must encapsulate it's logic in either the Hibernate object, which you can then override on a database level, or with triggers on a database level, which can be ugly and hard to trace (whereas a default value and 'ON UPDATE' for created_ts field is fairly common place)

Since you have a List, you can just look at the last element of the list.
You will want Hibernate to maintain the list order by adding #OrderColumn(name="INDEX_COL") below the #OneToMany

Related

JPA: 'CascadeType.REMOVE' or 'orphanRemoval = true', which use in a n:n relation that generate new table/class with EmbeddeId class?

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.

Deleting Child from Parent, using Named query

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.

Remove parent entity without removing children (jpa)

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.

JPA how to delete an entity that is a target of onetoone with cascade remove

I have two entities with a onetoone relationship, A and B. B entity is optional, can be updated and removed on it's own, but must always be linked to an instance of A.
So i have two JPA entities, A and B with a bi-directional relationship. THis is the one from A to B.
#OneToOne(mappedBy = "a", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
I can create a A and B, remove the A and both get deleted. good.
But because of the cascade from A to B, if i em.remove(b) the delete doesn't get persisted. Even if i do a.setB(null) first.
The only way to delete the optional entity, while keeping the cascade, seems to be to use a new JPA2 feature, orphanRemoval=true. Call a.setB(null), then persist A.
This means i can't do operations directly on B, it implies too strong of a composition relationship, all actions on B must be done via A.
But B is not an embedded object, it's a full blown Entity in it's own right, how can i delete it independently of A?
The best way seems to be to remove the cascade, and force users to make sure they delete any related objects separately before they delete the A? Enforced by a FK constraint in the B table.
This is such a straight forward case. two related entities, the relationship is optional on one end, and mandatory on the other.
Oh, this is with hibernate 4.2.3-Final
Your current object design implicitly defined that one is more important than another. That is, one will have the foreign key to another.
To make them equal, you just define the JoinTable between them. Set cascade on both sides, and then everything will work as expected.
Example:
Document class
#Entity
public class Document extends ABaseEntity {
private Medicine medicine;
#OneToOne(cascade = CascadeType.REMOVE)
#JoinTable(
name = "Document_Medicine",
joinColumns =
#JoinColumn(name = "DOC_ID", referencedColumnName = "ID"),
inverseJoinColumns =
#JoinColumn(name = "MED_ID", referencedColumnName = "ID"))
public Medicine getMedicine() {
return medicine;
}
public void setMedicine(Medicine medicine) {
this.medicine = medicine;
}
}
Medicine class
#Entity
public class Medicine extends ABaseEntity{
private Document document;
#OneToOne(mappedBy = "medicine", cascade = CascadeType.REMOVE)
public Document getDocument() {
return document;
}
public void setDocument(Document document) {
this.document = document;
}
}

Two almost identical OneToOne in Entity, custom join annotation

I've got Parent - Child relationship OneToOne, but two of them. Annotations are not good but it produces good DB schema, yet code is not working. If I try to save Parent instance, Hibernate at first tries to save child1 and child2 - but it breaks FK defined in Child -> because owner doesn't exist yet in DB...So I need to save Parent and then child1, and child2.
If I could do that it doesn't help, because when I try to load Parent, Hibernate will not know which record in Child table belongs to child1 or child2...So in this case I would need to specify one condition in join for child1 something like "where type = 1" and for child2 "where type = 2"...
Just to clarify: in Child table there will be zero or one child for one Parent with ChildType.A (always child1) and zero or one child with ChildType.B (always child2).
I need to save xml which looks like this:
<parent id="" oid="">
<child1 (and other attributes)>
<child2 (and other attributes)>
<parent>
Both child1 and child2 elements are the same type therefore are type of Child in java classes. Only difference is element name (in java I differentiate them with ChildType). Only identification for children are id and oid attributes from parent. They points to another Parent hence target in Child.
I need to change annotations somehow to get this working...Do you guys have some ideas, because I'm really stuck???
Parent.java
public class Parent {
private String oid
private Long id;
private Child child1;
private Child child2;
#Id
#GeneratedValue(generator = "IdGenerator")
#GenericGenerator(name = "IdGenerator", strategy = "com.example.IdGenerator")
#Column(name = "id")
public Long getId() {
return id;
}
#Id
#GeneratedValue(generator = "OidGenerator")
#GenericGenerator(name = "OidGenerator", strategy = "com.example.OidGenerator")
#Column(name = "oid", nullable = false, updatable = false, length = 36)
public String getOid() {
return oid;
}
#OneToOne(optional = true, fetch = FetchType.EAGER)
#Cascade({org.hibernate.annotations.CascadeType.ALL})
public Child getChild1() {
return child1;
}
#OneToOne(optional = true, fetch = FetchType.EAGER)
#Cascade({org.hibernate.annotations.CascadeType.ALL})
public Child getChild2() {
return child2;
}
}
Child.java
public class Child {
private Parent owner;
private String ownerOid;
private Long ownerId;
private ChildType type;
private Parent target;
#MapsId("owner")
#ManyToOne(fetch = FetchType.LAZY)
#PrimaryKeyJoinColumns({
#PrimaryKeyJoinColumn(name = "owner_oid", referencedColumnName = "oid"),
#PrimaryKeyJoinColumn(name = "owner_id", referencedColumnName = "id")
})
public Parent getOwner() {
return owner;
}
#MapsId("target")
#ManyToOne(fetch = FetchType.LAZY)
#PrimaryKeyJoinColumns({
#PrimaryKeyJoinColumn(name = "target_oid", referencedColumnName = "oid"),
#PrimaryKeyJoinColumn(name = "target_id", referencedColumnName = "id")
})
public Parent getTarget() {
return target;
}
#Id
#Column(name = "owner_id")
public Long getOwnerId() {
if (ownerId == null && owner != null) {
ownerId = owner.getId();
}
return ownerId;
}
#Id
#Column(name = "owner_oid", length = 36)
public String getOwnerOid() {
if (ownerOid == null && owner != null) {
ownerOid = owner.getOid();
}
return ownerOid;
}
#Id
#Column(name = "target_id")
public Long getTargetId() {
if (targetId == null && target != null) {
targetId = target.getId();
}
return targetId;
}
#Id
#Column(name = "target_oid", length = 36)
public String getTargetOid() {
if (targetOid == null && target != null) {
targetOid = target.getOid();
}
if (targetOid == null) {
targetOid = "";
}
return targetOid;
}
#Id
#Enumerated(EnumType.ORDINAL)
public ChildType getType() {
if (type == null) {
return ChildType.A;
}
return type;
}
}
ChildType.java
public enum ChildType {
A, B;
}
I also tried to use mappedBy approach mappedBy approach but there are still problems with loading - I can't tell hibernate which child record belogs to which child class member variable.
there are too many things I don't quite get in your solution to give a good answer but just some thoughts:
Consider using inheritance instead of ChildType enum. So you would have ChildA and ChildB extending Child.
That way you Parent can have:
private ChildA child1;
private ChildB child2;
Instead of having a composite primary key, I would consider using a unique auto generated key and then add a unique constraint on on the other id and oid fields. It should make the child parent relationships easier and you can have different parent implementation for ChildA and ChildB:
In ChildA:
#OneToOne(mappedBy="child1")
public Parent getParent() {
return parent;
}
And
In ChildB:
#OneToOne(mappedBy="child2")
public Parent getParent() {
return parent;
}
And in Child just:
public abstract Parent getParent();
Now the whole Parent/Owner/Target thing I still didn't quite grasp.
I see two problems with using one-to-one references:
Parent has zero or one child1, and zero or one child2. My understanding is that you use one to one when it's ALWAYS one.
Your IDs are getting really complicated. The idea with one-to-one is that the ID's are the same between the two entities, and you've got Parent with a two-part primary key, and Child with a different four part primary key.
The other addendum to 2. is that if you're using 1-1, then the ID of parent and child should be the same. But if the parent has two children, they can't both have the same ID! So there's a real data modeling problem. Also, the fact that child type is part of the key is also a bad smell, cause I expect child type is part of your business logic,
I think what you want is what Hibernate calls a 'unidirectional one-to-one association on a foreign key'. The Hibernate ORM manual shows how to do this in XML, here's an example with Annotations, although without the unique property set.
I'm going to assume there's no outside business reason why your primary keys have to be the way that they are, and suggest that you
Change Parent and Child to each have to have a single field primary key, using #Id and #GeneratedValue(strategy=GenerationType.AUTO).
Change Parent to have the references to child1 and child2 be #ManyToOne(unique=true)
If you want, change Child as detailed in the Hibernate manual so that it has a reference to the Parent.
You will lose the 'feature' that parents and children have the same ID, but gain massive simplicity in your object ID scheme, making database operations faster and the code simpler to read.
If there is an outside reason that you haven't specified as to why Parent and/or Child need multi-part primary keys, this answer will need some modification.
I also second the point that #barsju made about using inheritance, or even entirely independent classes, to do the two Child types. If you use ID's the way I lay out here, you can make Parent have references to the concrete subtypes, and the query will work fine.
Use #JoinColumns and define a filter
In Parent.java:
#OneToOne(optional = true, fetch = FetchType.EAGER)
#JoinColumns ({
#JoinColumn(name="id", referencedColumnName = "owner_id"),
#JoinColumn(name="oid", referencedColumnName = "owner_oid"),
})
#FilterJoinTable(name="type", condition="type = 'A'")
#Cascade({org.hibernate.annotations.CascadeType.ALL})
public Child getChild1() {
return child1;
}
#OneToOne(optional = true, fetch = FetchType.EAGER)
#JoinColumns ({
#JoinColumn(name="id", referencedColumnName = "owner_id"),
#JoinColumn(name="oid", referencedColumnName = "owner_oid"),
})
#FilterJoinTable(name="type", condition="type = 'B'")
#Cascade({org.hibernate.annotations.CascadeType.ALL})
public Child getChild2() {
return child2;
}
I never used filters, so the exact syntax may be a little bit different, but that should give you the idea how it should work.
By the way, you can make the design easier if you introduce a single id column (not a composite id as you do it now). In that case the type don't need to be part of the id. Hibernate does not really get happy with composite ids. Nevertheless composite ids work and they don't have anything to do with your problem. The filter should work in the same way then.

Categories

Resources