Assuming the following simple schema:
Entity table - attributes common to all entities
entityId, timeCreated,...
Comments table - some entities have a collection of these
entityId, commentId, commentText,....
Person table.
pensonId (entityId), firstName, lastName,...
And the following Java inheritance structure:
BaseEntity - abstract - entity, join inheritance with a discriminator
CommentedEntity - abstract - introduces `comments` collection - mapping tbd
Person - concrete - entity - mapping trivial
How would we map bidirectional relationship between CommentedEntity and Comment? The code below is my best interpretation of examples I have found.
CommentedEntity
#OneToMany(mappedBy="owner", fetch=FetchType.EAGER)
private List<Comment> comments = new ArrayList<>();
Comment
#ManyToOne(fetch=FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name="entityId", referencedColumnName="entityId")
private CommentedEntity owner;
Since CommentedEntity is an abstract class, this doesn't work.
owner references an unknown entity.
Making CommentedEntity an entity, would require giving it an id, so I don't think that makes
sense.
Since multiple concrete entities will have comments
collection, concrete entity name cannot be used in mapping.
How would we then bidirectionally map a person.commentList property?
If Person extends CommentedEntity then you wouldn't need an CommentedEntity owner for the Person entity because Person is part of aCommentedEntity`. In other words, there is no need for an owner field because a Person is a CommentedEntity.
Related
I am designing a data entry application using EntityFramework (Code First) to collect customers detail.
The data structure required is simple.
Customer entity has some flat and one-to-many details (eg. Name, Telephone Numbers, etc.) and then a large number of many-to-many properties which always follow the same pattern which allow for multiple choice from a list (in the UI, this would be shown as a checkbox list), which the user can also add items to. For each of these multiple choice properties, there is also one Notes property that allow the user to explain why these details where connected to the customer (in other words, this is just a string within the Customer entity).
Because of the similarity of these properties and the relative simplicity of the data, I started looking to model using inheritance however I am now thinking that perhaps there are better ways to achieve this, especially because there would be a major benefit if the system allowed an admin user to add a new property of this type dynamically.
I am looking for any suggestions to achieve this without having to define and connect all the entities manually or at least to minimize the amount of code required to do so.
SQL does not know the concept of inheritance. However there are several strategies to let entity framework accept your inherited classes. Which strategy you ought to use depends on the type of queries you will ask most.
Suppose you have two classes Student and Teacher. Both classes have a lot of properties (and possibly methods) in common. You'd like to put them in a base class: Person. You don't want to be able to create a Person object, so your Person class will be abstract.
In C#:
abstract class Person
{
... // common person properties
}
class Teacher : Person
{
public int Id {get; set;} // Primary Key
... // Teacher properties
}
class Student : Person
{
public int Id {get; set;} // Primary Key
... // Student properties
}
You are not planning to create Person objects, only Teachers and Students. Therefore you could create a Teachers table and a Students table. The Teachers table contains all Teacher properties plus all Person properties. Similarly a Student table contains the Student properties and all Person properties. For every concrete (= non-abstract) class you create a table.
This strategy is called Table-Per-Concrete-Class (TPC). It is very similar to a composition: a Teacher 'has' Person properties, instead of inherits Person properties. It follows the old adagium "Favour composition over inheritance"
You inform entity framework that you want this strategy in your DbContext
class MyDbContext : DbContext
{
public DbSet<Student> Students {get; set;}
public DbSet<Teacher> Teachers {get; set;}
// we don't want a Person table, so no DbSet<Person>
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// Tell entity framework that the Teacher Table will include Person properties
modelBuilder.Entity<Teacher>().Map(m =>
{
m.MapInheritedProperties();
});
// Tell entity framework that the Student table will include Person properties
modelBuilder.Entity<Student>().Map(m =>
{
m.MapInheritedProperties();
});
}
}
A query for "Give me all Teachers who ..." or "Give me the Students that ..." will involve one table. However, if you ask: "Give me all Persons that ..." will require a concat of two tables.
var result = myDbContext.Teachers.Cast<Person>()
.Concat(myDbContext.Students.Cast<Person>())
.Where(person => ... // something with Person properties)
.Select(person => ... // something with Person properties);
Whenever I need to model inheritence, I use this TPC strategy most of the time.
If you think that you'll be querying quite often for Persons that ... instead of Teachers who ..., consider using Table Per Type (TPT)
In TPT you'll have three tables: a Person table, containing all the Person properties, a Teacher table with the Teacher properties and a foreign key to the Person properties of this Teacher. Similarly you'll have a Student table with a foreign key to the Person properties it inherits.
Asking for "all Persons that ..." will only involve one table, whether the Person is a Student or a Teacher. Because you ask for Persons, you don't want any Student properties.
Asking for "all Teachers that ..." will always involve two tables, namely the Teacher table to get the Teacher properties and the Person table to access the Person properties.
So if you ask more often for "Persons who ..." that for "Teachers who ...", consider using TPT.
I was thinking that I understand the relations. But now.. Im not sure. I have a problem to add correct annotations.
I have classes Members and Relations.
In Relations
private GT_Member Mother;
private GT_Member Father;
private List<GT_Member> children;
One Member can be in several Relations as Mother or Father (reference to his sex) but he can be only in One relation as child.
I thought annotate Mother and Father as #OneToMany.
But I'm not sure if I can annotate List as #OneToOne ??
This seems like a problem in modeling the correct entity-relationship model for your database schema and visualizing your ORM (Object Relationship Model).
Rather than starting with classes Members and Relations, please first see what are the dominant data-entities in your system. And how would they be related to each other.
Personally I do not think Relation would be a good JPA entity.
Member looks more like a good entity and could embody the relations
Assuming one Father and one Mother, One to Many seems wrong but as a father or mother can have many children, the correct annotation should be #ManyToOne.
Children is definitely OneToMany, and yes you can annotate the List children as #OneToMany.
Member could have the following properties:
#Entity
public class Member implements Serializable{
#ManyToOne
private Member mother;
#ManyToOne
private Member father;
#OnetoMany
private List<Member> children;
}
This solves both your use cases and in this simple example Relation class is not needed.
hope this helps.
Employment of Relation entity is because I want to save information about status of relation. Donc I will store information about all married etc. Entity relation has other fields like type (neutral, married, fiance etc...).
I have a person Entity and two list of persons in it, that i implemented this way (thanks to this post : Hibernate many-to-many association with the same entity) :
#ManyToMany
#JoinTable(name="tbl_friends",
joinColumns=#JoinColumn(name="personId"),
inverseJoinColumns=#JoinColumn(name="friendId")
)
private List<User> friends;
#ManyToMany
#JoinTable(name="tbl_friends",
joinColumns=#JoinColumn(name="friendId"),
inverseJoinColumns=#JoinColumn(name="personId")
)
private List<User> friendOf;
But, with the #ManyToMany annotation the Cascadings (MERGE,DELETE,etc..) doesn't work.
Is there a way to achieve the same mapping but with enabling Cascadings ?
Cascadings do work with many-to-many associations. But most of the time, there shouldn't be any cascade set on a many-to-many association: since a friend is a friend of many persons, you can't, for example, delete all John's friends (Paul and Matt) when you delete John. Indeed, many other people (Jack, Sarah) also have Paul and Matt as friends, and it would thus lead to a constraint violation.
The problem with your code is that the mapping is wrong. You have a single, bidirectional, many-to-many association here, but you mapped it as two unidirectional many-to-many associations, using the same join table.
In a bidirectional association, one side must be the inverse side. If you choose friendOf as the inverse side, it should thus be mapped as
#ManyToMany(mappedBy = "friends")
private List<User> friendOf;
Is it possible to create a basic FK relationship in JPA without involving the full entity target object?
As an example, imagine I have an entity:
#Entity(name = "Mechanic")
public class Mechanic {
#Id
private Long id;
//...
and a Car that I want to reference a Mechanic.id:
#Entity(name = "Car")
public class Car {
//...
#NotNull
private Long mechanic_id;
From an Object perspective, this would be a unidirectional, one to one relationship with the Car requiring a Mechanic.id and the Mechanic not needing any back reference to Car.
All I want out of this is to store the Mechanic.id ONLY. For the purposes of this question it is not useful to have a #OneToOne (or #OneToMany etc) relationship with the entity reference, I'm explicitly trying to avoid that but still retain the underlying integrity that a FK will provide.
JPA 2 and I'm using EclipseLink.
I have a m:n relationship book - borrow - user, the borrow is the join table.
The tables are given (can not be changed):
on one side they are used by jdbc app as well.
on the other side i would like to use them via jpa
book(book_id) - borrow(book_id,used_id) - user(user_id)
used jpa annotations:
User:
#OneToMany(targetEntity=BorrowEntity.class, mappedBy="user")
#JoinColumn(name="USER_ID", referencedColumnName="USER_ID")
private List<BorrowEntity>borrowings;
Book:
#OneToMany(targetEntity=BorrowEntity.class, mappedBy="book")
#JoinColumn(name="BOOK_ID", referencedColumnName="BOOK_ID")
private List<BorrowEntity>borrowings;
My problem is that by the settings above it adds some extra (undesired) fields to the borrow table:
'user_USER_ID' and 'book_BOOK_ID'
How can I configure the jpa annotations to keep just Borrow:user_id,book_id which is enough the many to one ?
Take a look at the picture which tells more:
First of all, since the borrow table is a pure join table, you don't need to map it at all. All you need is a ManyToMany association using this borrow table as JoinTable.
#ManyToMany
#JoinTable(name = "borrow",
joinColumns = #JoinColumn(name = "USER_ID"),
inverseJoinColumns = #JoinColumn(name = "BOOK_ID"))
private List<Book> borrowedBooks;
...
#ManyToMany(mappedBy = "borrowedBooks")
private List<User> borrowingUsers;
If you really want to map the join table as an entity, then it should contain two ManyToOne associations (one for each foreign key). So the following is wrong:
#OneToMany(targetEntity=BorrowEntity.class, mappedBy="user")
#JoinColumn(name="USER_ID", referencedColumnName="USER_ID")
private List<BorrowEntity>borrowings;
Indeed, mappedBy means: this association is the inverse side of the bidirectional OneToMany/ManyToOne association, which is already mapped by the field user in the BorrowEntity entity. Please see the annotations on this field to know how to map the association.
So the #JoinColumn doesn't make sense. It's in contradiction with mappedBy. You just need the following:
#OneToMany(mappedBy="user")
private List<BorrowEntity>borrowings;
The targetEntity is also superfluous, since it's a List<BorrowEntity>: JPA can infer the target entity from the generic type of the list.