Why there is no mappedBy attribute present in #ManyToOne - java

I am a JPA newbie and trying to understand #JoinTable annotation for Bidirectional OneToMany relationship b/w Project and Task Entities where Project can have multiple tasks.
I can use #JoinTable with Entity having #ManyToOne annotation, but when I am placing #JoinColumn on the other Entity having #OneToMany, I am not getting an option to specify "mappedBy" attribute on #ManyToOne annotation.
I would like to know why ?
I have tried placing #JoinTable annotation on both the entities but then Hibernate is itrying to insert two records in Join table
Project Entity :-
#Entity
#Data
public class Project {
#Id
#Column(name = "project_pk")
#GeneratedValue
private Long id;
#Column(name = "project_name")
private String name;
#OneToMany(mappedBy = "project", cascade = CascadeType.ALL)
List<Task> tasks;
}
Tasks Entity :-
#Entity
#Data
public class Task {
#Id
#GeneratedValue
#Column(name = "task_pk")
private Long id;
public Task() {
}
private String name;
#ManyToOne(cascade = CascadeType.ALL)
#JoinTable(name = "project_related_tasks",
inverseJoinColumns = #JoinColumn(name = "project_id", referencedColumnName = "project_pk"),
joinColumns = #JoinColumn(name = "task_id", referencedColumnName = "task_pk")
)
private Project project;
public Task(String name) {
this.name = name;
}
}

There are two ways to implement one-to-many relations:
Using a join table
Using a foreign key on the many-to-one side
mappedBy is used for the second way (using a foreign key). You don't have to specify mappedBy, if you want to use a join table.
Using a join table is not very good idea because you can't control that join table using Hibernate. For example you can't just add a record to a join table directly.
what is #JoinColumn and how it is used in Hibernate

Related

Hibernate - In a one-to-many relationship, a child loses references to the parent when updating

As in the title, when performing the update operation, the previous child loses the reference to the parent.
Parent side
#OneToMany(cascade =CascadeType.ALL)
#JoinColumn(name = "individual_id")
private List<ContactMedium> contactMedium;
Children side
#Entity
#Table(name = "contactMedium")
#Data
#AllArgsConstructor
#NoArgsConstructor
public class ContactMedium
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id ;
#ManyToOne
private Individual individual;
Patch operation
public Individual patch(Individual individual, Long id) {
Individual objectToSave = individual;
objectToSave.setId(id);
return individualRepository.save(objectToSave);
}
When updating, the previous property loses references to the child. How can I prevent this?
Your mappings seems wrong. Ideally they should be as below:
#Entity
#Table(name = "contactMedium")
#Data
#AllArgsConstructor
#NoArgsConstructor
public class ContactMedium
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id ;
#ManyToOne
#JoinColumn
private Individual individual;
and
#OneToMany(mappedBy = "individual", cascade = CascadeType.ALL)
private List<ContactMedium> contactMedium;
You need to save the ContactMedium and Individual will automatically be saved. Here ContactMedium has the foreign key reference to Individual (and that is what is depicted in your database table screenshot).
Often one use mappedBy as parameter to #OneToMany instead of #JoinColumn to make the relationship two-ways.
Can you please try to change
#OneToMany(cascade =CascadeType.ALL)
#JoinColumn(name = "individual_id")
private List<ContactMedium> contactMedium;
to
#OneToMany(mappedBy = "individual", cascade =CascadeType.ALL)
private List<ContactMedium> contactMedium;
and see if that worked better?
I think you must add the #OneToMany(mappedBy="individual" , cascade =CascadeType.PERSIST) and the #JoinColumn in the #ManyToOne as below:
#OneToMany(mappedBy = "individual", cascade =CascadeType.PERSIST)
private List<ContactMedium> contactMedium;
#ManyToOne
#JoinColumn(name = "individual_id")
private Individual individual;
You should retrieve the entity from the database using the ID first and then update the specific fields and persist the updated entity back.

Hibernate Cascade.All property doesn't add ON DELETE CASCADE on Postgres Foreign Key Constraint

