In which case do you use the JPA #JoinTable annotation?
EDIT 2017-04-29: As pointed to by some of the commenters, the JoinTable example does not need the mappedBy annotation attribute. In fact, recent versions of Hibernate refuse to start up by printing the following error:
org.hibernate.AnnotationException:
Associations marked as mappedBy must not define database mappings
like #JoinTable or #JoinColumn
Let's pretend that you have an entity named Project and another entity named Task and each project can have many tasks.
You can design the database schema for this scenario in two ways.
The first solution is to create a table named Project and another table named Task and add a foreign key column to the task table named project_id:
Project Task
------- ----
id id
name name
project_id
This way, it will be possible to determine the project for each row in the task table. If you use this approach, in your entity classes you won't need a join table:
#Entity
public class Project {
#OneToMany(mappedBy = "project")
private Collection<Task> tasks;
}
#Entity
public class Task {
#ManyToOne
private Project project;
}
The other solution is to use a third table, e.g. Project_Tasks, and store the relationship between projects and tasks in that table:
Project Task Project_Tasks
------- ---- -------------
id id project_id
name name task_id
The Project_Tasks table is called a "Join Table". To implement this second solution in JPA you need to use the #JoinTable annotation. For example, in order to implement a uni-directional one-to-many association, we can define our entities as such:
Project entity:
#Entity
public class Project {
#Id
#GeneratedValue
private Long pid;
private String name;
#JoinTable
#OneToMany
private List<Task> tasks;
public Long getPid() {
return pid;
}
public void setPid(Long pid) {
this.pid = pid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Task> getTasks() {
return tasks;
}
public void setTasks(List<Task> tasks) {
this.tasks = tasks;
}
}
Task entity:
#Entity
public class Task {
#Id
#GeneratedValue
private Long tid;
private String name;
public Long getTid() {
return tid;
}
public void setTid(Long tid) {
this.tid = tid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
This will create the following database structure:
The #JoinTable annotation also lets you customize various aspects of the join table. For example, had we annotated the tasks property like this:
#JoinTable(
name = "MY_JT",
joinColumns = #JoinColumn(
name = "PROJ_ID",
referencedColumnName = "PID"
),
inverseJoinColumns = #JoinColumn(
name = "TASK_ID",
referencedColumnName = "TID"
)
)
#OneToMany
private List<Task> tasks;
The resulting database would have become:
Finally, if you want to create a schema for a many-to-many association, using a join table is the only available solution.
#ManyToMany associations
Most often, you will need to use #JoinTable annotation to specify the mapping of a many-to-many table relationship:
the name of the link table and
the two Foreign Key columns
So, assuming you have the following database tables:
In the Post entity, you would map this relationship, like this:
#ManyToMany(cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
})
#JoinTable(
name = "post_tag",
joinColumns = #JoinColumn(name = "post_id"),
inverseJoinColumns = #JoinColumn(name = "tag_id")
)
private List<Tag> tags = new ArrayList<>();
The #JoinTable annotation is used to specify the table name via the name attribute, as well as the Foreign Key column that references the post table (e.g., joinColumns) and the Foreign Key column in the post_tag link table that references the Tag entity via the inverseJoinColumns attribute.
Notice that the cascade attribute of the #ManyToMany annotation is set to PERSIST and MERGE only because cascading REMOVE is a bad idea since we the DELETE statement will be issued for the other parent record, tag in our case, not to the post_tag record.
Unidirectional #OneToMany associations
The unidirectional #OneToMany associations, that lack a #JoinColumn mapping, behave like many-to-many table relationships, rather than one-to-many.
So, assuming you have the following entity mappings:
#Entity(name = "Post")
#Table(name = "post")
public class Post {
#Id
#GeneratedValue
private Long id;
private String title;
#OneToMany(
cascade = CascadeType.ALL,
orphanRemoval = true
)
private List<PostComment> comments = new ArrayList<>();
//Constructors, getters and setters removed for brevity
}
#Entity(name = "PostComment")
#Table(name = "post_comment")
public class PostComment {
#Id
#GeneratedValue
private Long id;
private String review;
//Constructors, getters and setters removed for brevity
}
Hibernate will assume the following database schema for the above entity mapping:
As already explained, the unidirectional #OneToMany JPA mapping behaves like a many-to-many association.
To customize the link table, you can also use the #JoinTable annotation:
#OneToMany(
cascade = CascadeType.ALL,
orphanRemoval = true
)
#JoinTable(
name = "post_comment_ref",
joinColumns = #JoinColumn(name = "post_id"),
inverseJoinColumns = #JoinColumn(name = "post_comment_id")
)
private List<PostComment> comments = new ArrayList<>();
And now, the link table is going to be called post_comment_ref and the Foreign Key columns will be post_id, for the post table, and post_comment_id, for the post_comment table.
Unidirectional #OneToMany associations are not efficient, so you are better off using bidirectional #OneToMany associations or just the #ManyToOne side.
It's the only solution to map a ManyToMany association : you need a join table between the two entities tables to map the association.
It's also used for OneToMany (usually unidirectional) associations when you don't want to add a foreign key in the table of the many side and thus keep it independent of the one side.
Search for #JoinTable in the hibernate documentation for explanations and examples.
It's also cleaner to use #JoinTable when an Entity could be the child in several parent/child relationships with different types of parents. To follow up with Behrang's example, imagine a Task can be the child of Project, Person, Department, Study, and Process.
Should the task table have 5 nullable foreign key fields? I think not...
It lets you handle Many to Many relationship. Example:
Table 1: post
post has following columns
____________________
| ID | DATE |
|_________|_________|
| | |
|_________|_________|
Table 2: user
user has the following columns:
____________________
| ID |NAME |
|_________|_________|
| | |
|_________|_________|
Join Table lets you create a mapping using:
#JoinTable(
name="USER_POST",
joinColumns=#JoinColumn(name="USER_ID", referencedColumnName="ID"),
inverseJoinColumns=#JoinColumn(name="POST_ID", referencedColumnName="ID"))
will create a table:
____________________
| USER_ID| POST_ID |
|_________|_________|
| | |
|_________|_________|
Related
What is the difference between:
#Entity
public class Company {
#OneToMany(cascade = CascadeType.ALL , fetch = FetchType.LAZY)
#JoinColumn(name = "companyIdRef", referencedColumnName = "companyId")
private List<Branch> branches;
...
}
and
#Entity
public class Company {
#OneToMany(cascade = CascadeType.ALL , fetch = FetchType.LAZY,
mappedBy = "companyIdRef")
private List<Branch> branches;
...
}
The annotation #JoinColumn indicates that this entity is the owner of the relationship (that is: the corresponding table has a column with a foreign key to the referenced table), whereas the attribute mappedBy indicates that the entity in this side is the inverse of the relationship, and the owner resides in the "other" entity. This also means that you can access the other table from the class which you've annotated with "mappedBy" (fully bidirectional relationship).
In particular, for the code in the question the correct annotations would look like this:
#Entity
public class Company {
#OneToMany(mappedBy = "company",
orphanRemoval = true,
fetch = FetchType.LAZY,
cascade = CascadeType.ALL)
private List<Branch> branches;
}
#Entity
public class Branch {
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "companyId")
private Company company;
}
#JoinColumn could be used on both sides of the relationship. The question was about using #JoinColumn on the #OneToMany side (rare case). And the point here is in physical information duplication (column name) along with not optimized SQL query that will produce some additional UPDATE statements.
According to documentation:
Since many to one are (almost) always the owner side of a bidirectional relationship in the JPA spec, the one to many association is annotated by #OneToMany(mappedBy=...)
#Entity
public class Troop {
#OneToMany(mappedBy="troop")
public Set<Soldier> getSoldiers() {
...
}
#Entity
public class Soldier {
#ManyToOne
#JoinColumn(name="troop_fk")
public Troop getTroop() {
...
}
Troop has a bidirectional one to many relationship with Soldier through the troop property. You don't have to (must not) define any physical mapping in the mappedBy side.
To map a bidirectional one to many, with the one-to-many side as the owning side, you have to remove the mappedBy element and set the many to one #JoinColumn as insertable and updatable to false. This solution is not optimized and will produce some additional UPDATE statements.
#Entity
public class Troop {
#OneToMany
#JoinColumn(name="troop_fk") //we need to duplicate the physical information
public Set<Soldier> getSoldiers() {
...
}
#Entity
public class Soldier {
#ManyToOne
#JoinColumn(name="troop_fk", insertable=false, updatable=false)
public Troop getTroop() {
...
}
Unidirectional one-to-many association
If you use the #OneToMany annotation with #JoinColumn, then you have a unidirectional association, like the one between the parent Post entity and the child PostComment in the following diagram:
When using a unidirectional one-to-many association, only the parent side maps the association.
In this example, only the Post entity will define a #OneToMany association to the child PostComment entity:
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name = "post_id")
private List<PostComment> comments = new ArrayList<>();
Bidirectional one-to-many association
If you use the #OneToMany with the mappedBy attribute set, you have a bidirectional association. In our case, both the Post entity has a collection of PostComment child entities, and the child PostComment entity has a reference back to the parent Post entity, as illustrated by the following diagram:
In the PostComment entity, the post entity property is mapped as follows:
#ManyToOne(fetch = FetchType.LAZY)
private Post post;
The reason we explicitly set the fetch attribute to FetchType.LAZY is because, by default, all #ManyToOne and #OneToOne associations are fetched eagerly, which can cause N+1 query issues.
In the Post entity, the comments association is mapped as follows:
#OneToMany(
mappedBy = "post",
cascade = CascadeType.ALL,
orphanRemoval = true
)
private List<PostComment> comments = new ArrayList<>();
The mappedBy attribute of the #OneToMany annotation references the post property in the child PostComment entity, and, this way, Hibernate knows that the bidirectional association is controlled by the #ManyToOne side, which is in charge of managing the Foreign Key column value this table relationship is based on.
For a bidirectional association, you also need to have two utility methods, like addChild and removeChild:
public void addComment(PostComment comment) {
comments.add(comment);
comment.setPost(this);
}
public void removeComment(PostComment comment) {
comments.remove(comment);
comment.setPost(null);
}
These two methods ensure that both sides of the bidirectional association are in sync. Without synchronizing both ends, Hibernate does not guarantee that association state changes will propagate to the database.
Which one to choose?
The unidirectional #OneToMany association does not perform very well, so you should avoid it.
You are better off using the bidirectional #OneToMany which is more efficient.
I disagree with the accepted answer here by Óscar López. That answer is inaccurate!
It is NOT #JoinColumn which indicates that this entity is the owner of the relationship. Instead, it is the #ManyToOne annotation which does this (in his example).
The relationship annotations such as #ManyToOne, #OneToMany and #ManyToMany tell JPA/Hibernate to create a mapping. By default, this is done through a seperate Join Table.
#JoinColumn
The purpose of #JoinColumn is to create a join column if one does
not already exist. If it does, then this annotation can be used to
name the join column.
MappedBy
The purpose of the MappedBy parameter is to instruct JPA: Do NOT
create another join table as the relationship is already being mapped
by the opposite entity of this relationship.
Remember: MappedBy is a property of the relationship annotations whose purpose is to generate a mechanism to relate two entities which by default they do by creating a join table. MappedBy halts that process in one direction.
The entity not using MappedBy is said to be the owner of the relationship because the mechanics of the mapping are dictated within its class through the use of one of the three mapping annotations against the foreign key field. This not only specifies the nature of the mapping but also instructs the creation of a join table. Furthermore, the option to suppress the join table also exists by applying #JoinColumn annotation over the foreign key which keeps it inside the table of the owner entity instead.
So in summary: #JoinColumn either creates a new join column or renames an existing one; whilst the MappedBy parameter works collaboratively with the relationship annotations of the other (child) class in order to create a mapping either through a join table or by creating a foreign key column in the associated table of the owner entity.
To illustrate how MapppedBy works, consider the code below. If MappedBy parameter were to be deleted, then Hibernate would actually create TWO join tables! Why? Because there is a symmetry in many-to-many relationships and Hibernate has no rationale for selecting one direction over the other.
We therefore use MappedBy to tell Hibernate, we have chosen the other entity to dictate the mapping of the relationship between the two entities.
#Entity
public class Driver {
#ManyToMany(mappedBy = "drivers")
private List<Cars> cars;
}
#Entity
public class Cars {
#ManyToMany
private List<Drivers> drivers;
}
Adding #JoinColumn(name = "driverID") in the owner class (see below), will prevent the creation of a join table and instead, create a driverID foreign key column in the Cars table to construct a mapping:
#Entity
public class Driver {
#ManyToMany(mappedBy = "drivers")
private List<Cars> cars;
}
#Entity
public class Cars {
#ManyToMany
#JoinColumn(name = "driverID")
private List<Drivers> drivers;
}
The annotation mappedBy ideally should always be used in the Parent side (Company class) of the bi directional relationship, in this case it should be in Company class pointing to the member variable 'company' of the Child class (Branch class)
The annotation #JoinColumn is used to specify a mapped column for joining an entity association, this annotation can be used in any class (Parent or Child) but it should ideally be used only in one side (either in parent class or in Child class not in both) here in this case i used it in the Child side (Branch class) of the bi directional relationship indicating the foreign key in the Branch class.
below is the working example :
parent class , Company
#Entity
public class Company {
private int companyId;
private String companyName;
private List<Branch> branches;
#Id
#GeneratedValue
#Column(name="COMPANY_ID")
public int getCompanyId() {
return companyId;
}
public void setCompanyId(int companyId) {
this.companyId = companyId;
}
#Column(name="COMPANY_NAME")
public String getCompanyName() {
return companyName;
}
public void setCompanyName(String companyName) {
this.companyName = companyName;
}
#OneToMany(fetch=FetchType.LAZY,cascade=CascadeType.ALL,mappedBy="company")
public List<Branch> getBranches() {
return branches;
}
public void setBranches(List<Branch> branches) {
this.branches = branches;
}
}
child class, Branch
#Entity
public class Branch {
private int branchId;
private String branchName;
private Company company;
#Id
#GeneratedValue
#Column(name="BRANCH_ID")
public int getBranchId() {
return branchId;
}
public void setBranchId(int branchId) {
this.branchId = branchId;
}
#Column(name="BRANCH_NAME")
public String getBranchName() {
return branchName;
}
public void setBranchName(String branchName) {
this.branchName = branchName;
}
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="COMPANY_ID")
public Company getCompany() {
return company;
}
public void setCompany(Company company) {
this.company = company;
}
}
I'd just like to add that #JoinColumn does not always have to be related to the physical information location as this answer suggests. You can combine #JoinColumn with #OneToMany even if the parent table has no table data pointing to the child table.
How to define unidirectional OneToMany relationship in JPA
Unidirectional OneToMany, No Inverse ManyToOne, No Join Table
It seems to only be available in JPA 2.x+ though. It's useful for situations where you want the child class to just contain the ID of the parent, not a full on reference.
Let me make it simple.
You can use #JoinColumn on either sides irrespective of mapping.
Let's divide this into three cases.
1) Uni-directional mapping from Branch to Company.
2) Bi-direction mapping from Company to Branch.
3) Only Uni-directional mapping from Company to Branch.
So any use-case will fall under this three categories. So let me explain how to use #JoinColumn and mappedBy.
1) Uni-directional mapping from Branch to Company.
Use JoinColumn in Branch table.
2) Bi-direction mapping from Company to Branch.
Use mappedBy in Company table as describe by #Mykhaylo Adamovych's answer.
3)Uni-directional mapping from Company to Branch.
Just use #JoinColumn in Company table.
#Entity
public class Company {
#OneToMany(cascade = CascadeType.ALL , fetch = FetchType.LAZY)
#JoinColumn(name="courseId")
private List<Branch> branches;
...
}
This says that in based on the foreign key "courseId" mapping in branches table, get me list of all branches. NOTE: you can't fetch company from branch in this case, only uni-directional mapping exist from company to branch.
JPA is a layered API, the different levels have their own annotations. The highest level is the (1) Entity level which describes persistent classes then you have the (2) relational database level which assume the entities are mapped to a relational database and (3) the java model.
Level 1 annotations: #Entity, #Id, #OneToOne, #OneToMany, #ManyToOne, #ManyToMany.
You can introduce persistency in your application using these high level annotations alone. But then you have to create your database according to the assumptions JPA makes. These annotations specify the entity/relationship model.
Level 2 annotations: #Table, #Column, #JoinColumn, ...
Influence the mapping from entities/properties to the relational database tables/columns if you are not satisfied with JPA's defaults or if you need to map to an existing database. These annotations can be seen as implementation annotations, they specify how the mapping should be done.
In my opinion it is best to stick as much as possible to the high level annotations and then introduce the lower level annotations as needed.
To answer the questions: the #OneToMany/mappedBy is nicest because it only uses the annotations from the entity domain. The #oneToMany/#JoinColumn is also fine but it uses an implementation annotation where this is not strictly necessary.
#Entity
public class Company {
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinColumn(name = "company_id_ref", referencedColumnName = "company_id")
private List<Branch> branches;
...
}
That Will give below Hibernate logs
Hibernate: select nextval ('hibernate_sequence')
Hibernate: select nextval ('hibernate_sequence')
Hibernate: insert into company (name, company_id) values (?, ?)
Hibernate: insert into branch (company_id_ref, name, id) values (?, ?, ?)
Hibernate: update branch set company_id_ref=? where id=?
And
#Entity
public class Company {
#OneToMany(cascade = CascadeType.ALL , fetch = FetchType.LAZY,
mappedBy = "company")
private List<Branch> branches;
...
}
That will give below Hibernate logs
Hibernate: select nextval ('hibernate_sequence')
Hibernate: select nextval ('hibernate_sequence')
Hibernate: insert into company (name, company_id) values (?, ?)
Hibernate: insert into branch (company_id_ref, name, id) values (?, ?, ?)
We can clearly see that #joinColumn will cause additional update queries.
so you do not need to set parent entity explicitly to child entity,
That we have to do while using mappedBy
to save children with a parent
My model (exemplified) is the following:
CREATE TABLE person (
id INT PRIMARY KEY,
name TEXT
...
);
CREATE TABLE team (
id INT PRIMARY KEY,
name TEXT
....
);
CREATE TABLE team_reference_persons (
team_id INT NOT NULL,
person_id INT NOT NULL,
uses_telephone BOOLEAN,
PRIMARY KEY (team_id, person_id),
FOREIGN KEY (team_id) REFERENCES team(id),
FOREIGN KEY (person_id) REFERENCES person(id)
);
And my JPA defintion:
#Entity
#Table(name = "team")
public class Team {
#Id
private Integer id;
#OneToMany
#JoinTable(name = "team_reference_persons", joinColumns = #JoinColumn(name = "team_id", referencedColumnName = "id"), inverseJoinColumns = #JoinColumn(name = "person_id", referencedColumnName = "id"))
private List<Person> teamReferencePersons;
...
}
#Entity
#Table(name = "person")
public class Person {
#Id
private UUID id;
private String name;
...
}
So far, so good, when all you need is the person list on the team. But now I need to add the team_reference_persons.uses_telephone property from the join table in my person domain, So I am looking for a way to keep the persons logic, while I create a new subclass.
private class TeamIndividual extends Person {
boolean uses_telephone;
}
Then changing List<Person> on Team entity by List<TeamIndividual>. Is that possible someway? JPA should be indicated in such smart way that it adds the join table property to the final target entity (on both read and save).
No need to extend TeamIndividual to Person.
Annotate TeamIndividual with #Table(name = "team_reference_persons")
Define fields(teamId,personId,uses_telephone) inside TeamIndividual
Annotate fields teamId and PersonId with #ManyToOne and #JoinColumn
Add List to Team without annotation
Try this,It will work..!!
Building a Spring Boot REST service backed by MySQL here. I'm adding a super-simple chat feature to an app and this service will handle its backend/enndpoints. I'm new to JPA and have two concerns: (1) that my primordial data model itself may be a little awry; and (2) that I'm not wrapping that model correctly using JPA conventions/best practices.
So first: an overview of the simple problem I'm trying to solve: Users can send Messages to 1+ other Users. This creates a Conversation, which is really just a container of 1+ Messages. If the Conversation is only between 2 Users, it's considered (by the app) to be a Direct Message (DM). Otherwise its considered to be a Group Chat.
My tables (pseudo-schema):
[users]
=======
id PRIMARY KEY AUTO_INC INT NOT NULL,
username VARCHAR(255) NOT NULL
[conversations]
===============
id PRIMARY KEY AUTO_INC INT NOT NULL,
created_on DATETIME NOT NULL
[messages]
==========
id PRIMARY KEY AUTO_INC INT NOT NULL,
conversation_id FOREIGN KEY INT NOT NULL, # on conversations table
sender_id FOREIGN KEY INT NOT NULL, # on users table
text VARCHAR(2000) NOT NULL,
sent_at DATETIME
[users_x_conversations]
=======================
id PRIMARY KEY AUTO_INC INT NOT NULL,
conversation_id FOREIGN KEY INT NOT NULL, # on conversations table
user_id FOREIGN KEY INT NOT NULL, # on users table
So in my design above, you can see I'm really just using the [conversations] table as a placeholder and as a way of grouping messages to a single conversation_id, and then [users_x_conversations] is crosswalk (many-to-many) table where I'm actually storing who is a "member of" which conversation.
Is this the right approach to take or is there a better way to relate the tables here? That's Concern #1.
Assumning I'm modeling the problem at the database correctly, then I have the following JPA/entity classes:
#MappedSuperclass
abstract public class BaseEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// Ctors, getters & setters down here...
}
#Entity(name = 'messages')
#AttributeOverrides({
#AttributeOverride(name = 'id', column=#Column(name='message_id'))
})
public class Message extends BaseEntity {
#OneToOne(fetch = FetchType.EAGER, cascade = [CascadeType.PERSIST, CascadeType.MERGE])
#JoinColumn(name = 'conversation_id', referencedColumnName = 'conversation_id')
#NotNull
#Valid
private Conversation conversation;
#OneToOne(fetch = FetchType.EAGER, cascade = [CascadeType.PERSIST, CascadeType.MERGE])
#JoinColumn(name = 'user_id', referencedColumnName = 'user_id')
#NotNull
#Valid
private User sender;
#Column(name = 'message_text')
#NotEmpty
private String text;
#Column(name = 'message_sent_at')
#NotNull
private Date sentAt;
// Ctors, getters & setters down here...
}
#Entity(name = 'conversations')
#AttributeOverrides({
#AttributeOverride(name = 'id', column=#Column(name='conversation_id'))
})
public class Conversation extends BaseEntity {
#Column(name = 'conversation_created_on')
#NotNull
private Date createdOn;
// Ctors, getters & setters down here...
}
What I'm stuck on now is: how should I model my [users_x_conversations] table at the JPA layer? Should I create something like this:
#Entity(name = 'users_x_conversations')
#AttributeOverrides({
#AttributeOverride(name = 'id', column=#Column(name='users_x_conversations_id'))
})
public class UserConversations extends BaseEntity {
#ManyToMany(fetch = FetchType.EAGER, cascade = [CascadeType.PERSIST, CascadeType.MERGE])
#JoinTable(
name="users_x_conversations",
joinColumns=[
#JoinColumn(name="user_id")
],
inverseJoinColumns=[
#JoinColumn(name="conversation_id")
]
)
private Map<User,Conversation> userConversations;
// Ctors, getters & setters down here...
}
Basically my service will want to be able to do queries like:
Given a conversationId, who are all the users that are members of that conversation?; and
Given a userId, what are all the conversations that user is a member of (DM and Group Chat alike)?
Is this the right approach to take or is there a better way to relate the tables here?
Your approach seems OK at the DB layer, except that if users_x_conversations serves only as a join table (i.e. if there are no extra properties associated with the (user, conversation) associations represented within), then I would use (conversation_id, user_id) as its PK instead of giving it a surrogate key. If you don't do that, then you should at least put a uniqueness constraint on that pair.
What I'm stuck on now is: how should I model my [users_x_conversations] table at the JPA layer?
I take you to be asking whether you should model that table as an entity. If you insist on giving it a surrogate key as you have done, then that implies "yes". But as I already discussed, I don't think that's needful. Nor much useful, for that matter. I would recommend instead modeling a direct many-to-many relationship between Conversation and User entities, with this table (less its id column) serving as the join table:
#Entity
#Table(name = "converations")
public class Conversation extends BaseEntity {
#Column(name = 'conversation_created_on')
#NotNull
private Date createdOn;
#ManyToMany(mappedBy = "conversations")
#JoinTable(name = "users_x_conversations",
joinColumns = #JoinColumn(name="conversation_id", nullable = false, updateable = false),
inverseJoinColumns = #JoinColumn(name = "user_id", nullable = false, updateable = false)
)
private Set<User> users;
// Ctors, getters & setters down here...
}
#Entity
#Table(name = "users")
public class User extends BaseEntity {
#NotNull
private String username;
#ManyToMany(mappedBy = "users")
// this is the non-owning side of the relationship; the join table mapping
// is declared on the other side
private Set<Conversation> conversations;
// Ctors, getters & setters down here...
}
Note in that case that User and Conversation entities are directly associated in the object model.
On the other hand, if you did choose to model users_x_conversations via an entity of its own, then the code you present for it is all wrong. It would look more like this:
#Entity
#Table(name = "users_x_converations", uniqueConstraints =
#UniqueConstraint(columnNames={"converation_id", "user_id"}))
public class UserConversation extends BaseEntity {
#ManyToOne(optional = false)
#JoinColumn(name = "conversation_id", nullable = false, updatable = false)
Conversation conversation;
#ManyToOne(optional = false)
#JoinColumn(name = "user_id", nullable = false, updatable = false)
User user;
// Ctors, getters & setters down here...
}
Note well that:
This makes the object-level association between Conversations and Users indirect, via UserConversation entities. If the relationships are navigable from the other side, then they would be modelled via #OneToMany relationship fields of type Set<UserConversation> or List<UserConversation>.
It requires more code, and more objects in the system at runtime.
On the other hand, it does have the minor advantage of saving you from making a somewhat arbitrary choice of which side of a direct #ManyToMany relationship is the owning side.
I have table Animal with OneToMany mapping to table EventAnimal:
#OneToMany(mappedBy = "animal")
public Set<EventAnimal> getEventAnimals() {
return eventAnimals;
}
Table EventAnimal looks like this
#Entity
#Table(name = "eventAnimal")
public class EventAnimal {
#Id
int id;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "userEvent_id")
UserEvent userEvent;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "animal_id", nullable=false)
Animal animal;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "eventAnimalClass_id")
EventAnimalClass eventAnimalClass;
}
When I add Event animal to animal and save animal, database is not being updated:
//Create EventAnimal object, set properties
eventAnimal.setUserEvent(newEvent);
eventAnimal.setAnimal(animal);
animal.getEventAnimals().add(eventAnimal);
animalPersistenceService.saveAnimal(animal);
What am I doing wrong?
When I try inserting Event animal, like eventAnimalDao.insert(eventAnimal);
instead of
animalPersistenceService.saveAnimal(animal);
I get exception that "animal_id" does not have default value even though I set it.
What is your ID generation strategy? Are you generating the ids by yourself or you will leave this to the DB? If it will be database put:
#GeneratedValue(strategy=GenerationType.AUTO)
You may want to update the key to Integer instead of int too but don`t think this is the problem.
Also if you want to add event in animal and expect to persist it update your mapping to:
#OneToMany(mappedBy = "animal", cascade = CascadeType.ALL)
I have table Company with OneToMany mapping to table Customer:
//bi-directional many-to-one association to Customer
#OneToMany(mappedBy="company")
private Set<Customer> customer;
I have mapped from tis format:
//bi-directional many-to-one association to Company
#ManyToOne
#JoinColumn(name="company_id")
private Company company;
I have use this code, Sucessfully add,edit and delete function is working.
Add function :
Customer customer = new Customer();
Company company = new Company();
company.setCompanyId(intCompanyId);
customer.setCompany(company);
I resolved the problem. It was rather silly mistake - script didn't remove one foreign key from table and I didn't care to look into SQL table.
I am running into the exception below whenever I use an entity that I have defined.
org.hibernate.exception.SQLGrammarException: Invalid column name 'coordinator_sycs_coord_id'.
at org.hibernate.exception.internal.SQLStateConversionDelegate.convert(SQLStateConversionDelegate.java:122)
at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:47)
I will post below the entities involved and the query that Hibernate is generating. The context is two entities that have a many-to-many relationship in an association table. I find interesting that the query that Hibernate is generating is changing the column name even when I have it right in my annotations. See below:
#Entity
#Table(name = "sycs_coord")
public class SycsCoordinator {
#Id
#GeneratedValue
#Column(name = "sycs_coord_id")
Long id;
#OneToMany(mappedBy = "club", fetch = FetchType.LAZY)
Set<SycsCoordinatorClub> clubs;
//Standard setters and getters below
}
#Entity
#Table(name = "sycs_coord_clb")
#IdClass(SycsCoordinatorClubPk.class)
public class SycsCoordinatorClub {
#Id
#Column(name = "sycs_coord_id")
Long sycs_coord_id;
#Id
#Column(name = "clb_id")
String clb_id;
#ManyToOne
#PrimaryKeyJoinColumn(name = "sycs_coord_id", referencedColumnName="sycs_coord_id")
SycsCoordinator coordinator;
#ManyToOne
#PrimaryKeyJoinColumn(name = "clb_id", referencedColumnName = "Clb_id")
Club club;
}
I am not including the classes Club and SycsCoordinatorClubPk for now because they seem irrelevant to the problem. The query that Hibernate is generating some times is:
select
clubs0_.club_Clb_Id as club4_0_3_,
clubs0_.clb_id_fk as clb1_3_,
clubs0_.sycs_coord_id as sycs2_3_,
clubs0_.clb_id_fk as clb1_2_2_,
clubs0_.sycs_coord_id as sycs2_2_2_,
clubs0_.club_Clb_Id as club4_2_2_,
clubs0_.coordinator_sycs_coord_id as coordina5_2_2_,
clubs0_.lst_updt_dt as lst3_2_2_,
clubs0_.sycs_coord_secur_grp_cd as sycs6_2_2_,
sycscoordi1_.sycs_coord_id as sycs1_0_0_,
sycscoordi2_.sycs_coord_secur_level_id as sycs4_3_1_
from
sycs_coord_clb clubs0_
left outer join
sycs_coord sycscoordi1_
on clubs0_.coordinator_sycs_coord_id=sycscoordi1_.sycs_coord_id
where
clubs0_.club_Clb_Id=?
Notice that sometimes the column name coordinator_sycs_coord_id appears in the query, even when there is no such name in any of the annotations. Why is this?
You are mis-using the #PrimaryKeyJoinColumn annotation, hence the strange results:
It is used to join the primary table of an entity subclass in the
JOINED mapping strategy to the primary table of its superclass; it is
used within a SecondaryTable annotation to join a secondary table to a
primary table; and it may be used in a OneToOne mapping in which the
primary key of the referencing entity is used as a foreign key to the
referenced entity.
http://docs.oracle.com/javaee/5/api/javax/persistence/PrimaryKeyJoinColumn.html
You should probably be using the #JoinColumn instead:
#JoinColumn(name = "sycs_coord_id", referencedColumnName = "sycs_coord_id")