Hibernate mappedBy="table_name" is confusing one - java

I have two domain models as follows,
#Entity
#Table(name = "candidate") // lowercase-for-database-conventions
public class Candidate {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
.
.
.
#OneToOne(mappedBy="Candidate", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
//above here, mappedBy has Class name to map, as OO Design suggests.
private Ctc ctc;
}
When I try to run the application, it gives me this exception.
org.hibernate.AnnotationException: Unknown mappedBy in: com.hrsystem.model.Candidate.ctc, referenced property unknown: com.hrsystem.model.Ctc.Candidate
But if I put the value of mappedBy exactly as database-convention (i.e. lower case letters as in #Table(name="candidate") ), it works perfectly fine.
So my question is, why we should encourage database-convention driven development though we are using Object Oriented Design?
UPDATE---
Here is my Ctc.java entity.
#Entity
#Table(name = "ctc")
public class Ctc {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private double basic;
private double hra;
private double da;
#OneToOne
#JoinColumn(name = "candidate_id")
private Candidate candidate;
}
and getters and setters below it..!!

you do not put the table name in the mappedBy, you put the name of the reference to your object.
So in your case
#OneToOne(mappedBy="candidate", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
//above here, mappedBy has Class name to map, as OO Design suggests.
private Ctc ctc;
We would expect Ctc class to be something like that
public class Ctc {
//other properties
#OneToOne
#JoinColumn
private Candidate candidate

Related

How to use #JsonManagedReference and #JsonBackReference for Many to Many relationship with best practice?

I will be much apprecaite for you to explain my questions.
I have 2 entity and 1 bridge table entity,
Let's say they are Team, User and TeamUser.
TeamEntity:
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "TEAM_ID")
private Integer id;
#JsonManagedReference
#OneToMany(mappedBy = "team", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<TeamUsers> team_users = new HashSet<>();
UserEntity:
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#JsonBackReference
#OneToMany(mappedBy = "user")
private Set<TeamUsers> team_users = new HashSet<>();
TeamUserEntity(bridge table):
#EmbeddedId
private TeamUsersId id;
#ManyToOne
#MapsId("teamId")
#JoinColumn(name = "team_id")
#JsonBackReference
private Team team;
#ManyToOne
#MapsId("userId")
#JoinColumn(name = "user_id")
#JsonManagedReference
private User user;
#Column(name = "active")
private int active;
As you can see I used #JsonManagedReference and #JsonBackReference
to telling the program the direction for the query of the Entity and avoid infinite recrusive.
Now if I run get repo.findAll() on Team CRUDrepository I will get all Team object, and within the content I will get all bridge table data and it also include User details information.
But Let's say sometimes if I want to query the data in oppisite way, I want to get All User information and the object should contain all Team information, looks like the annotation #JsonManagedReference and #JsonBackReference block the result.
In real world development, how should we manage here?
The #JsonManagedReference and #JsonBackReference annotations are used to handle circular references. They can be used to create a JSON structure in a bidirectional way. The #JsonManagedReference annotation is used on a child reference of the target POJO, which means it is a forward reference that includes during the serialization process whereas #JsonBackReference annotation is a backreference that omits during the serialization process, usually used in the corresponding child class.
Hence, The property annotated with #JsonBackReference here, which is the Team in the TeamUsers, won't be serialized. That is why when you try to get all the users having Team inside the TeamUsers, it won't work. Also, if it did, it would violate the purpose of the annotations that they're used for, recursive access mapping.
If you want to fetch data in either way, you should use #JsonIgnoreProperties instead of those two annotations. Change your entity classes as follows and you'll get your desired output.
In Team class, set #JsonIgnoreProperties("team") on the team_users field to ignore mapping team inside this field again to avoid recursive mapping. Change your Team class to:
public class Team {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "TEAM_ID")
private Integer id;
#OneToMany(mappedBy = "team", cascade = CascadeType.ALL, orphanRemoval = true)
#JsonIgnoreProperties("team")
private Set<TeamUsers> team_users = new HashSet<>();
}
Similarly, in User class, set #JsonIgnoreProperties("user") on the team_users field to ignore mapping user inside this field again to avoid recursive mapping. Change your User class to:
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
#JsonIgnoreProperties("user")
private Set<TeamUsers> team_users = new HashSet<>();
}
And finally, in TeamUsers class, set #JsonIgnoreProperties("team_users") on both the team and user field to ignore mapping team_users inside these field again to avoid recursive mapping. Change your TeamUsers class to:
public class TeamUsers {
#EmbeddedId
private TeamUserId id;
#ManyToOne
#MapsId("teamId")
#JoinColumn(name = "team_id")
#JsonIgnoreProperties("team_users")
private Team team;
#ManyToOne
#MapsId("userId")
#JoinColumn(name = "user_id")
#JsonIgnoreProperties("team_users")
private User user;
#Column(name = "active")
private int active;
}
Now you can fetch the data in either way without having recursive mapping.

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.

JPA Hibernate Annotation Issue

