Hibernate: Two OneToMany properties, mapped by the same entity with different columns - java

I know that this can be easily solved with an HQL query, however I prefered to simply have Hibernate handle this with a few OneToMany properties for me.
Let me demonstrate what I want my domain model to look like in pseudo-code:
Game
Long GameID
Team HomeTeam
Team AwayTeam
#OneToMany(mappedBy="team")
Set<TeamPlay> HomeTeamPlays
#OneToMany(mappedBy="team")
Set<TeamPlay> AwayTeamPlays
The table structure is similar, there are two foreign keys that both point to the Team table on the Game table. Clearly if there were only one foreign key then it would represent a true One-To-Many relationship but in reality what I want is two bi-directional One-To-Many properies for the same entity child type.
I don't believe using the #Where annotation will work as it requires a constant, and #JoinColumn is not allowed here. If it is not possible then that is okay, I just wanted to here it from somebody else.

I bet you don't really understand the use of mappedBy.
You may refer to my other answer in https://stackoverflow.com/a/13812047/395202
In short, mappedBy is the property name in the "opposite side" of a bi-directional relationships.
For you case, it probably look something like:
class TeamPlay {
#ManyToOne
Team homeTeam;
#ManyToOne
Team awayTeam;
}
class Team {
#OneToMany(mappedBy="homeTeam")
Set<TeamPlay> homeTeamPlays;
#OneToMany(mappedBy="awayTeam")
Set<TeamPlay> awayTeamPlays;
}

There is nothing wrong with your code. I've tested it with #ManyToOne on TeamPlay class and it works fine. Creates a join column on TeamPlay table as expected. Nothing unusual

Related

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.

Hibernate: bidirectionality vs. unidirectionality, depending on relationship

Reading a wiki page about Hibernate I elaborated some perplexing conclusions:
1) Bidirectionality is reccomended in one-to-many
2) Bidirectionality is optional in many-to-one
3) Bidirectionality is normally present in many-to-many
4) Unidirectionality is reccomended in one-to-one relationships,
using as owner class the one with the primary key of the
relation (not the foreign key).
Are these statements true? Do you have any example to explain why in some cases unidirectionality is reccomended and in others bidirectionality is reccomended instead?
Here's the wiki page (read under "concepts"):
http://wiki.elvanor.net/index.php/Hibernate
Note that "bidirectionality" in the context of Hibernate means that in your Java classes, both sides of the relationship maintain a link to the other side. It has no impact on the underlying database schema (except in the case of indexed collections, see below), it's just whether or not you want the Java side to reflect that.
For all of your conclusions, "recommended" actually translates to "it usually ends up making sense, given your business logic, that you'd do it this way".
You really want to read through chapters 7 and 8 of the Hibernate Core Reference Manual.
It's recommended if you need it. A lot of convenience comes from specifying a bidirectional relationship; particularly it becomes possible to navigate the relationship from both ends in your business logic. However, if you don't actually need to do this, there's nothing to gain. Use whatever is most appropriate for the situation. In practice I've found that I want to specify both ends of the relationship to Hibernate more often than not -- but it is not a rule, rather, it reflects what I want to accomplish.
This is true. In a many-to-one (or one-to-many) relationship, it is optional. Consider the following schema:
table: users
fields: userId, userName
table: forumPosts
fields: postId, userId, content
Where forumPosts.userId is a foreign key into users. Your DAO classes might be (getters/setters omitted for brevity):
public class User {
private long userId;
private String userName;
}
public class ForumPost {
private long postId;
private User user;
private String content;
}
As you can see, this is a unidirectional many-to-one relationship (ForumPost-to-User). The ForumPost links to the user, but the User does not contain a list of ForumPosts.
You could then add a one-to-many mapping to User to make it have a list of ForumPosts. If you use a non-indexed collection like a set, this has no impact on the database schema. Merely by specifying both sides to Hibernate, you have made it bidirectional (using exactly the same schema as above), e.g.:
public class User {
private long userId;
private String userName;
private Set<ForumPost> forumPosts;
}
public class ForumPost {
private long postId;
private User user;
private String content;
}
Hibernate will now populate User.forumPosts when necessary (essentially with SELECT * FROM forumPosts WHERE userId = ?). The only difference between bidirectional and unidirectional here is that in one case Hibernate fills a set of ForumPosts in User, and in the other case it doesn't. If you ever have to get a collection of any given user's posts, you will want to use a bidirectional relationship like this rather than explicitly constructing an HQL query. Depending on your inverse/insert/update/cascade options in your relationship, you can also add and remove posts by modifying the User's set of posts, which may be a more accurate reflection of your business logic (or not!).
The reason I specified that non-indexed collections don't impact the underlying schema is because if you want to use an ordered, indexed collection like a list, you do have to add an extra list index field to the forumPosts table (although you do not have to add it to the ForumPost DAO class).
This is true, but is not a requirement and it's deeper than that. Same as above. Bidirectionality is usually present in many-to-many. Many-to-many relationships are implemented with a third join table. You specify the details of this table on both sides of the relationship. You can simply not specify the relationship on one side, and now it's a unidirectional relationship. Again, whether or not you tell Hibernate about the mapping is what determines if its unidirectional or bidirectional (in the context of Hibernate). In this case it also has no impact on the underlying schema unless you are using an ordered index collection. In fact, the many-to-many example in the Hibernate reference manual is a unidirectional setup.
In reality, it would be odd to have a unidirectional many-to-many relationship, unless perhaps you are working with an existing database schema and your particular application's business logic has no need for one of the sides of the relationship. Usually, though, when you've decided you need a many-to-many relationship, you've decided that because you need to maintain a collection of references on both sides of the relationship, and your DAO classes would reflect that need.
So the correct conclusion here is not merely that "bidirectionality is normally present in many-to-many", but instead "if you've designed a database with a join table, but your business logic only uses a unidirectional relationship, you should question whether or not your schema is appropriate for your application (and it very well may be)".
This is not true. Exactly the same as all the points above. If you need to navigate the one-to-one relationship from both sides, then you'd want to make it bidirectional (specify both sides of the mapping to Hibernate). If not, then you make it unidirectional (don't specify both sides of the mapping to Hibernate). This again comes down to what makes sense in your business layer.
I hope that helps. I left a lot of intricacies out. You really should read through the Hibernate documentation - it is not organized particularly well but Chapter 7 and 8 will tell you everything you need to know about collection mapping.
When I'm designing an application and a database from scratch, personally, I try to forget about Hibernate and the database entirely. I set up my DAOs in a way that makes sense for my business requirements, design a database schema to match, then set up the Hibernate mappings, making any final tweaks to the schema (e.g. adding index fields for ordered collections) at that point if necessary.

