What Is "The Inverse Side of the Association" in a Bidirectional JPA Onetomany/Manytoone Association

What is “the inverse side of the association” in a bidirectional JPA OneToMany/ManyToOne association?

To understand this, you must take a step back. In OO, the customer owns the orders (orders are a list in the customer object). There can't be an order without a customer. So the customer seems to be the owner of the orders.

But in the SQL world, one item will actually contain a pointer to the other. Since there is 1 customer for N orders, each order contains a foreign key to the customer it belongs to. This is the "connection" and this means the order "owns" (or literally contains) the connection (information). This is exactly the opposite from the OO/model world.

This may help to understand:

public class Customer {
// This field doesn't exist in the database
// It is simulated with a SQL query
// "OO speak": Customer owns the orders
private List<Order> orders;
}

public class Order {
// This field actually exists in the DB
// In a purely OO model, we could omit it
// "DB speak": Order contains a foreign key to customer
private Customer customer;
}

The inverse side is the OO "owner" of the object, in this case the customer. The customer has no columns in the table to store the orders, so you must tell it where in the order table it can save this data (which happens via mappedBy).

Another common example are trees with nodes which can be both parents and children. In this case, the two fields are used in one class:

public class Node {
// Again, this is managed by Hibernate.
// There is no matching column in the database.
@OneToMany(cascade = CascadeType.ALL) // mappedBy is only necessary when there are two fields with the type "Node"
private List<Node> children;

// This field exists in the database.
// For the OO model, it's not really necessary and in fact
// some XML implementations omit it to save memory.
// Of course, that limits your options to navigate the tree.
@ManyToOne
private Node parent;
}

This explains for the "foreign key" many-to-one design works. There is a second approach which uses another table to maintain the relations. That means, for our first example, you have three tables: The one with customers, the one with orders and a two-column table with pairs of primary keys (customerPK, orderPK).

This approach is more flexible than the one above (it can easily handle one-to-one, many-to-one, one-to-many and even many-to-many). The price is that

  • it's a bit slower (having to maintain another table and joins uses three tables instead of just two),
  • the join syntax is more complex (which can be tedious if you have to manually write many queries, for example when you try to debug something)
  • it's more error prone because you can suddenly get too many or too few results when something goes wrong in the code which manages the connection table.

That's why I rarely recommend this approach.

Bidirectional OneToMany vs OneToOne association with mappedBy attribute

If you use cascade = PERSIST on the @OneToMany you should see the same error. The difference is just that Hibernate does not try to "manage" *-to-many associations on the inverse side i.e. where the mappedBy is present.

Hibernate/JPA ManyToOne vs OneToMany

Suppose you have an Order and an OrderLine. You can choose to have a unidirectional OneToMany between Order and OrderLine (Order would have a collection of OrderLines). Or you can choose to have a ManyToOne association between OrderLine and Order (OrderLine would have a reference to its Order). Or you can choose to have both, in which case the association becomes a bidirectional OneToMany/ManyToOne association.

The solution you choose mainly depends on the situation, and on the level of coupling between the entities. For example, if a user, a company, a provider all have many addresses, it would make sense to have a unidirectional between every of them and Address, and have Address not know about their owner.

Suppose you have a User and a Message, where a user can have thousands of messages, it could make sense to model it only as a ManyToOne from Message to User, because you'll rarely ask for all the messages of a user anyway. The association could be made bidirectional only to help with queries though, since JPQL queries join between entities by navigating through their associations.

In a bidirectional association, you could be in a situation where the graph of objects is inconsistent. For example, Order A would have an empty set of OrderLines, but some OrderLines would have a reference to the Order A. JPA imposes to always have one side of the association being the owner side, and the other side being the inverse side. The inverse side is ignored by JPA. The owner side is the side that decides what relation exists. In a OneToMany bidirectional association, the owner side must be the many side. So, in the previous example, the owner side would be OrderLine, and JPA would persist the association between the lines and the order A, since the lines have a reference to A.

Such an association would be mapped like this:

in Order :

@OneToMany(mappedBy = "parentOrder") // mappedBy indicates that this side is the 
// inverse side, and that the mapping is defined by the attribute parentOrder
// at the other side of the association.
private Set<OrderLine> lines;

in OrderLine :

@ManyToOne
private Order parentOrder;

Java JPA ORM OneToMany ManyToOne

I think the approach you are trying to follow is incorrect. You do not need to have a map object storing the keys and related entities between the association.

A simple OneToMany mapping can be achieved by something as minimal as the following example:

@Data
@Entity
@Table(name = "users")
public class User {

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;

@OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "user")
private List<Quiz> quizzes = new ArrayList<>();

}

And

@Data
@Entity
@Table(name = "quizzes")
public class Quiz {

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;

@JoinColumn(name = "user_id")
@ManyToOne(fetch = FetchType.LAZY)
@ToString.Exclude
private User user;

}

I also think that the tutorial you are looking at, might be steering you towards a wrong direction. Check the following links for more concrete information:

https://vladmihalcea.com/the-best-way-to-map-a-onetomany-association-with-jpa-and-hibernate/

https://thorben-janssen.com/best-practices-many-one-one-many-associations-mappings/

Non-owning entity side of @OneToOne/@ManyToOne/@ManyToMany

In the bi-directional relationship betweens two objects ,you have to choose which sides to manage the relationship. From the database perspective , managing the relationship means managing the value of some FK column that link between two tables. The side that managing it is called owning side. Otherwise, it is call non-owning side.

So back to your example on ProjectManager and Project. Which object is the owning side depends on which object you choose to manage their relationship.

If you choose ProjectManager to be the owning side (hence Project is the non-owning side), only the values of ProjectManager#getProjects() will be used to determine the value of such FK column. (i.e. project table 's project_manager_id column in this case) The value of Project#getProjectManager() will be ignored and does not affect the value of this FK column.

In term of JPA mapping , it is :

@Entity
@Table(name="project_manager")
public class ProjectManager{

@OneToMany
private List<Project> projects = new ArrayList<>();

}

@Entity
@Table(name="project")
public class Project {

@ManyToOne
@JoinColumn(name = "project_manager_id")
private ProjectManager projectManager;
}

On the other hands , if you choose Project to the owning side (hence ProjectManager is the non-owning side) , only the value of Project#getProjectManager() will be used to determine the value of this FK column while the value of ProjectManager#getProjects() will be ignored. The JPA mapping in the case will be :

@Entity
@Table(name="project_manager")
public class ProjectManager{

@OneToMany(mappedBy="projectManager")
private List<Project> projects = new ArrayList<>();

}

@Entity
@Table(name="project")
public class Project {

@ManyToOne
@JoinColumn(name = "project_manager_id")
private ProjectManager projectManager;
}

P.S: I explain it using property access , hopefully you should get the idea.



Related Topics



Leave a reply



Submit