#ManyToMany Duplicate Entry Exception - java

I have mapped a bidirectional many-to-many exception between the entities Course and Trainee in the following manner:
Course
{
...
private Collection<Trainee> students;
...
#ManyToMany(targetEntity = lesson.domain.Trainee.class,
cascade = {CascadeType.All}, fetch = {FetchType.EAGER})
#Jointable(name="COURSE_TRAINEE",
joincolumns = #JoinColumn(name="COURSE_ID"),
inverseJoinColumns = #JoinColumn(name = "TRAINEE_ID"))
#CollectionOfElements
public Collection<Trainee> getStudents() {
return students;
}
...
}
Trainee
{
...
private Collection<Course> authCourses;
...
#ManyToMany(cascade = {CascadeType.All}, fetch = {FetchType.EAGER},
mappedBy = "students", targetEntity = lesson.domain.Course.class)
#CollectionOfElements
public Collection<Course> getAuthCourses() {
return authCourses;
}
...
}
Instead of creating a table where the Primary Key is made of the two foreign keys (imported from the table of the related two entities), the system generates the table "COURSE_TRAINEE" with the following schema:
I am working on MySQL 5.1 and my App. Server is JBoss 5.1.
Does anyone guess why?

In addition to Bence Olah: the primary key constraint for (COURSE_ID, TRAINEE_ID) pair is not created because your mapping doesn't say that these pairs are unique. You need to change Collections to Sets in your code in order to express this restriction, and primary key will be created.

Use either #CollectionOfElements OR #ManyToMany. Don't use both of them at the same time!
Be aware that #CollectionOfElements is Hibernate specific, while #ManyToMany is based on JPA standard.
Futher reading:
http://docs.jboss.org/ejb3/app-server/HibernateAnnotations/reference/en/html_single/index.html

Related

What happens in a SQL database when we use nested JoinColumn in entity

I am going through some code and it uses nested #JoinColumn for one of the associations, as following:
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinColumns({#JoinColumn(name = "QuestId"), #JoinColumn(name = "GuildId")})
private GuildQuests guildQuest;
I understand that when I do something like the following:
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinColumns(name="GuildQuestId")
private GuildQuests guildQuest;
I am creating a column with the name GuildQuestId in the table and it has a foreign key constraint with the entity GuildQuests.
However, what will the nested #JoinColumn(first code snippet) do?
Your first snippet is saying that you specify multiple foreign keys for the same association.
Example:
#ManyToOne
#JoinColumns({
#JoinColumn(name="ADDR_ID", referencedColumnName="ID"),
#JoinColumn(name="ADDR_ZIP", referencedColumnName="ZIP")
})
public Address getAddress() { return address; }
Edit:
You're actually not creating any foreign keys with (both example) in the database. If you use for example Hibernate, Hibernate does not create any foreign keys on database level itself.

OpenJPA - Nested OneToMany relationships merge issue

Posting this here as I wasn't seeing much interest here: http://www.java-forums.org/jpa/96175-openjpa-one-many-within-one-many-merge-problems.html
Trying to figure out if this is a problem with OpenJPA or something I may be doing wrong...
I'm facing a problem when trying to use OpenJPA to update an Entity that contains a One to Many relationship to another Entity, that has a One to Many relationship to another. Here's a quick example of what I'm talking about:
#Entity
#Table(name = "school")
public class School {
#Column(name = "id")
protected Long id;
#Column(name = "name")
protected String name;
#OneToMany(mappedBy = "school", orphanRemoval = true, cascade = CascadeType.ALL)
protected Collection<ClassRoom> classRooms;
}
#Entity
#Table(name = "classroom")
public class ClassRoom {
#Column(name = "id")
protected Long id;
#Column(name = "room_number")
protected String roomNumber;
#ManyToOne
#JoinColumn(name = "school_id")
protected School school;
#OneToMany(mappedBy = "classRoom", orphanRemoval = true, cascade = CascadeType.ALL, fetch = FetchType.EAGER)
protected Collection<Desk> desks;
}
#Entity
#Table(name = "desk")
public class Desk {
#Column(name = "id")
protected Long id;
#ManyToOne
#JoinColumn(name = "classroom_id")
protected ClassRoom classRoom;
}
In the SchoolService class, I have the following update method:
#Transactional
public void update(School school) {
em.merge(school);
}
I'm trying to remove a Class Room from the School. I remove it from the classRooms collection and call update. I'm noticing if the Class Room has no desks, there are no issues. But if the Class Room has desks, it throws a constraint error as it seems to try to delete the Class Room first, then the Desks. (There is a foreign key constraint for the classroom_id column)
Am I going about this the wrong way? Is there some setting I'm missing to get it to delete the interior "Desk" instances first before deleting the Class Room instance that was removed?
Any help would be appreciated. If you need any more info, please just let me know.
Thanks,
There are various bug reports around FK violations in OpenJPA when cascading remove operations to child entities:
The OpenJPA FAQ notes that the following:
http://openjpa.apache.org/faq.html#reorder
Can OpenJPA reorder SQL statements to satisfy database foreign key
constraints?
Yes. OpenJPA can reorder and/or batch the SQL statements using
different configurable strategies. The default strategy is capable of
reordering the SQL statements to satisfy foreign key constraints.
However ,you must tell OpenJPA to read the existing foreign key
information from the database schema:
It would seem you can force the correct ordering of the statements by either setting the following property in your OpenJPA config
<property name="openjpa.jdbc.SchemaFactory"> value="native(ForeignKeys=true)"/>
or by adding the org.apache.openjpa.persistence.jdbc.ForeignKey annotation to the mapping:
#OneToMany(mappedBy = "classRoom", orphanRemoval = true, cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#org.apache.openjpa.persistence.jdbc.ForeignKey
protected Collection<Desk> desks;
See also:
https://issues.apache.org/jira/browse/OPENJPA-1936

