Change the way hibernate looks up related data? - java

Let's say I have the following "parent" pojo...
#Entity
#Table(name = "parent")
public class Parent{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#OneToMany(fetch=FetchType.LAZY, mappedBy = "parent", cascade = {CascadeType.ALL})
#JsonIgnoreProperties("parent")
List<Child> children;
}
and I have the following child POJO :
#Entity
#Table(name = "child")
public class Child{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne
#JoinColumn(name="parent_id")
private Parent parent;
}
The result of this will be that I have two tables, and my jpa repository will perform queries using the parent_id field within the child table.
However, What if I want it be like a lookup, where by there is a third table for the relationship, where I have the child id and the parent id as a row, and that would be the relationship? can I modify my spring - jpa / hibernate setup for that? If so, some help would be appreciated!

Related

Retrieving related objects in spring boot using hibernate?

I have this "Parent" class :
#Entity
#Table(name = "parent_table")
public class Parent {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String firstName;
private String lastName;
#OneToMany(fetch=FetchType.LAZY, mappedBy = "parent", cascade = {CascadeType.ALL})
List<Child> children;
}
And, I have the following child class :
#Entity
#Table(name = "children")
public class Child {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long childId;
#ManyToOne
#JoinColumn(name="parent_id")
private Parent parent;
private String name;
}
I am also sending this as my request body :
{
"firstName": "Test",
"lastName": "Parent",
"children":[{
"name":"jack"
},
{
"name":"jill"
}
]
}
The good news is that is that I can write these children to the database directly from the parent repository, however... when I do my GET, to get a parent, it comes back without the child entities (even though they are in the database, in their table)
SEMI-USEFUL UPDATE (MAYBE?) : I have noticed that the parent_id field in the database for the child records doesn't seem to be getting populated! No idea why!
You are creating the relationship with the wrong column names.
Your parent class has id column defined as:
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
That will generate a column id in your parent table.
Your child class has foreign key defined as:
#ManyToOne
#JoinColumn(name="parent_id")
private Parent parent;
You indicate to look for parent_id column within parent table to make a relationship. Such column does not exist.
If you wish to indicate foreign key column name must be parent_id you have to define it as:
#ManyToOne
#Column(name = "parent_id") // Your child field
#JoinColumn(name="id") // Your parent id field
private Parent parent;

Spring-boot JPA entity OneToOne adding new child with relation to parent

I cannot figure out how to simply relate a child entity to and existing parent.
#Entity
#Table(name = "parent")
#Document(indexName = "parent")
public class Parent implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator")
#SequenceGenerator(name = "sequenceGenerator")
private Long id;
#Column(name = "name")
private String name;
#OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(unique = true)
private Child child;
//getters, setters
}
Child
#Entity
#Table(name = "child")
#Document(indexName = "child")
public class Child implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator")
#SequenceGenerator(name = "sequenceGenerator")
private Long id;
#Column(name = "name")
private String name;
#OneToOne(mappedBy = "child")
private Parent parent;
//getters, setters
}
These are the two basic models.
The parent already exists within the database, and I want to add a new child in relation.
Child childEntity = childRepository.save(child);
The child is populated as follows:
child.json
{
"name": "smallChild",
"parent": { "id" : "1" }
}
I want to be able to save the child, and have it automatically have a relation to the parent.
I did some really nasty code...
Save the Child without a parent for the ID
Query the database for the parent by ID
Set the child to the parent entity
Save the Parent with the new child
Set the Parent entity TO the child entity
Resave the child with the parent.
This ended up being 6-ish database queries.
I tried watching a few course videos from lynda.com, but it didn't help.
Thanks!
Either the Parent Primary Key or the complete entity is required.
If the parent's ID available, then the extra query for fetching parent object is not required if the goal is to just save.
If you have the parent's Id available with you then you can save the child entity as:
Child child = new Child();
//...
//Setters for child
//...
//Now just create a parent object and set the id to it
Parent p = new Parent();
p.SetId(parentId); // as the parentId is already vailable
child.setParent(p);
Child childEntity = childRepository.save(child);

#ManyToOne not filling the parent id field

I have a many-to-one bidirectionnal relationship between a parent and child entity. The problem is, when i'm persisting the child, the parent_id isn't persisted. The other fields are fine, but parent_id stay at NULL. I'm using Spring Data JPA with Hibernate and mapstruct to convert between entity and dto, if that can help.
The java files are the following :
#Entity
#Table(name = "Parent")
public class ParentEntity implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#OneToMany (fetch = FetchType.LAZY, mappedBy="parent_entity", cascade = { CascadeType.ALL })
private List<Child> children;
}
#Entity
#Table(name = "Child")
public class ChildEntity implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#ManyToOne(optional = true)
#JoinColumn(nullable = true, name = "parent_entity_id", insertable = false, updatable = false)
private Parent parent_entity;
}
I already tried a bunch of answers from SO, but to no avail for now.
Since you're using bidirectional relationship, you are now responsible to set a parent for your child. And remove insertable = false.
As far as I know. You have to add each other in both parent and child. Only adding children to the list does not seem to work.
Try it out, and see if that fixes the problem.

Why does setting ManyToOne parent early causes flush to fail?

