Java JPA #OneToMany needed to reciprocate #ManyToOne? - java

Create Table A (
ID varchar(8),
Primary Key(ID)
);
Create Table B (
ID varchar(8),
A_ID varchar(8),
Primary Key(ID),
Foreign Key(A_ID) References A(ID)
);
Given that I have created two tables using the SQL statements above, and I want to create Entity classes for them, for the class B, I have these member attributes:
#Id
#Column(name = "ID", nullable = false, length = 8)
private String id;
#JoinColumn(name = "A_ID", referencedColumnName = "ID", nullable = false)
#ManyToOne(optional = false)
private A AId;
In class A, do I need to reciprocate the many-to-one relationship?
#Id
#Column(name = "ID", nullable = false, length = 8)
private String id;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "AId")
private List<B> BList; //<-- Is this attribute necessary?
Is it a necessary or a good idea to have a reciprocal #OneToMany for the #ManyToOne? If I make the design decision to leave out the #OneToMany annotated attribute now, would that come back to bite me further along?

Is it a necessary or a good idea to have a reciprocal #OneToMany for the #ManyToOne?
No, it's not mandatory at all, it's a pure design decision. The whole question is... Do you want this (i.e. an uni-directional association):
Or this (i.e. a bi-directional association):
If you don't need to get Bs from A, then you can skip the bs attribute and the OneToMany on A side.
If I make the design decision to leave out the #OneToMany annotated attribute now, will come back to bite me further down.
No, and you can add it later if you discover that you need it.

They are optional. There is no need to add them to your model if you don't want to use them.
I'd sugguest to avoid the reverse mapping at all because such collections may become quite large and most persistance layers don't handle these very good. In many cases you'd have to deal with add/remove of already loaded/managed entities related to these collections yourself. So only add those if they really make things easier for you.

Sure not. Thats design decision between one vs two direction relationship. In most casses, a better choice is to have one direction relationship, especially if its a domain classes. doing this, your design will express better the mean of your domain.

Related

Returning any field instead of a fixed value in Hibernate Hash Code [duplicate]

After reading several articles, threads and making some research, now I am completely confused regarding to implementing a proper equals and hashCode method in my Spring Boot app.
For example, I have the following class:
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#Entity
public class Recipe {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(nullable = false)
private String title;
#Column(length = 100)
private String description;
#Column(nullable = false)
private Integer prepTime;
#Column(nullable = false)
private Integer cookTime;
#Column(nullable = false)
private Integer servings;
#Lob
#org.hibernate.annotations.Type(type = "org.hibernate.type.TextType")
#Column(nullable = false)
private String instructions;
#Column(nullable = false)
#Enumerated(value = EnumType.STRING)
private Difficulty difficulty;
#Column(nullable = false)
#Enumerated(value = EnumType.STRING)
private HealthLabel healthLabel;
#ManyToOne(optional = true, fetch = FetchType.LAZY)
#JoinColumn(name = "category_id", referencedColumnName = "id")
private Category category;
#OneToMany(mappedBy = "recipe", cascade = CascadeType.ALL, orphanRemoval = true)
private List<RecipeIngredient> recipeIngredients = new ArrayList<>();
}
I have trouble for these issues below and trying to implement equals and hashCode methods properly:
1) As far as I know, when there is a unique field that can be used to differentiate one person from the other e.g. email or username, then it is enough ONLY use these fields in equals and hashCode method. Is that true?
2) If there is not any unique field except from id, then should I add ALL the fields (id and the others) to equals and hashCode method implementation?
3) When using Hibernate and Data JPA, should I follow a different approach than other situation as there are 4 states transient, managed, removed, and detached in JPA lifecycle? I think id field should not be used in this situation as it is not present in transient mode? Right?
When implementing equals() and hashCode() methods:
If there is a unique field that can be used to differentiate one object from another, use only that field in the implementation.
If there is no unique field, use all the fields including the id in the implementation.
When using Hibernate and Data JPA, do not use the ID field in the implementation as it is not present in the transient state, instead use fields that are present in all states such as unique fields or all fields.
The problem with equals and hashCode is that their contract is broken for any mutable entity and with JPA, there aren't really any other.
Ignoring JPA for a moment, by definition the id of an entity defines its identity.
So it should be used for equals and hashCode.
But this requires the id to be unmodifiable in an entity, but JPA requires a no args constructor and a way to set all properties, including the id.
Probably the best way around this is to
use the id.
make sure that equals and hashCode is never ever used before the id is set, and the id is never changed afterwards.
Not changing the id after it is once set is normally not a problem, since the id shouldn't change from one value to another.
The problem is creation of new instances.
Again instances returned by JPA aren't a problem, because JPA will fully initialise them before returning them to you.
Creating fresh instances in your application is the problem.
Here you have the following options:
create the instance and immediately assign a id. UUIDs are perfect for this.
They can be generated easily and efficiently on the application server.
This could be done in a static factory method on the entity class.
The drawback is that UUIDs are a pain to work with for humans, since they are long and basically random.
They are also large and eat more memory in the database than a traditional sequence number.
But the use cases with so many rows that this actually is a problem are rare.
generate the id in the database as most people do, and make sure that your new entity gets saved immediately after creation.
This could be nicely done in a custom method in a repository.
But it does require that you set all required properties in one place, which often can be a problem.
Using some other attribute which is supposed to be immutable, like the account name or an email works only for very few entities in the first place and even for those the fact that it is immutable now doesn't mean it stays that way.
Instead of trying to avoid the pitfalls created by JPA you could alternatively rely on it.
JPA guarantees that for a given class and id only one instance is in a persistence context.
Therefore, as long as you only work within a single session/transaction with an entity and don't try to compare detachd entities, there is no need to implement equals and hashCode at all.
as you are already using lombok, you can use #Data annotation as well:
#Data
All together now: A shortcut for #ToString, #EqualsAndHashCode, #Getter on all fields, #Setter on all non-final fields, and #RequiredArgsConstructor!

