M:N relationship in JPA (wrapping given tables) - jpa

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.

Related

Having multiple fetching strategies (LAZY, EAGER) by custom condition

I have one entity class, which consists of multiple foreign key constraints, which are handled by ManyToMany etc.
public class MyExampleClazz {
.......
#ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinTable(name = "secondClazzEntities", joinColumns = #JoinColumn(name = "id"),
inverseJoinColumns = #JoinColumn(name = "id"))
List<MySecondClazz> secondClazz;
.....
}
For some cases, I would like to change the fetching strategy from e.g. from EAGER to LAZY and vice versa, because for some read operations, I don't need EAGER fetching (imagine a RESTful service , which offers only some small portion of data and not everything) but in most cases I need EAGER instead.
One option could be introduce an entity (for same table) but different annotation, but it would duplicate code and effort in regards of maintenance.
Are there other ways present, to achive the same result by doing less?
There're two layers where you can control data fetching in JPA:
At the level of entity class via fetch type and fetch mode
At the query level via the "join fetch" clause or using #EntityGraph
I suggest you use FetchType.LAZY, by default for almost all associations. And fetch them only when you need them via #EntityGraph.

Database not written to when relationship edited from the inverse side on a ManyToMany relationship

JPA Provider is EclipseLink. Having the following entities:
#Entity
class Symptom {
#ManyToMany(mappedBy = "symptoms")
private Set<Disorder> disorders;
}
#Entity
class Disorder {
#ManyToMany
#JoinTable(name = "disorder_symptoms")
private Set<Symptom> symptoms;
}
One symptom can be found on many disorders and many disorders can have many symptoms. Symptom is defined as the inverse (non-owning) side of the relationship, but this being a bidirectional relationship, it shouldn't matter.
When i add a new symptom to Disorder, database table disorder_symptoms is updated as expected. But when i add a disorder to Symptom, the table is not populated. Tried with cascade attribute set for either side, still no result.
Is there something wrong i'm doing here?

mapping list of Person in another person

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;

JPA Simple ForeignKey relationship

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.

JPA #EmbeddedId: How to update part of a composite primary key?

I have a many-to-many relationship where the link table has an additional property. Hence the link table is represented by an entity class too and called Composition. The primary key of Composition is an #Embeddable linking to the according entities, eg. 2 #ManyToOne references.
It can happen that a user makes an error when selecting either of the 2 references and hence the composite primary key must be updated. However due to how JPA (hibernate) works this will of course always create a new row (insert) instead of an update and the old Composition will still exist. The end result being that a new row was added instead of one being updated.
Option 1:
The old Composition could just be deleted before the new one is inserted but that would require that the according method handling this requires both the old and new version. plus since the updated version is actually a new entity optimistic locking will not work and hence last update will always win.
Option 2:
Native query. The query also increments version column and includes version in WHERE clause. Throw OptimisticLockException if update count is 0 (concurrent modification or deletion)
What is the better choice? What is the "common approach" to this issue?
Why not just change the primary key of Composition to be a UID which is auto-generated? Then the users could change the two references to the entities being joined without having to delete/re-create the Composition entity. Optimistic locking would then be maintained.
EDIT: For example:
#Entity
#Table(name = "COMPOSITION")
public class Composition {
#Id
#Column(name = "ID")
private Long id; // Auto-generate using preferred method
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn( .... as appropriate .... )
private FirstEntity firstEntity;
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn( .... as appropriate .... )
private SecondEntity secondEntity;
....