I am creating a JPA entity with a ManyToOne relationship. Why does having child.setParent(parent); cause the following fail during flush():
org.apache.openjpa.persistence.ArgumentException: Missing field for property "parent_id" in type "class test.ChildPrimaryKey".
The test code that fails:
Parent parent = new Parent();
Child child = new Child();
ChildPrimaryKey childPrimaryKey = new ChildPrimaryKey();
childPrimaryKey.setLookupId(1);
child.setId(childPrimaryKey);
child.setParent(parent); // <-- FAIL because of this
// Begin transaction
entityManager.clear();
entityManager.getTransaction().begin();
LOGGER.info("Persisting parent without child.");
entityManager.persist(parent);
LOGGER.info("Updating parent with child.");
childPrimaryKey.setParentId(parent.getId());
parent.getChildren().add(child);
entityManager.merge(parent);
// Fail happens at flush
entityManager.flush();
Entities:
#Embeddable
public class ChildPrimaryKey {
#Column(name = "lookup_id")
private int lookupId;
#Column(name = "parent_id")
private long parentId;
}
#Entity
#Table(name = "child")
public class Child {
#EmbeddedId
private ChildPrimaryKey id;
#MapsId("parent_id")
#ManyToOne
private Parent parent;
}
#Entity
#Table(name = "parent")
public class Parent {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private long id;
#OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private List<Child> children;
}
If I remove child.setParent(parent); statement, then my code passes. Using OpenJPA 2.2.2
I beleive your MapsId should be #MapsId("parentId") based on the class structure you've presented. The value of MapsId is an attribute, not a column name.

JPA: unidirectional many-to-one and cascading delete

Say I have a unidirectional #ManyToOne relationship like the following:
#Entity
public class Parent implements Serializable {
#Id
#GeneratedValue
private long id;
}
#Entity
public class Child implements Serializable {
#Id
#GeneratedValue
private long id;
#ManyToOne
#JoinColumn
private Parent parent;
}
If I have a parent P and children C1...Cn referencing back to P, is there a clean and pretty way in JPA to automatically remove the children C1...Cn when P is removed (i.e. entityManager.remove(P))?
What I'm looking for is a functionality similar to ON DELETE CASCADE in SQL.
If you are using hibernate as your JPA provider you can use the annotation #OnDelete. This annotation will add to the relation the trigger ON DELETE CASCADE, which delegates the deletion of the children to the database.
Example:
public class Parent {
#Id
private long id;
}
public class Child {
#Id
private long id;
#ManyToOne
#OnDelete(action = OnDeleteAction.CASCADE)
private Parent parent;
}
With this solution a unidirectional relationship from the child to the parent is enough to automatically remove all children. This solution does not need any listeners etc. Also a JPQL query like DELETE FROM Parent WHERE id = 1 will remove the children.
Relationships in JPA are always unidirectional, unless you associate the parent with the child in both directions. Cascading REMOVE operations from the parent to the child will require a relation from the parent to the child (not just the opposite).
You'll therefore need to do this:
Either, change the unidirectional #ManyToOne relationship to a bi-directional #ManyToOne, or a unidirectional #OneToMany. You can then cascade REMOVE operations so that EntityManager.remove will remove the parent and the children. You can also specify orphanRemoval as true, to delete any orphaned children when the child entity in the parent collection is set to null, i.e. remove the child when it is not present in any parent's collection.
Or, specify the foreign key constraint in the child table as ON DELETE CASCADE. You'll need to invoke EntityManager.clear() after calling EntityManager.remove(parent) as the persistence context needs to be refreshed - the child entities are not supposed to exist in the persistence context after they've been deleted in the database.
Create a bi-directional relationship, like this:
#Entity
public class Parent implements Serializable {
#Id
#GeneratedValue
private long id;
#OneToMany(mappedBy = "parent", cascade = CascadeType.REMOVE)
private Set<Child> children;
}
I have seen in unidirectional #ManytoOne, delete don't work as expected.
When parent is deleted, ideally child should also be deleted, but only parent is deleted and child is NOT deleted and is left as orphan
Technology used are Spring Boot/Spring Data JPA/Hibernate
Sprint Boot : 2.1.2.RELEASE
Spring Data JPA/Hibernate is used to delete row .eg
parentRepository.delete(parent)
ParentRepository extends standard CRUD repository as shown below
ParentRepository extends CrudRepository<T, ID>
Following are my entity class
#Entity(name = “child”)
public class Child {
#Id
#GeneratedValue
private long id;
#ManyToOne( fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = “parent_id", nullable = false)
#OnDelete(action = OnDeleteAction.CASCADE)
private Parent parent;
}
#Entity(name = “parent”)
public class Parent {
#Id
#GeneratedValue
private long id;
#Column(nullable = false, length = 50)
private String firstName;
}
Use this way to delete only one side
#ManyToOne(cascade=CascadeType.PERSIST, fetch = FetchType.LAZY)
// #JoinColumn(name = "qid")
#JoinColumn(name = "qid", referencedColumnName = "qid", foreignKey = #ForeignKey(name = "qid"), nullable = false)
// #JsonIgnore
#JsonBackReference
private QueueGroup queueGroup;
#Cascade(org.hibernate.annotations.CascadeType.DELETE_ORPHAN)
Given annotation worked for me. Can have a try
For Example :-
public class Parent{
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name="cct_id")
private Integer cct_id;
#OneToMany(cascade=CascadeType.REMOVE, fetch=FetchType.EAGER,mappedBy="clinicalCareTeam", orphanRemoval=true)
#Cascade(org.hibernate.annotations.CascadeType.DELETE_ORPHAN)
private List<Child> childs;
}
public class Child{
#ManyToOne(fetch=FetchType.EAGER)
#JoinColumn(name="cct_id")
private Parent parent;
}
You don't need to use bi-directional association instead of your code, you have just to add CascaType.Remove as a property to ManyToOne annotation, then use #OnDelete(action = OnDeleteAction.CASCADE), it's works fine for me.

Categories