I am using SpringBoot + PostgreSQL + JPA Hibernate, everything seems to working fine however the Cascade.ALL property is not applied on the table user_roles. Am i missing anything important. I have tried ManytoMany relationship and ManytoOne but no luck yet.
The entity files I am using are as follows.
User.java
#Entity
#Table(name = "USERS")
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class User {
#Id
#SequenceGenerator( name = "lineupSeq", sequenceName = "lineup_seq", allocationSize = 20, initialValue = 50 )
#GeneratedValue( strategy = GenerationType.SEQUENCE, generator = "lineupSeq" )
#Column(name="id")
private Long id;
#Column(unique=true)
private String username;
#Column
#JsonIgnore
private String password;
#Column(name="prodcrud")
private String prodcrud;
#Column(name="devcrud")
private String devcrud;
#Column(name="testcrud")
private String testcrud;
#ManyToOne(fetch = FetchType.EAGER, cascade=CascadeType.ALL)
#JoinTable(name = "user_roles",
joinColumns = {#JoinColumn(name = "user_id", referencedColumnName="id") },
inverseJoinColumns = {#JoinColumn(name = "role_id", referencedColumnName="id") })
private Role role;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
Screenshot of postgres constraint added
Postgres constraint added
Yes, you simply expect this annotation to do something it doesn't do.
Cascade.ALL tells JPA that every operation (persist, merge, delete, etc.) done, at runtime, using JPA, on a User must also be applied on its role.
Note that it doesn't make sense: since it's a ManyToOne, trying to delete the role of a user when deleting the user will lead to broken foreign key constraints, since the same role is also referenced by other users.
Read the documentation. Don't guess what annotations do.

Get id's of a ManyToMany mapping table

I'm writing an API using Spring Boot and Hibernate where my persisted entity objects are also used as DTOs sent to and from the client. This is a simplified version of a typical entity I use:
#Entity
#Table(name = "STUDENT")
public class Student {
#Id
#GeneratedValue
#Column(name = "ID")
private Long id;
#ElementCollection
#CollectionTable(name = "GROUP_STUDENT",
joinColumns = #JoinColumn(name = "GROUP_ID"))
#Column(name="STUDENT_ID")
private Set<Long> groupIds;
#JsonIgnore
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name="GROUP_STUDENT",
joinColumns = #JoinColumn(name="GROUP_ID"),
inverseJoinColumns = #JoinColumn(name="STUDENT_ID")
)
private Set<Group> groups = new HashSet<>();
// getters and setters
}
and this is the associated class:
#Entity
#Table(name = "GROUP")
public class Group {
#Id
#GeneratedValue
#Column(name = "ID")
private Long id;
#JsonIgnore
#ManyToMany(fetch = FetchType.LAZY, mappedBy = "groups")
private Set<Student> students = new HashSet<>();
// getters and setters
}
As you can see, there is a #ManyToMany association between Student and Group.
Since I send objects like these to the client, I choose to send only the id's of the associations and not the associations themselves. I've solved this using this answer and it works as expected.
The problem is this. When hibernate tries to persist a Student object, it inserts the groups as expected, but it also tries to insert the groupIds into the mapping table GROUP_STUDENT. This will of course fail because of the unique constraint of the mapping table composite id. And it isn't possible to mark the groupIds as insertable = false since it is an #ElementCollection. And I don't think I can use #Formula since I require a Set and not a reduced value.
This can of course be solved by always emptying either the groups of the groupIds before saving or persisting such an entity, but this is extremely risky and easy to forget.
So what I want is basically a read only groupIds in the Student class that loads the data from the GROUP_STUDENT mapping table. Is this possible? I'm grateful for any suggestions and glad to ellaborate on the question if it seems unclear.
I've managed to solve this by making the id-collection #Transient and populating it using #PostLoad:
#Entity
#Table(name = "STUDENT")
public class Student {
#PostLoad
private void postLoad() {
groupIds = groups.stream().map(Group::getId).collect(Collectors.toSet());
}
#Id
#GeneratedValue
#Column(name = "ID")
private Long id;
#Transient
private Set<Long> groupIds;
#JsonIgnore
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name="GROUP_STUDENT",
joinColumns = #JoinColumn(name="GROUP_ID"),
inverseJoinColumns = #JoinColumn(name="STUDENT_ID")
)
private Set<Group> groups = new HashSet<>();
// getters and setters
}

JPA2 One-To-Many Empty Join Table

I'm trying to create a one-to-many relationship between Publications and Authors, however for some reason when I persist the Publication, the Authors get persisted, but the Join table is empty.
Publication.java
#Id
#Column(name="PUBLICATIONID")
private String id;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "authorspublication", fetch = FetchType.EAGER)
private Collection<Author> authors;
Author.java
#Id
#Column(name = "AUTHORID")
private String authorid;
#ManyToOne(optional = false, targetEntity = Publication.class)
#JoinColumn(name = "authorspublication", referencedColumnName = "publicationid")
private Publication authorspublication;
DataParser.java
//pub is created - non managed
//author is created - non managed
author.setPublication(pub);
pub.getAuthors().add(author);
em.merge(pub);
I dont know if owning sides are backwards, or if its something else.
Any insight would be appreciated.
You are using #JoinColumn which indicates you are using a FK column in the authors table to link publications to their authors.
If you want to use a join table you need to remove this and use the #JoinTable annotation. However it seems to me that you have your mappings the wrong way round. Surely the relationship is OneToMany from Authors to Publications? For that matter surely an author can have many publications and a publication can have more than one author?
For the scenario posted however it should like:
public class Publication{
#Id
#Column(name="PUBLICATIONID")
private String id;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "publications", fetch = FetchType.EAGER)
private Collection<Author> authors;
}
public class Author{
#Id
#Column(name = "AUTHORID")
private String id;
#ManyToOne
#JoinTable(name = "author_publications", joinColumn = #JoinColumn(name = "AUTHORID"), inverseJoinColumn = #JoinColumn(name = "PUBLICATIONID"))
private Publication publications;
}
However you probably want to change the Publication field in Author to a be a Collection of Publications and replace the #OnetoMany with a #ManyToMany
You haven't defined it to use a #JoinTable; it is currently using a foreign key hence why no insert into the join table. You need to add
#JoinTable
to the one to many field (authors). Don't forget to remove the #JoinColumn since that is for FK relations

