Say I have entity A the is with @OneToMany annotation that reference a class B that has the @ManyToOne annotation, that is B is a child of A.
Can I model it when I have many children? say thousands of them...for example I have a table B that has 6000 rows with a foreign key to table A . Is JPA with @OneToMany suitable for the job?
CodePudding user response:
The short answer is: it depends ...
Such annotations only declare how your Entities are connected and how they depend on each other (nothing related to how many rows will be in the tables) ... When they're used, the JPA provider allows you to:
Create JPA Queries that navigate through your entities path.
Load entities into memory for you without creating/executing other queries by yourself, just by accessing a getter or setter on any entity ...
The "performance issues" may appear depending on your use cases, how big is your data, how you access the data and other conditions as well (By the way, I´m quoting because maybe a couple of seconds more could not be a bottle neck for you) ... let me give you some examples.
Let's assume that our entities are: Department
and Employee
... One Department
can have many Employees
(so it will have a list of Employees annotated as @OneToMany
) ... An Employee
can work only in one Department
(so it will have an department property annotated as @ManyToOne
) ...
Loading issues that you may encounter:
Problem #1: You need to load an Employee
from the database ... By default, @ManyToOne
have an EAGER
fetch plan, which means that once you load an Employee
, the JPA Provider will load for you his associated Department
also (and to do that, it will execute another query). The problem here is that you may not need the Department
information for your use case and the JPA Provider is going to the database without reason. Solution: mark the @ManyToOne
as fetch LAZY
.
Problem #2: You mark the @ManyToOne
as fetch LAZY
, but you have a business case where you really need the Employee
plus his Department
info ... you want to do this in just one trip to database (only one query) and avoid what the JPA Provider did in the Problem #1. Solution: You can load your Employee
entity using fetch graph.
Problem #3: You're a loading a group of Employees
, and you need to access their Department
info ... you load such group and for each of employee, you access his department as: employees.stream().map(Employee::getDepartment) ...
you noticed that the JPA Provider is calling the database N times more to get the department info ... this is called the: N 1 problem
(you can search about it) ... Solution: use fetch graph or configure you JPA Provider to load the entities using a batch strategy.
Problem #4: You need to load an Department
and access to all its Employees
... you noticed that by doing: department.getEmployees(), takes some time to load all the data into memory. Solution: Check your database model, It may required to have some indexes in it.
Problem #5: You need to load an Department
and access to all its Employees
... you have indexes and all, but you know for sure that the tables have millions of rows ... you don't need to load all your data at once because it may produce OutOfMemoryProblems
. Solution: you load the Employees
by looping and using a bulk strategy (see, MaxResults and FirstResult)
As you can see, the problem and the solutions are closely related to the business case.
To summarize,
Use the annotations, and make some tests using a sample of data as the one you're planning to encounter in production, run your numbers. As a rule of thumb, it is always good idea to make your relationships
fetch=LAZY
.If you think there are performance issues, start by enabling the JPA provider debug mode. It will show you the queries that are being executed and it will help you to detected what problem you're facing ...
Once you identify the problem, start proposing solutions taking into account your business case. Remember, there is no silver bullet or one-size-fits-all solution, so the right solution will be linked to your use case ...
Happy Coding ...