Spring Data JPA @ManyToOne Annotation
Let us learn how to use @ManyToOne annotation in Spring Data JPA with a spring boot application as an example.
What is Many To One Relationship?
The many-to-one mapping or association means that one parent record can have multiple child records. In other words, multiple records of a table can associate themselves with a common record in another table.
For example, The relationship of Account to Branch entities follows the Many to One mapping. Because multiple accounts can map to a single branch, but not vise versa.
@ManyToOne Annotation in Spring Data JPA
JPA allows you to define Many-to-one relationships between entity classes using the @ManyToOne annotation. For example, take a look at the records of Account and Branch tables here.
insert into branch (id, branch_name, branch_code)
values (1, 'South West branch', '6000000021');
insert into account (id, account_number, branch_id, full_name, balance)
values (1, '1231231231', 1, 'John1 Doe1', 100.23),
(2, '1231231232', 1, 'John2 Doe2', 201.76),
(3, '1231231233', 1, 'John3 Doe3', 403200.00),
(4, '1231231234', 1, 'John4 Doe4', 10120.44),
(5, '1231231235', 1, 'John5 Doe5', 43540.13);
Code language: SQL (Structured Query Language) (sql)
As you can see, “many” account records map to “one” branch record. Let’s write entities for these tables.
@Entity
public class Branch {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Integer id;
String branchCode;
String branchName;
// Getters and Setters
}
Code language: Java (java)
@Entity
public class Account {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Integer id;
String accountNumber;
String fullName;
BigDecimal balance;
@ManyToOne
Branch branch;
// Getters and Setters
}
Code language: Java (java)
As you see, We have included the Branch object itself instead of “branch_id”. And we also marked this object field as @ManyToOne. By using this annotation, we are letting Spring Data JPA know that this is a join with a many to one relationship. So when JPA evaluates this, it will find and map the entire branch object instead of just the branch_id.
By doing the many to one mapping, whenever we query for Account entities, we can also hold the information about its Branch details.
For example, let’s write Spring Jpa Repository interface for the Account entity. We are also adding a query to find each account by their ID.
@Repository
public interface AccountRepository extends JpaRepository<Account, Integer> {
@Query("select a from Account a where a.accountNumber = ?1")
public Account findAccount(String accountNumber);
}
Code language: Java (java)
Let’s use this repository in our service and controller layer to show the results.
@Service
public class AccountService {
private final AccountRepository accountRepository;
public AccountService(AccountRepository accountRepository){
this.accountRepository = accountRepository;
}
public Account getAccount(String accountNumber){
return accountRepository.findAccount(accountNumber);
}
}
Code language: Java (java)
@RestController
public class AccountController {
private AccountService accountService;
public AccountController(AccountService accountService) {
this.accountService = accountService;
}
@GetMapping(path = "/accounts/{accountNumber}")
public Account getAccount(@PathVariable("accountNumber") String accountNumber) {
return accountService.getAccount(accountNumber);
}
}
Code language: Java (java)
Now we can test the response on our controller and see that the branch object also comes as part of the response.
If you enable the JPA SQL logs, you can see the SQL queries fired during this request process.
So what happened here is that the application first loaded the Account record matching the account number. But because the Account record expects the details of the Branch entity as well, there is a second query to the BRANCH table.
Many To One mappings and Spring JPA queries(JPQL)
As we saw just now, @ManyToOne does two queries to load the data we expected. But we can avoid this entirely through JPQL join fetch. To demonstrate this, we need to rewrite the JPA method with joins as shown below.
@Query("select a from Account a join fetch a.branch where a.accountNumber = ?1")
public Account findAccount(String accountNumber);
Code language: Java (java)
By deliberately instructing JPQL to join fetch the records, we can gather all the details in one query to the database.
As you see in this screenshot, the DB requests happen only once using an inner join. Spring JPA will parse the SQL resultset and create the objects accordingly.
Summary
So far we have learned what is Many-to-one relationship is and how to implement them in spring JPA projects. You can find this example spring boot application in the GitHub repository.
Hi Great explanation. I like it the way you show how things look in db prospective with this association. The we can avoid round trip of query by using join. I request you to write article on joins like left, right, inner and full join for Springboo JPA that shows things in the same way you have shown. How things in Spring JPA and how things in DB and how they work.
I`m a bit confused.
We have account table with branch_id and in Entity specify only:
@ManyToOne
Branch branch;
How does Spring know that branch_id is an Id for branch if we did not defined it directly?
That’s the neat part of spring JPA. When Spring sees a @ManyToOne, It just checks the
@Id definition in the Branch class. Spring also generated column names based on the field names. So when you say the field “branch”, it would just add an “_id” to the branch, thus branch_id. You will see the same magic when you observe spring(hibernate) generating the DB schema for you.
When I tried http://localhost:8081/accounts/1231231231 on postman using Get request method, I got the following output. but I need to get branch data too.
{
“id”: 1,
“accountNumber”: “1231231231”,
“fullName”: “John1 Doe1”,
“balance”: 100.23
}
Hi can you please explain where we need to insert that data i mean which layer i m new to spring
Spring boot will load any inserts under `src/main/resources/data.sql`. So in my case I loaded them like this -> https://github.com/springhow/spring-boot-many-to-one/blob/master/src/main/resources/data.sql