Eclipse + Hibernate + Spring question

I can't figure out how to organize classes in project...
I need to show all Workers with their history of the specialties (types).
I have 3 tables in DB:
Workers: id, name, address
Specialty: id, description
Worker_Type: id_worker, id_specialty, hire_date
I have:
class Specialty
{
...
}
class Worker
{
private List<Specialty> history;
...
}
How can i map Specialty(Class) to two tables (Specialty and Worker_Specialty) to get all information about worker inculding history ?
Maybe i have to create one more class SpecialtyHistory and map it to Worker_Specialty ?
What you are describing is called a ternary association, or in other words, a many-to-many relationship that includes adding information on the relationship itself. There are several approaches to mapping ternary associations, one of which being creating a SpecialtyHistory class as you mentioned, treating the relationship as a first class entity. If you did not need hire_date, you could just use a normal many-to-many mapping.
The Hibernate documentation has additional information on ternary associations.
Some previous StackOverflow questions: hibernate: ternary association mapping and Ternary (and n-ary) relationships in Hibernate.

JPA update many-to-many deleting records

I have a #ManyToMany relationship between two entities. When I perform an update on the owning side, it appears that JPA deletes all the linked records from my database and re-inserts them. For me this is a problem because I have a MySQL trigger that fires before a record is deleted. Any ideas on how to get around this problem?
#Entity
public class User {
#Id
#Column(name="username")
private String username;
...
#ManyToMany
#JoinTable(name="groups", joinColumns=
#JoinColumn(name="username", referencedColumnName="username"),
inverseJoinColumns=#JoinColumn(name="groupname",
referencedColumnName="type_id"))
private List<UserType> types;
...
}
#Entity
public class UserType {
#Id
#Column(name="type_id")
private String id;
#ManyToMany(mappedBy="types")
private List<User> users;
...
}
Use Set instead of List solved the problem. But I have no idea why it works.
Another solution provided by Hibernate is to split the #ManyToMany association into two bidirectional #OneTo#Many relationships. See Hibernate 5.2 documentation for example.
If a bidirectional #OneToMany association performs better when
removing or changing the order of child elements, the #ManyToMany
relationship cannot benefit from such an optimization because the
foreign key side is not in control. To overcome this limitation, the
link table must be directly exposed and the #ManyToMany association
split into two bidirectional #OneToMany relationships.
Try this one:
1) change declaration to:
private List<UserType> types = new Vector<UserType>();
2) never call
user.setTypes(newTypesList)
3) only call
user.getTypes().add(...);
user.getTypes().remove(...);
Its probably related to this question. You have to ensure you have an appropriately defined hashCode an equals method in your mapped object so that Eclipselink can determine equality and thus determine that the existing objects map to existing objects in the DB. Otherwise it has no choice but to recreate the child objects every time.
Alternatively, I've read that this kind of join can only support efficient adding and removing of list items if you use an index column, but that's going to be EclipseLink specific, since the JPA annotations don't seem to support such a thing. I know there is an equivalent Hibernate annotation, but I don't know what it would be in Eclipselink, if such a thing exists.
It appears my problem was that I was not merging the entity.

Categories