Hibernate One To Many Mapping With Fixed Values - java

I don't have a lot of experience with database design and i try to understand the general logic behind it with using an ORM like hibernate. I have two tables user and languages. User could know one or more languages so there is a one to many relation between two tables. But i have a fixed length of languages English , Spanish and French for example. As i understand with each new user instance persisted there will be duplicate entries in the language table with a foreign key of that person. Is there a way to prevent this duplicate entries ?

Your understanding is a little confused. You can map a OneToMany relationship using a Foreign Key, and there are good database reasons to do so, though generally a JPA provider recommends against it. However, you are describing a ManyToMany relationship. A User will (or could) have many Languages. A Language will have many Users. When you create a many to many relationship with annotations:
#Entity
public class Person {
#Id #GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
#ManyToMany
private List<Language> languages;
and
#Entity
public class Language {
#Id #GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
The JPA provider will create an association or Join Table, with an id from each Entity in it:
create table Person_Language (Person_id bigint not null, languages_id bigint not null)
When you create a language, an entry will be put in the language table. When you create a user, an entry will be put into the user table. When you add a language to a person's languages then an entry will be put into the Join Table.
insert into Person_Language (Person_id, languages_id) values (?, ?)
There will be only unique combinations of Person_id and languages_id in the join table, and so the database will be well normalized. You would not be able to assign a language to multiple users using a Foreign Key in the Language entity for the reason you pointed out: there would be only one foreign key column for any given language.

Related

Hibernate: how to set relationship among three entity classes, their join table and save in database

I have following three entity classes.
#Entity
public class User {
#Id
#Column(nullable = false)
#GeneratedValue(strategy= GenerationType.AUTO)
private Integer id;
}
#Entity
public class LanguageProficiencyLevel {
#Id
#Column(nullable = false)
#GeneratedValue(strategy= GenerationType.AUTO)
private Integer id;
private String name; // A1, A2, B1 ... etc
}
#Entity
public class Language {
#Id
#Column(nullable = false)
#GeneratedValue(strategy= GenerationType.AUTO)
private Integer id;
private String name; //English, Chinese ect ...
}
Currently in the database, I have around 20 languages saved in Language table and 6 language proficiency levels A1, A2, B1, B2, C1, C2 saved in LanguageProficiencyLevel table.
Now I have the following relationship among the entity classes.
A User can know more than one languages with one proficiency level and A language with one proficiency level is known by many users.
So for example, A user may know English and his English proficiency may be C1, Again same user may also know Spanish and his Spanish proficiency may be B1.
Here I understand, User and Language has many to many relation. But I don't understand how to relate LanguageProficiencyLevel with User or Language.
Also how should I save this in database? My idea is to make one join table (LanguageSkill) with column names as user_id, language_id and languageProficiencyLevel_id and this table row will be inserted when a user is created. I am not sure if this the way to implement it. Please give me an idea how to do this and what should be the configuration for this.
User and Language will have many to many relation as you said. And, Language and LanguageProficiencyLevel will have one to one relation.
So, you have to create a mapping table that will have have many to one relation to all the 3 tables.
Refer this link to create mapping table with multiple columns.
Your relations between entity objects would be like:
A user can have multiple languages and language can have multiple users. So its a Many-to-Many relationship between User-Language.
A language can have only one proficiency at a time but a proficiency can have multiple languages. So LanguageProficiency to Language would be a one-to-many relation.
Relation between user and language proficiency is also many-to-many.
Here is a link how you can go about your database design for many-to-many relations.
How to implement a many-to-many relationship in PostgreSQL?
After creating the database design you can probably use some reverse engineering tool(https://www.javacodegeeks.com/2013/10/step-by-step-auto-code-generation-for-pojo-domain-java-classes-and-hbm-using-eclipse-hibernate-plugin.html) to create the hibernate pojo classes. I would recommend to use a tool to do this rather than taking things in hands to avoid unnecessary issues.
So now if you look carefully... your Entity classes of User, Language and LanguageProficiency would be something like this.
Hope this is useful.
You should absolutely create another table LanguageSkill, like you said.
Language and Proficiency are so-called base data - they will have comparatively few entries and will be independent of users. Neither of them should be mapped into User.
A User should then have a #OneToMany relation to LanguageSkill, which represents his knowledge of a particular language. LanguageSkill has a #ManyToOne to both Language and Proficiency (and User).
Skipping LanguageSkill would result in data duplication in your schema, or at least in a schema that is hard to read with all the jointables.
Also, it would mix concerns - data that is relatively stable (Language, Proficiency) and data that will change often (a person's knowledge of a language).

Should #JoinTable be specified in both sides of a #ManyToMany relationship?

I've an entity Course and an entity User. There's a many-to-many relation ship between course and user, since a course can have many users and a user can be enrolled in many courses. In both entities I've put the #ManyToMany annotation on the specific field, that is, in Course I have:
#ManyToMany
private List<RegisteredUser> members;
and in User I have:
#ManyToMany
private List<Course> coursesTaken;
Now, I know that this kind of many-to-many relationships are usually represented by a third table. I also know that there's the annotation #JoinTable which allows us to do that. What I don't know is if I should add this annotation #JoinTable over both fields in the two different entities or not. By the way, if I need to add to both, the names need to match right?
It's actually a good question, and it helps to understand the concept of an "owning" entity because neither side needs a #JoinTable annotation. If you want to prevent both sides from having join tables, a good idea, then you need to have a mappedBy= element on one side. The #JoinTable annotation is used to either specify the table name, or the columns that map the association.
First look at the Javadoc for #JoinTable:
Specifies the mapping of associations. It is applied to the owning side of an association.
Whether or not there is a join table is controlled by the mappedBy="name" element of the #ManyToMany annotation. The Javadoc for mappedBy for the ManyToMany annotation says:
The field that owns the relationship. Required unless the relationship is unidirectional.
For your (bidirectional) example in Hibernate (5.0.9.Final), if there were only two #ManyToMany annotations and no mappedBy= element, the default will have two Entity tables and two Join Tables:
Hibernate: create table Course (id bigint not null, primary key (id))
Hibernate: create table Course_Member (Course_id bigint not null, members_id bigint not null, primary key (Course_id, members_id))
Hibernate: create table Member (id bigint not null, primary key (id))
Hibernate: create table Member_Course (Member_id bigint not null, courses_id bigint not null, primary key (Member_id, courses_id))
While this is saying that each Entity "owns" its ManyToMany relationship, the extra join table is redundant in the typical use case. However, if I decide to have the Member entity "own" the relationship, then I add the mappedBy= element to the Course entity to specify that it doesn't own the relationship:
#ManyToMany(mappedBy="courses")
Set<Member> members;
Adding #JoinTable(name="Member_Course") to the Member entity doesn't change anything: it's only naming the table the same as it would have been named anyway.
Since the Course entity no longer owns its ManyToMany relationship, the extra JoinTable will not be created:
Hibernate: create table Course (id bigint not null, primary key (id))
Hibernate: create table Member (id bigint not null, primary key (id))
Hibernate: create table Member_Course (members_id bigint not null, courses_id bigint not null, primary key (members_id, courses_id))
This is important to the developer because he or she must understand that no relationship is persisted unless it's added to the owning entity, in this case the Member entity. However, since this a bidirectional relationship, the developer should be adding both a Course to Member.courses and a Member to Course.members anyway.
So, if you have a bidirectional ManyToMany relationship, which means you have ManyToMany on both entities involved, then you should add a mappedBy="name" on one of them to avoid having a redundant join table. Since it's bidirectional, I don't think it matters which side you make the owning entity. As always, it's always a good idea to enable the sql logs and see what's going on in the database:
References:
What is the difference between Unidirectional and Bidirectional associations?.
What does relationship owner means in bidirectional relationship?.
What is the “owning side” in an ORM mapping?.
Most efficient way to prevent an infinite recursion in toString()?.
You actually CAN use #JoinTable on both sides and often it makes perfect sense! I am talking out of experience after I had been looking for this solution for weeks.
Even though all throughout the internet, blogs and articles tell a different story - and the Javadoc of JPA is easily misunderstood (or wrong) in this way. I tried it after seeing this uncommented example in a book for professionals - and it worked.
How to do it:
Singer-Instrument-Association:
Singer side:
#ManyToMany
#JoinTable(name = "singer_instrument", joinColumns =
#JoinColumn(name = "SINGER_ID"), inverseJoinColumns = #JoinColumn(name = "INSTRUMENT_ID"))
public Set<Instrument> instruments;
And exactly the same on the other side!
Instrument side:
#ManyToMany
#JoinTable(name = "singer_instrument",
joinColumns = #JoinColumn(name = "INSTRUMENT_ID"),
inverseJoinColumns = #JoinColumn(name = "SINGER_ID"))
public Set<Singer> singers;
So, if you address the same join table, "singer_instrument", with the same name, it work's.
If you address one join table "singer_instrument" and one join table "instrument-singer" though, it will indead result in two different join tables in the database.
This makes a lot of sense, because a many-to-many relationship has no owning side - seen from the database perspective. Owning side means the side, that owns the foreign key of the relationship. But neither the table "singer" nor "instrument" have a foreign key referring to each other. The foreign keys are inside the neccessary join table between them.
The advantage of #JoinTable on both sides of the relation:
Let's say, a singer starts to learn a new instrument: You can add the instrument to singer (and vise versa, as it is bidirectional) and update/merge the singer. The update will update only the singer and the join table. It won't touch the instrument-table.
Now the other case - a guitar-course has ended, so you want to remove the connection between the guitar and the former course-participants/singers: After removing the instrument "guitar" from the singers (and vise versa!), you update/merge the instrument. The update will update only the instrument and the join table. It won't touch the singer-table.
If you had #JoinTable only on one side, you would always have to update/save/delete this side to safely handle the entries in the join table (the relationships between singers and instruments). In this case, you would have to update each singer, who ended the guitar course. That is not reflecting the type of relationship properly and can cause performance issues and conflicts during data transaction.
Nope. Both sides get #ManyToMany, but only one has the #JoinTable
More ManyToMany info here
For #ManyToMany to work on an an existing schema (not made by Hibernate) you will have to use the #JoinTable annotation on both classes to specify the table and which columns map to Java member variables in the appropriate class. I think this example may help you with what properties should be passed to the annotation:
https://dzone.com/tutorials/java/hibernate/hibernate-example/hibernate-mapping-many-to-many-using-annotations-1.html

ManyToOne relation without list/set using JPA

I have new JPA project using hibernate and I run into a difficulty while reading its code. I saw:
#Entity
public class Product {
#ManyToOne(optional = false, fetch = FetchType.LAZY)
private Manufacturer manufacturer;
...
}
and another Entity
#Entity
public class Manufacturer{
private ManufacturerId manufacturerId;
private String name;
private Manufacturer() {
}
...
Why there is not List/Set with Product(s) in the Manufacturer Entity class? ManyToOne relationship is Bidirectional? Why is this possible? How Manufacturer knows about its products, how this will be persisted on the DB table?
The many-side of an one to many association is optional. You can implement it if intended or skip it if not needed or even risky. A manufacturer could have many thousand of products. Than it makes no sense to fetch all of them at once. It's better to load via a query and use paging. Of course you could add the collection of products to your Manufacturer if you think this helps you.
Why there is not List/Set with Product(s) in the Manufacturer Entity class?
Either because not needed or as considered risky.
ManyToOne relationship is Bidirectional?
Of course yes. Even if the relationship is not impletemented it still exists.
Why is this possible? How this will be persisted on the DB table?
A OneToMany relationship is always implemented by an id on the one side. (ManufacturerId in Products in this case. Nothing else is needed. Even if you implement the products collection. This will not impact the way it's persisted.
How Manufacturer knows about its products?
It doesn't. But of course it's possible to query the database.
If you look at it at DB level, table Product will have something like manufacturer_id which is a foreign key to Manufacturer table. The table structure remains the same in both unidirectional and bidirectional mapping case.
Manufacturer will know its product by querying table Product with manufacturer_id = <its id>. On JPA level, in case of unidirectional mapping you could query it by from Product p where p.manufacturer.id = :man_id. In case of bidirectional mapping you could just do manufacturer.getProducts(), but it would translate to the same SQL.

POJO data with one to many / many to one relationship(JDBC)

For example I have two entities : Enterprise, Department. Enterprise has many departments, department has one enterprise, so there is a column - Enterprise_ID in Department table.
I have a function for saving Department object
void save(Department department);
To add Enterprise_ID in the table I need to have either reference on Enterprise object or enterprise's id.
Which way is more suitable?
However I prefer do not have such information in department object but on this way how can I save Enterprise_ID in the table ? It seems to me Hibernate somehow doing it.
public class Department{
private long id;
private String name;
private DepartmentType type;
private List<Employee> employees;
//getters()/setters()
}
public class Enterprise{
...
private List<Department> departments;
...
}
Department does not have any information about Enterprise in which it exists. So using only department object I can't insert Enterprise_ID(FK) in department table. But hibernate's save method somehow doing it. How can I do it without hibernate using entities above;
I use JDBC.
To do it the same way as hibernate does, you would have a save(Enterprise) method that would persist the enterprise object to the db and also insert/update the foreign key association.
Hibernate supports both nullable and non-nullable foreign key. In the latter case, it will first insert the enterprise, obtaining its primary key value, and then insert the department's along with the correct foreign key value.
You could do the same. But the save(Department) method would only be able to do updates on the department table and not change the association to the enterprise table. To do that, you would have to change the collection in enterprise and save/update that to the db.
Hibernate will only save/update the foreign key if you change something in the Enterprise.departments collection. It's the only way to do it if you don't have the reverse relation.
In your code, you'll have to use the Enterprise object to update the foreign keys in the Department table.
You could create a bidirectional association, by putting a field 'enterprise' in your Department class, but then you need to keep both relations in synch manually...

ArrayList of primitive types in Hibernate

I have a question regarding an ArrayList of Integers, or primitive types in general. Suppose I'm designing a POS program and each product may have several prices.
Let's assume I can represent a price value with ints and in the Product class I have the field ArrayList<Integer> prices. What's the best way to map this with Hibernate?
I could map it to a product_prices table with a field containing the price value and a field for the foreign key referencing the product in question, but this seems overkill.
On the other hand, I could concatenate all prices in a String and store it as a field in the products table, with the prices separated by semicolons, for instance. This way I'm saving one table and a future select, but it doesn't seem quite OO.
What is the best to do here?
Let's forget the sample here (which may not be the best one). With Hibernate, you can map a collection of basic types or embeddedable objects with the #CollectionOfElements annotation (and optionally an #IndexColumn for ordered collections):
#Entity
public class Product {
#Id #GeneratedValue
private Long id;
#CollectionOfElements #IndexColumn(name="price_index")
private List<Integer> prices = new ArrayList<Integer>();
...
}
Semantically, this is close to a #OneToMany except that the elements of the collection are not Entities, they don't have an id property, and their lifecycle is fully dependent on the owner object.
From a database point of view, this would result in a table for the product and a table for the prices:
create table Product (id bigint not null, primary key (id))
create table Product_prices (Product_id bigint not null, element integer, price_index integer not null, primary key (Product_id, price_index))
alter table Prodcut_prices add constraint FK9D26D06FB343359D foreign key (Product_id) references Product
In JPA 2.0, this annotation has been standardized so prefer the new #ElementCollection annotation if you are using JPA 2.0.
That being said, for the particular case of Product and Price, what #duffymo said is very true and they should probably not be implemented using the mentioned annotations.
On the other hand, I could concatenate
all prices in a String and store it as
a field in the products table, with
the prices separated by semicolons,
for instance. This way I'm saving one
table and a future select, but it
doesn't seem quite OO.
No, it's not relational. That breaks the rules for the first normal form.
I don't see why you're worried about saving a table and a SELECT. This is premature optimization at its worst.
A product may have several prices, but there will also be criteria that tell you when one applies (e.g., effective date, discount conditions, etc.) You should add those into your schema as well.
I'd recommend a Product table, without any pricing or discount information in it.
It sounds like there'd be a many-to-many relationship between products and prices if Price has an effective date in it, so you'd have a Product_Price JOIN table as well.

Categories