JPA mapping of two tables using and when to use jointable

This is more of a general 'understanding' question rather than a specific senario question.
I have been lookiing at the ways in which JPA maps tables together and found two examples here that seem to work in different ways.
One has a Set of Phone objects using #JoinTable to join STUDENT_PHONE to STUDENT by STUDENT_ID
The other has a Set of StockDailyRecord but seems to just use mappedby stock and in the stock_detail table object have the #PrimaryKeyJoinColumn annotation.
Simply trying to get an understanding of which method would be the prefered way and why?
Method 1:
#OneToMany(cascade = CascadeType.ALL)
#JoinTable(name = "STUDENT_PHONE", joinColumns = { #JoinColumn(name = "STUDENT_ID") }, inverseJoinColumns = { #JoinColumn(name = "PHONE_ID") })
public Set<Phone> getStudentPhoneNumbers() {
return this.studentPhoneNumbers;
}
Method 2:
#Table(name = "stock", catalog = "mkyongdb", uniqueConstraints = {
#UniqueConstraint(columnNames = "STOCK_NAME"),
#UniqueConstraint(columnNames = "STOCK_CODE") })
#OneToMany(fetch = FetchType.LAZY, mappedBy = "stock")
public Set<StockDailyRecord> getStockDailyRecords() {
return this.stockDailyRecords;
}
#Table(name = "stock_detail", catalog = "mkyongdb")
#OneToOne(fetch = FetchType.LAZY)
#PrimaryKeyJoinColumn
public Stock getStock() {
return this.stock;
}
Method #2:
It uses an extra column to build the OneToMany relation. This column is a Foreign key column of the other table. Before building the relation if these data needs to be added to the database then this foreign key column needs to be defined as nullable. This breaks the efficiency and cannot provide a normalized schema.
Method #1:
It uses a third table and is the efficient way to store data in a relational database and provides a normalized schema. So where possible its better to use this approach, if the data needs to be existed before building the relation.

Changing the generated name of a foreign key in Hibernate

#OneToOne()
#JoinColumn(name="vehicle_id", referencedColumnName="vehicleId")
public Vehicle getVehicle() {
return vehicle;
}
My UserDetails class has a one-to-one mapping with the Entitity class Vehicle. Hibernate creates the 2 tables and assigns a generic Foreign Key, which maps the vehicle_id column (UserDetails table.) to the primary key vehicleId (Vehicle table).
KEY FKB7C889CEAF42C7A1 (vehicle_id),
CONSTRAINT FKB7C889CEAF42C7A1 FOREIGN KEY (vehicle_id) REFERENCES vehicle (vehicleId)
My question is : how do we change this generated foreign key, into something meaningful, like Fk_userdetails_vehicle for example.
Since JPA 2.1, you can use the #javax.persistence.ForeignKey annotation:
#OneToOne()
#JoinColumn(name="vehicle_id", referencedColumnName="vehicleId", foreignKey=#ForeignKey(name = "Fk_userdetails_vehicle"))
public Vehicle getVehicle() {
return vehicle;
}
Prior to JPA 2.1, you could use Hibernate’s #org.hibernate.annotations.ForeignKey annotation, but this is now deprecated:
#OneToOne()
#JoinColumn(name="vehicle_id", referencedColumnName="vehicleId")
#ForeignKey(name="Fk_userdetails_vehicle")
public Vehicle getVehicle() {
return vehicle;
}
Also you can use #ForeignKey embedded in #JoinColumn like this:
#JoinColumn(name = "BAR_ID", foreignKey = #ForeignKey(name = FK_BAR_OF_FOO))
for #ManyToMany relations you can use foreignKey and inverseForeignKey embedded in #JoinTable like this:
#JoinTable(name = "ARC_EMPLOYEE_OF_BAR"
, joinColumns = {#JoinColumn(name = "BAR_ID")}
, inverseJoinColumns = {#JoinColumn(name = "EMPLOYEE_ID")}
, uniqueConstraints = {#UniqueConstraint(name = "ARC_UK_EMPLOYEE_OF_BAR", columnNames = {"EMPLOYEE_ID", "BAR_ID"})}
, foreignKey = #ForeignKey(name = "ARC_FK_BAR_OF_EMPLOYEE")
, inverseForeignKey = #ForeignKey(name = "ARC_FK_EMPLOYEE_OF_BAR"))
You can do it also by implementing ImplicitNamingStrategy.determineForeignKeyName and using
configuration.setImplicitNamingStrategy(
new MyImplicitNamingStrategy())
which is nice as you don't have to do it manually again and again. However, it may be hard to put the relevant information there. I tried to concat everything I got (using three underscore to separate the parts) and ended up with
FK_ACCESS_TEMPLATE____TEMPLATE____TEMPLATE_ID____TEMPLATE_ID__INDEX_B
which isn't really better than
FKG2JM5OO91HT64EWUACF7TJCFN_INDEX_B
I guess, using just the referenced table and column names together with a number for uniqueness would be just fine.
Note also that this seems to be legacy Hibernate stuff, unsupported by JPA.
OTOH it works with Hibernate 5.0.1 (just one week old).
May be you should try this, adding #ForeignKey annotation :
#ManyToOne
#ForeignKey(name="FK_some_model")
#JoinColumn(name="some_model_id")
private SomeModel someModel

In which case do you use the JPA #JoinTable annotation?

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 |
|_________|_________|
| | |
|_________|_________|

Categories

Resources