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.
Related
The Problem
I have a 1:n relation, but the n side shouldnt rely on constraints. So i actually wanna insert a EntityPojo via its future id, when its not saved yet ( Lets ignore that its a bad practice ). This looks kinda like this.
var relation = new RelationshipPojo();
.
.
.
relation.targets.add(session.getReference(futureID, EntityPojo.class));
session.save(relation);
// A few frames later
session.save(theEntityPojoWithTheSpecificId);
Cascading is not possible here, i only have its future ID, not a reference to the object i wanna save. Only its id it will have in the future.
#Entity
#Table(name = "relationship")
#Access(AccessType.FIELD)
public class RelationshipPojo {
.
.
.
#ManyToMany(cascade = {}, fetch = FetchType.EAGER)
public Set<EntityPojo> targets = new LinkedHashSet<>();
}
Question
How do we tell hibernate that it should ignore the constraints for this 1:n "target" relation ? It should just insert the given ID into the database, ignoring if that EntityPojo really exists yet.
Glad for any help on this topic, thanks !
For a much simpler solution, see the EDIT below
If the goal is to insert rows into the join table, without affecting the ENTITY_POJO table, you could model the many-to-many association as an entity itself:
#Entity
#Table(name = "relationship")
#Access(AccessType.FIELD)
public class RelationshipPojo {
#OneToMany(cascade = PERSIST, fetch = EAGER, mappedBy = "relationship")
public Set<RelationShipEntityPojo> targets = new LinkedHashSet<>();
}
#Entity
public class RelationShipEntityPojo {
#Column(name = "entity_id")
private Long entityId;
#ManyToOne
private RelationshipPojo relationship;
#ManyToOne
#NotFound(action = IGNORE)
#JoinColumn(insertable = false, updatable = false)
private EntityPojo entity;
}
This way, you'll be able to set a value to the entityId property to a non-existent id, and if an EntityPojo by that id is later inserted, Hibernate will know how to populate relationship properly. The caveat is a more complicated domain model, and the fact that you will need to control the association between RelationshipEntityPojo and EntityPojo using the entityId property, not entity.
EDIT Actually, disregard the above answer, it's overly complicated. Turing85 is right in that you should simply remove the constraint. You can prevent Hibernate from generating it in the first place using:
#ManyToMany(cascade = CascadeType.PERSIST, fetch = FetchType.EAGER)
#JoinTable(inverseJoinColumns = #JoinColumn(name = "target_id", foreignKey = #ForeignKey(name = "none", value = ConstraintMode.NO_CONSTRAINT)))
public Set<EntityPojo> targets = new LinkedHashSet<>();
The only caveat is that when you try to load RelationshipPojo.targets before inserting the missing EntityPojo, Hibernate will complain about the missing entity, as apparently #NotFound is ignored for #ManyToMany.
I'm starting my first project with Hibernate 4.2.21 and first with JPA 2.0, I want to create a relationship OneToMany Unidirectional. I saw a lot examples in version of Hibernate 3 but not much in 4.2.21 This example works perfectly but I don't know if is a good practice, I want to know the Opinion from another members about that?
Relationship One To Many:
-Parent Template:
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name = "template_id")
private Set<Variable> variables = new LinkedHashSet<Variable>();
-Child: Variable
#Column(name = "template_id", nullable = false)
Integer templateId;
According with this another post's.
Hibernate unidirectional one to many association - why is a join table better?
http://docs.jboss.org/hibernate/stable/annotations/reference/en/html_single/#entity-mapping-association-collections
A unidirectional one to many using a foreign key column in the owned entity is not that common and not really recommended. We strongly advise you to use a join table for this kind of association (as explained in the next section). This kind of association is described through a #JoinColumn
#Entity
public class Customer implements Serializable {
#OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER)
#JoinColumn(name="CUST_ID")
public Set<Ticket> getTickets() {
...
}
#Entity
public class Ticket implements Serializable {
... //no bidir
}
Unidirectional with join table
A unidirectional one to many with join table is much preferred. This association is described through an #JoinTable.
#Entity
public class Trainer {
#OneToMany
#JoinTable(
name="TrainedMonkeys",
joinColumns = #JoinColumn( name="trainer_id"),
inverseJoinColumns = #JoinColumn( name="monkey_id")
)
public Set<Monkey> getTrainedMonkeys() {
...
}
#Entity
public class Monkey {
... //no bidir
}
Finally the only way it's implement the bidirectional method... yes or no?
I am using Hibernate to create a database that I will use in my Java application.
I have two entities:
Role[ID, name, description]
UIElement[ID, name, description]
They have a many to many relationship. i.e. A role can have many UIElements, and a UIElement can be set to a number of roles. The two are related in the following table:
Role_UI[Role_Id, UI_ID, property]
Property is a varchar(20) or a String, for example, Read/Create/Edit
In my java application (In the Role Object, since total ownership) I have the many to many set-up as follows:
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "role_ui",
joinColumns = { #JoinColumn(name = "role_id", nullable = false, updatable = false) },
inverseJoinColumns = { #JoinColumn(name = "ui_id", nullable = false, updatable = false) }
)
private Map<UIElement, String> uiElements = new HashMap<>();
Is this correct? Will the String in the map be the property field from the database?
I think that's workaround.
You need to create a new Entity:
Role_UI[Role_Id, UI_ID, property]
#Entity
class Role_UI{
#ManyToOne
Role role;
#ManyToOne
UIElement element;
#Enumeration
Permission property;
}
enum Permission{
CREATE, EDIT, READ;
}
You should create a separate entity for ROLE_UI table and split the many-many to association into two one-to-many associations. Entity representing ROLE_UI table have the additional field (property).
Java Persistence/ManyToMany
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
I have two classes, say Group and Person with a ManyToMany-Relation that is mapped in a JoinTable.
If I delete a Person that has a relation to a Group, I want to delete the entry from the join table (not delete the group itself!).
How do I have to define the cascade-Annotations? I didn't found a really helpful documentation but several unsolved board discussions...
public class Group {
#ManyToMany(
cascade = { javax.persistence.CascadeType.? },
fetch = FetchType.EAGER)
#Cascade({CascadeType.?})
#JoinTable(name = "PERSON_GROUP",
joinColumns = { #JoinColumn(name = "GROUP_ID") },
inverseJoinColumns = { #JoinColumn(name = "PERSON_ID") })
private List<Person> persons;
}
public class Person {
#ManyToMany(
cascade = { javax.persistence.CascadeType.? },
fetch = FetchType.EAGER,
mappedBy = "persons",
targetEntity = Group.class)
#Cascade({CascadeType.?})
private List<Group> group;
}
Cascade will not clean up the leftover references to the deleted Person that remain on the Group object in memory. You have to do that manually. It seems like cascade should do this, but sadly that's not the way it works.
Based on the info provided in your question, I don't think you need any cascade options set on your Person or Group entities. It doesn't sound like they share a parent/child relationship where the existence of one depends upon the other. That's the kind of relationship where I would expect to see some cascade options.
I believe what you want is:
cascade = CascadeType.ALL
To remove the DB relation, remove the association from each group. Remove the person from the Group.persons collection and remove the Group from the Person.group collection, then persist your person object.
You can probably do it on a database specifically (depends on your database and it capabilities). By adding "on delete cascade" to the foreign key of the relationship table.