JPA entity, identify Business key

Lets say I'm writing a JPA entity. Very simple one. It has 3 properties:
public class MenuItem {
#Id
#GeneratedValue
private Long menuItemId;
#Column(nullable = false)
private String name;
#Column(nullable = false)
private Type type;
#OneToOne(cascade = {CascadeType.ALL})
#JoinColumn(name = "colorId")
private Color color;
}
Now, lets say I need to override its equals/hashcode methods. According to this suggestion Implementing equals() and hashCode() I have to use some sort of "Business key equality". But I have doubts, what should I consider as a "Business" key here. Does it have to be all the properties except of ID, or Color can be excluded from it. Just seems unclear to me. Would be thankful if anyone can explain more on this topic.
The last statement in the reference is pure gold:
Note that a business key does not have to be as solid as a database
primary key candidate. Immutable or unique properties are usually good
candidates for a business key.
So in your case name and type would be good candidates. Assuming the Type would be an immutable object (you could still get away without that but you might experience some hard to detect bugs).
IF you would put a unique constraint on color then i would consider that in the equals / hashCode method also.

Hibernate defining table in #Entity

I'm new in Hibernate. What is the difference between
#Id
#GeneratedValue
private Integer id;
and
#Id
#GeneratedValue
#Column(name="id", unique=true, nullable=false, etc)
private Integer id;
What is the reason of defining this if we already did it creating database? Does Hibernate warn us when we break these constraints (not in this example since it's auto generated value)? Does it create table when there isn't one?
The only reason is if the column is named differently, for example in a USER table, if the id column is called USER_ID, you might want to map that to a User object, on the field id.
The unique and nullable attributes are not used, as PK are unique and non-null. Read the javadoc of #Column to understand what else you can define there.
As you mentioned, if you create your DB from scripts (it's actually a bad practice to create your production DB from the JPA annotations), the only relevant attribute is the name... the other ones are not used for #Id columns or are used to autogenerate the DDL.
A slight word of caution, sometimes the attribute nullable influences on how updates are ordered. As a good practice, I always set it to false on mandatory associations (as otherwise Hibernate might decide clear the association and set the column to null... and then do another update with the new ID.).

How to model many-to-many relationship

How do I implement the following relationship using JPA?
table person (
id int,
name text
)
table person_home (
person_id int,
home_id int,
type char(1) -- 'p' = primary, 's' = secondary
)
table home (
id int,
address text
)
A person can have many homes, and a home can have many persons living in it (i.e. ManyToMany relationship).
Furthermore, a home can be a primary residence for one person, but a secondary residence for another person at the same time.
I'm not sure how to model this relationship, even though the database schema is clear.
I have thought of splitting mapping table person_home into person_primary_home and person_secondary_home, however I would prefer to retain the schema if possible.
This question is pretty much asked and answered here:
How to create a composite primary key which contains a #ManyToOne attribute as an #EmbeddedId in JPA?
You need four classes:
Person.java
Home.java
PersonHome.java
PersonHomePk.java
The Person.java and Home.java files you create with one to many relations to the PersonHome.java. They'll have #Id fields to identify the primary keys. Each will have a #OneToMany relation defined with at least a mappedBy attribute mapping to their respective fields in the PersonHome entity. i.e. in the Person.java you could have something like
#OneToMany(cascade = CascadeType.ALL, mappedBy = "Person")
private Collection<PersonHome> personHome;
The PersonHome.java will have an #EmbeddedId field to identify the PersonHomePk instance declaration which is its primary key (that is, instead of an #Id column you will have an #EmbeddedId annotating a declaration of a class representing the primary key of the join table PersonHome). Any other fields are declared as normal columns. The PersonHome.java will also declare two ManyToOne relations one each to person and home. These will use #JoinColumn annotation (make sure they have the attributes insertable=false and updatable=false). The datatypes will be the Person and Home classes. i.e.
#EmbeddedId
protected PersonHomePk personHomePk;
#Column (name = "type")
private String type;
#JoinColumn(name = "person_id", referencedColumnName = "person_id", insertable = false, updatable = false)
#ManyToOne(optional = false)
private Person person;
You'll need the same for the "Home" declaration too. Why are you using only a char for "type". I'd recommend a varchar so people who maintain the thing once you're gone will understand the code and database better when you aren't around. 'detached' is easier to understand the 'd'.
I believe if you're going to have metadata besides the relationship on the person_home table, you need to use three objects with two one-to-many relationships in order to be able to access all of the data.
You could eliminate this need by having two many-to-one relationships from the person table to the home table, by having primary_home_id and secondary_home_id -- unless I'm missing a requirement here and a person can have more than one primary or secondary home.

Difference Between One-to-Many, Many-to-One and Many-to-Many?

Ok so this is probably a trivial question but I'm having trouble visualizing and understanding the differences and when to use each. I'm also a little unclear as to how concepts like uni-directional and bi-directional mappings affect the one-to-many/many-to-many relationships. I'm using Hibernate right now so any explanation that's ORM related will be helpful.
As an example let's say I have the following set-up:
public class Person {
private Long personId;
private Set<Skill> skills;
//Getters and setters
}
public class Skill {
private Long skillId;
private String skillName;
//Getters and setters
}
So in this case what kind of mapping would I have? Answers to this specific example are definitely appreciated but I would also really like an overview of when to use either one-to-many and many-to-many and when to use a join table versus a join column and unidirectional versus bidirectional.
Looks like everyone is answering One-to-many vs. Many-to-many:
The difference between One-to-many, Many-to-one and Many-to-Many is:
One-to-many vs Many-to-one is a matter of perspective. Unidirectional vs Bidirectional will not affect the mapping but will make difference on how you can access your data.
In Many-to-one the many side will keep reference of the one side. A good example is "A State has Cities". In this case State is the one side and City is the many side. There will be a column state_id in the table cities.
In unidirectional, Person class will have List<Skill> skills but
Skill will not have Person person. In bidirectional, both
properties are added and it allows you to access a Person given a
skill( i.e. skill.person).
In One-to-Many the one side will be our point of reference. For example, "A User has Addresses". In this case we might have three columns address_1_id, address_2_id and address_3_id or a look up table with multi column unique constraint on user_id
on address_id.
In unidirectional, a User will have Address address. Bidirectional
will have an additional List<User> users in the Address class.
In Many-to-Many members of each party can hold reference to arbitrary number of members of the other party. To achieve this a look up table is used. Example for this is the relationship between doctors and patients. A doctor can have many patients and vice versa.
One-to-Many: One Person Has Many Skills, a Skill is not reused between Person(s)
Unidirectional: A Person can directly reference Skills via its Set
Bidirectional: Each "child" Skill has a single pointer back up to the
Person (which is not shown in your code)
Many-to-Many: One Person Has Many Skills, a Skill is reused between Person(s)
Unidirectional: A Person can directly reference Skills via its Set
Bidirectional: A Skill has a Set of Person(s) which relate to it.
In a One-To-Many relationship, one object is the "parent" and one is the "child". The parent controls the existence of the child. In a Many-To-Many, the existence of either type is dependent on something outside the both of them (in the larger application context).
Your subject matter (domain) should dictate whether or not the relationship is One-To-Many or Many-To-Many -- however, I find that making the relationship unidirectional or bidirectional is an engineering decision that trades off memory, processing, performance, etc.
What can be confusing is that a Many-To-Many Bidirectional relationship does not need to be symmetric! That is, a bunch of People could point to a skill, but the skill need not relate back to just those people. Typically it would, but such symmetry is not a requirement. Take love, for example -- it is bi-directional ("I-Love", "Loves-Me"), but often asymmetric ("I love her, but she doesn't love me")!
All of these are well supported by Hibernate and JPA. Just remember that Hibernate or any other ORM doesn't give a hoot about maintaining symmetry when managing bi-directional many-to-many relationships...thats all up to the application.
1) The circles are Entities/POJOs/Beans
2) deg is an abbreviation for degree as in graphs (number of edges)
PK=Primary key, FK=Foreign key
Note the contradiction between the degree and the name of the side. Many corresponds to degree=1 while One corresponds to degree >1.
One-to-many
The one-to-many table relationship looks like this:
In a relational database system, a one-to-many table relationship associates two tables based on a Foreign Key column in the child table referencing the Primary Key of one record in the parent table.
In the table diagram above, the post_id column in the post_comment table has a Foreign Key relationship with the post table id Primary Key column:
ALTER TABLE
post_comment
ADD CONSTRAINT
fk_post_comment_post_id
FOREIGN KEY (post_id) REFERENCES post
#ManyToOne annotation
In JPA, the best way to map the one-to-many table relationship is to use the #ManyToOne annotation.
In our case, the PostComment child entity maps the post_id Foreign Key column using the #ManyToOne annotation:
#Entity(name = "PostComment")
#Table(name = "post_comment")
public class PostComment {
#Id
#GeneratedValue
private Long id;
private String review;
#ManyToOne(fetch = FetchType.LAZY)
private Post post;
}
Using the JPA #OneToMany annotation
Just because you have the option of using the #OneToMany annotation, it doesn't mean it should be the default option for all the one-to-many database relationships.
The problem with JPA collections is that we can only use them when their element count is rather low.
The best way to map a #OneToMany association is to rely on the #ManyToOne side to propagate all entity state changes:
#Entity(name = "Post")
#Table(name = "post")
public class Post {
#Id
#GeneratedValue
private Long id;
private String title;
#OneToMany(
mappedBy = "post",
cascade = CascadeType.ALL,
orphanRemoval = true
)
private List<PostComment> comments = new ArrayList<>();
//Constructors, getters and setters removed for brevity
public void addComment(PostComment comment) {
comments.add(comment);
comment.setPost(this);
}
public void removeComment(PostComment comment) {
comments.remove(comment);
comment.setPost(null);
}
}
The parent Post entity features two utility methods (e.g. addComment and removeComment) which are used to synchronize both sides of the bidirectional association.
You should provide these methods whenever you are working with a bidirectional association as, otherwise, you risk very subtle state propagation issues.
The unidirectional #OneToMany association is to be avoided as it's less efficient than using #ManyToOne or the bidirectional #OneToMany association.
One-to-one
The one-to-one table relationship looks as follows:
In a relational database system, a one-to-one table relationship links two tables based on a Primary Key column in the child which is also a Foreign Key referencing the Primary Key of the parent table row.
Therefore, we can say that the child table shares the Primary Key with the parent table.
In the table diagram above, the id column in the post_details table has also a Foreign Key relationship with the post table id Primary Key column:
ALTER TABLE
post_details
ADD CONSTRAINT
fk_post_details_id
FOREIGN KEY (id) REFERENCES post
Using the JPA #OneToOne with #MapsId annotations
The best way to map a #OneToOne relationship is to use #MapsId. This way, you don't even need a bidirectional association since you can always fetch the PostDetails entity by using the Post entity identifier.
The mapping looks like this:
#Entity(name = "PostDetails")
#Table(name = "post_details")
public class PostDetails {
#Id
private Long id;
#Column(name = "created_on")
private Date createdOn;
#Column(name = "created_by")
private String createdBy;
#OneToOne(fetch = FetchType.LAZY)
#MapsId
#JoinColumn(name = "id")
private Post post;
public PostDetails() {}
public PostDetails(String createdBy) {
createdOn = new Date();
this.createdBy = createdBy;
}
//Getters and setters omitted for brevity
}
This way, the id property serves as both Primary Key and Foreign Key. You'll notice that the #Id column no longer uses a #GeneratedValue annotation since the identifier is populated with the identifier of the post association.
Many-to-many
The many-to-many table relationship looks as follows:
In a relational database system, a many-to-many table relationship links two parent tables via a child table which contains two Foreign Key columns referencing the Primary Key columns of the two parent tables.
In the table diagram above, the post_id column in the post_tag table has also a Foreign Key relationship with the post table id Primary Key column:
ALTER TABLE
post_tag
ADD CONSTRAINT
fk_post_tag_post_id
FOREIGN KEY (post_id) REFERENCES post
And, the tag_id column in the post_tag table has a Foreign Key relationship with the tag table id Primary Key column:
ALTER TABLE
post_tag
ADD CONSTRAINT
fk_post_tag_tag_id
FOREIGN KEY (tag_id) REFERENCES tag
Using the JPA #ManyToMany mapping
This is how you can map the many-to-many table relationship with JPA and Hibernate:
#Entity(name = "Post")
#Table(name = "post")
public class Post {
#Id
#GeneratedValue
private Long id;
private String title;
#ManyToMany(cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
})
#JoinTable(name = "post_tag",
joinColumns = #JoinColumn(name = "post_id"),
inverseJoinColumns = #JoinColumn(name = "tag_id")
)
private Set<Tag> tags = new HashSet<>();
//Getters and setters ommitted for brevity
public void addTag(Tag tag) {
tags.add(tag);
tag.getPosts().add(this);
}
public void removeTag(Tag tag) {
tags.remove(tag);
tag.getPosts().remove(this);
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Post)) return false;
return id != null && id.equals(((Post) o).getId());
}
#Override
public int hashCode() {
return getClass().hashCode();
}
}
#Entity(name = "Tag")
#Table(name = "tag")
public class Tag {
#Id
#GeneratedValue
private Long id;
#NaturalId
private String name;
#ManyToMany(mappedBy = "tags")
private Set<Post> posts = new HashSet<>();
//Getters and setters ommitted for brevity
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Tag tag = (Tag) o;
return Objects.equals(name, tag.name);
}
#Override
public int hashCode() {
return Objects.hash(name);
}
}
The tags association in the Post entity only defines the PERSIST and MERGE cascade types. The REMOVE entity state transition doesn't make any sense for a #ManyToMany JPA association since it could trigger a chain deletion that would ultimately wipe both sides of the association.
The add/remove utility methods are mandatory if you use bidirectional associations so that you can make sure that both sides of the association are in sync.
The Post entity uses the entity identifier for equality since it lacks any unique business key. You can use the entity identifier for equality as long as you make sure that it stays consistent across all entity state transitions.
The Tag entity has a unique business key which is marked with the Hibernate-specific #NaturalId annotation. When that's the case, the unique business key is the best candidate for equality checks.
The mappedBy attribute of the posts association in the Tag entity marks that, in this bidirectional relationship, the Post entity owns the association. This is needed since only one side can own a relationship, and changes are only propagated to the database from this particular side.
The Set is to be preferred, as using a List with #ManyToMany is less efficient.
I would explain that way:
OneToOne - OneToOne relationship
#OneToOne
Person person;
#OneToOne
Nose nose;
OneToMany - ManyToOne relationship
#OneToMany
Shepherd shepherd;
#ManyToOne
List<Sheep> sheeps;
ManyToMany - ManyToMany relationship
#ManyToMany
List<Traveler> travelers;
#ManyToMany
List<Destination> destinations;
Take a look at this article: Mapping Object Relationships
There are two categories of object relationships that you need to be concerned with when mapping. The first category is based on multiplicity and it includes three types:
*One-to-one relationships. This is a relationship where the maximums of each of its multiplicities is one, an example of which is holds relationship between Employee and Position in Figure 11. An employee holds one and only one position and a position may be held by one employee (some positions go unfilled).
*One-to-many relationships. Also known as a many-to-one relationship, this occurs when the maximum of one multiplicity is one and the other is greater than one. An example is the works in relationship between Employee and Division. An employee works in one division and any given division has one or more employees working in it.
*Many-to-many relationships. This is a relationship where the maximum of both multiplicities is greater than one, an example of which is the assigned relationship between Employee and Task. An employee is assigned one or more tasks and each task is assigned to zero or more employees.
The second category is based on
directionality and it contains two
types, uni-directional relationships
and bi-directional relationships.
*Uni-directional relationships. A uni-directional relationship when an object knows about the object(s) it is related to but the other object(s) do not know of the original object. An example of which is the holds relationship between Employee and Position in Figure 11, indicated by the line with an open arrowhead on it. Employee objects know about the position that they hold, but Position objects do not know which employee holds it (there was no requirement to do so). As you will soon see, uni-directional relationships are easier to implement than bi-directional relationships.
*Bi-directional relationships. A bi-directional relationship exists when the objects on both end of the relationship know of each other, an example of which is the works in relationship between Employee and Division. Employee objects know what division they work in and Division objects know what employees work in them.
this would probably call for a many-to-many relation ship as follows
public class Person{
private Long personId;
#manytomany
private Set skills;
//Getters and setters
}
public class Skill{
private Long skillId;
private String skillName;
#manyToMany(MappedBy="skills,targetClass="Person")
private Set persons; // (people would not be a good convenion)
//Getters and setters
}
you may need to define a joinTable + JoinColumn but it will possible work also without...
First of all, read all the fine print. Note that NHibernate (thus, I assume, Hibernate as well) relational mapping has a funny correspondance with DB and object graph mapping. For example, one-to-one relationships are often implemented as a many-to-one relationship.
Second, before we can tell you how you should write your O/R map, we have to see your DB as well. In particular, can a single Skill be possesses by multiple people? If so, you have a many-to-many relationship; otherwise, it's many-to-one.
Third, I prefer not to implement many-to-many relationships directly, but instead model the "join table" in your domain model--i.e., treat it as an entity, like this:
class PersonSkill
{
Person person;
Skill skill;
}
Then do you see what you have? You have two one-to-many relationships. (In this case, Person may have a collection of PersonSkills, but would not have a collection of Skills.) However, some will prefer to use many-to-many relationship (between Person and Skill); this is controversial.
Fourth, if you do have bidirectional relationships (e.g., not only does Person have a collection of Skills, but also, Skill has a collection of Persons), NHibernate does not enforce bidirectionality in your BL for you; it only understands bidirectionality of the relationships for persistence purposes.
Fifth, many-to-one is much easier to use correctly in NHibernate (and I assume Hibernate) than one-to-many (collection mapping).
Good luck!

Categories

Resources