JPA CascadeType.REMOVE not working

I have two entities Business which is composed of a list of Departments
#Entity
#Table(name = "Business")
public class Business implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "Id")
private Long id;
#OneToMany(mappedBy = "business",
cascade = {CascadeType.PERSIST, CascadeType.REMOVE})
private List<Department> departments;
#OneToMany(mappedBy = "business", orphanRemoval = true,
cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REMOVE})
private List<Process> processs;
#ManyToMany
private List<Competence> competences;
}
#Entity
#Table(name = "Department")
public class Department implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#OneToMany(mappedBy = "father",
cascade = {CascadeType.PERSIST, CascadeType.REMOVE})
private List<Department> departments;
}
When I try to remove a business instance I get a Mysql Exception
Cannot delete or update a parent row: a foreign key constraint fails (evac_java.Department, CONSTRAINT FK_Department_Business FOREIGN KEY (Business) REFERENCES Business (Id)):HY000 - null
Which means I can't delete the business instance because it has departments associated with it, but a department cannot exists by itself so I want to delete all business's departments when it gets removed. I thought I would achieve this by adding cascade = CascadeType.REMOVE to the #OneToMany annotation in the business entity, but it does not work.
I did a search on the net and I found a lot of questions similar to this one on stackoverflow but they all suggest the same: add cascade = CascadeType.REMOVE or CascadeType.ALL
So I'm wondering if I'm missing somethig.
I'm using Glassfish 4.1 and EclipseLink
I tried with
#OneToMany(mappedBy = "business", orphanRemoval = true)
private List<Department> departments;
on the business entity but it does not work either
Here's the method I'm using to remove entities which is declared in an abstract class
public void remove(T entity) {
getEntityManager().remove(getEntityManager().merge(entity));
}
JPA can only remove and cascade the remove over entities it knows about, and if you have not been maintaining both sides of this bidirectional relationship, issues like this will arise. If the collection of departments is empty, try an em.refresh() before the remove, forcing JPA to populate all relationships so that they can be correctly removed, though it is better to maintain both sides of the relationship as changes are made to avoid the database hit.

Categories

Resources