I have three Entities i'm modeling and am having issues with the associated annotations. I basically have a class that I intend on returning to the caller, a nested listed of Project's and the Project can contain a nested list of Endpoint's. It's a top-level has-a one-to-many, then the nested one-to-many has two one-to-many's.
I've played with #JoinColumn annotations, i've attempted to put a #ManyToOne on the other side of the OneToMany's (but it doesn't like that it's a Long..). I'm just fairly new and unsure on how to do this. I think the mappedById is the solution, but i'm uncertain.
Main Issue: This code allows me to "save" to the database, but upon retrieval, the list of Project's inside the DownDetectorPackage is empty.
A CascadeType.ALL throws referential integrity errors that I don't completely understand.
#Data
#NoArgsConstructor
#AllArgsConstructor
#Entity
#Builder
public class DownDetectorPackage {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#OneToMany(mappedBy="id",fetch = FetchType.EAGER)
private List<Project> projects;
#Temporal(TemporalType.DATE)
private Date dateJobsLastRan;
#Entity
#Data
#NoArgsConstructor
#AllArgsConstructor
#Builder
public class Project{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String projectName;
#OneToMany(mappedBy="id")
private List<Service> externalDependencies;
#OneToMany(mappedBy="id")
private List<Service> endpoints;
}
#Entity
#Data
#NoArgsConstructor
#AllArgsConstructor
#Builder
public class Service {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String url;
private Boolean endpointIsUp;
private String serviceName;
}
You should be using #JoinColumn instead of mappedBy. MappedBy can be used when you have used #ManyToOne in the other class too, which you haven't.
So your final class should look something like this (this is applicable for the other classes too which you have mentioned) :
public class DownDetectorPackage {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#JoinColumn(name = "downDetectorPackageId")
#OneToMany(fetch = FetchType.EAGER)
private List<Project> projects;
#Temporal(TemporalType.DATE)
private Date dateJobsLastRan;
Also, remember to state the parent object name in #JoinColumn annotation, since it would create a column for that foreign key.
You should mark every join column as JoinColumn denotating the referenced column from the other entity. Then, you are supposed to say which relation type are using this column.
public class Project {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String projectName;
#JoinColumn(referencedColumnName = "id")
#OneToMany(fetch = FetchType.LAZY)
private ExternalDependencyEntity externalDependencies;
#JoinColumn(referencedColumnName = "id")
#OneToMany(fetch = FetchType.LAZY)
private EndpointEntity endpoints;
}
Finally, note that in a relational database, every fk column can takes only 1 value (pk of referenced entity id), so, on your entity, you should mark the data type as the entity you are refering to and no as a collection.
I think this sould solve your problem.

Found shared references to a collection: OneToMany empty collections refering to same PersistentBag

I've the following model : An Organization has a List<Contract>, each Contract may have a pricebookId and a collection of PricebookEntry. Multiple Contract entities can have the same pricebookId. The Collection<PricebookEntry> is the result of a #OneToMany association with the pricebookId as JoinColumn.
Here is the postulate: An organization has 2 contracts and both contracts have no pricebookId.
And here is my problem: During an Hibernate session where this particular Organization is involved, Hibernate throws this exception:
org.hibernate.HibernateException: Found shared references to a collection: Contract.pricebookEntries.
Indeed, both contracts have their pricebookEntries set to the same PersistentBag (same reference).
How can I fix this ?
Thank you for your help!
#Entity
public class Organization implements Serializable {
#Id
private String id;
#Fetch(FetchMode.SUBSELECT)
#OneToMany(fetch = FetchType.EAGER, mappedBy = "organization")
private final List<Contract> contracts = new ArrayList<>();
}
#Entity
public class Contract implements Serializable {
#Id
private String id;
#Column(name = "pricebook_id")
private String pricebookId;
#JoinColumn(name = "organization_id", referencedColumnName = "id")
#ManyToOne(fetch = FetchType.LAZY)
private Organization organization;
#JoinColumn(name="pricebookId", referencedColumnName = "pricebook_id")
#OneToMany(fetch = FetchType.EAGER)
#Fetch(FetchMode.SUBSELECT)
private final Collection<PricebookEntry> pricebookEntries = new ArrayList<>();
}
#Entity
public class PricebookEntry {
#Id
private String id;
private String pricebookId;
}
What you have here isn't a #OneToMany relationship, since multiple Contract entities may reference the same (set) of PricebookEntry instances.
To fix turn the Collection<PricebookEntry> into a proper entity Pricebook and have a many-to-one relationship from Contract to Pricebook.
Also lose the now redundant pricebookid since it is simply the id of the Pricebook.

What I need to write in JoinColumn?

Sorry for my question. Really can`t understand my mistake.
I have a Hibernate relations between two classes but when I run it logs give me this error:
org.hibernate.AnnotationException: mappedBy reference
an unknown target entity property: model.pilgi.Pilgi.PilgiDocument in model.pilgi_doc.PilgiDocument.pilgi
Code of the fisrt class PilgiDocument:
#Entity
#Table(name = "pilgi_document")
public class PilgiDocument {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column
private BigInteger pdocument_id;
#OneToMany(mappedBy = "PilgiDocument")
private List<Pilgi> pilgi = new ArrayList();
Pilgi class is here:
#Entity
#Table(name = "pilgi")
public class Pilgi {
#JoinColumn(name = "pilga")
#ManyToOne(fetch = FetchType.LAZY)
private PilgiDocument pdocument_id;
The error your have says that Hibernate doesn't find the PilgiDocument attribute in the class Pilgi: you don't have it but you have a pdocument_id property, that is an instance of PilgiDocument
In your PilgiDodument class, what you need to write is :
#Entity
#Table(name = "pilgi_document")
public class PilgiDocument {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column
private BigInteger pdocument_id;
#OneToMany(mappedBy = "pdocument_id")
private List<Pilgi> pilgi = new ArrayList();
You indicate the name of the attribute of your Pilgi class that represent the PilgiDocument attribute
In your Pilgi class, you need to write, in your #JoinColumn the name of the database column used as your foreign key for your PilgiDocument

Categories

Resources