🏠Spring BootSpring Data JPA @ManyToOne Annotation

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.

many to one real world example
Multiple accounts that belong to a single branch of a bank

@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.

Spring data jpa and Many to one mapping in action
Many to one mapping in action

If you enable the JPA SQL logs, you can see the SQL queries fired during this request process.

Spring data JPA running two queries for ManyToOne mapping

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.

Spring data jpa using join fetch on Many To One mapping
JPA loading all @ManyToOne mappings in a single query using join fetch

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.

Related

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *

6 Comments

  1. 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.

  2. 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?

    1. 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.