@OneToMany is one of the most often used annotations in #JPA. Whenever a Parent Entity can have multiple Childs we use a Collection with the @OneToMany annotation.
import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;
import org.springframework.data.jpa.domain.AbstractPersistable;
@Entity
public class BankAccount extends AbstractPersistable<Long> {
@Column
private String iban;
@OneToMany
private Set<Transaction> transactions = new HashSet<>();
}
We should however stop here for a moment and think about the relationship.
How many Transactions will be there in a BankAccount?
Do i always need all Transactions of a BankAccount?
Do i want to filter and/or sort them?
This above case is a good example. A Bank account can have a few but also thousands of transactions. And most likely when fetching a BankAccount Entity we will never want load all it's transactions at once. Usually in banking applications in the dashboard we get shown the last X transactions ordered by date descending. In the history module of a banking application we might have filters for amount, transaction date, credit or debit, receiver or sender and so on and the data will probably be paged.
So this Collection of Transactions might not only be unnecesary, but might also cause a performance problem if someone will use it. On a developer machine we might never see a problem but in production this might be a whole different story. It means transferring a lot of data over the network and taking memory in the java app to keep the entities. We don't want that.
Another problem is that we cannot sort, filter or paginate this on the database side. OK, there is an @OrderBy annotation, but it is static. There is Hibernate's also @FilterDef but you might not want to work with Hibernate directly and it's a bit cumbersone.
Solution: Use a @Query and just @ManyToOne
In the example from above remove the @OneToMany Collection of Transactions and in the Transaction Entity just use a @ManyToOne relationship:
import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;
@Entity
public class Transaction extends AbstractPersistable<Long> {
@Column
private BigDecimal amount;
@ManyToOne
private BankAccount bankAccount;
}
Now to fetch Transactions for a given BankAccount you can use the following @Query:
interface TransactionRepository extends JpaRepository<Transaction, Long> {
@Query("SELECT trx FROM Transaction trx WHERE trx.bankAccount = :account")
List<Transaction> findTransactionsForBankAccount(BankAccount account);
}
From here you can extend this @Query adding a Spring Data JPA Sort to dynamically sort the Transactions. Or you can add a Spring Data JPA PageRequest object to paginate and simultaneously sort the Transactions. Last but not least you can use a Spring Data JPA Specification to dynamically filter Transactions.
When to use @OneToMany?
Basically when we have a few elements or we always want all elements to be at hand and paging and complex filtering is not required. For example, we have a Parent entity and it has a collection of Childs. Most Parents have 1-3 children and we always want all.
Another example might be in a School a Class entity that has a Collection for all it's Pupils which might be always max. 30 (depending on the country).