Category: Spring Data JPA

Spring Data JPA : Auditing with @CreatedBy, @CreatedDate, @LastModifiedBy and @LastModifiedDate

 

What is database Auditing?

database auditing means that keeping a track and log of the events (specially create and update) associated with the persistent entities. It keeps a track of who created or changed an entity and when the change happened.

I have seen projects storing these things manually. Doing so becomes very complex because you will need to write it completely on your own, which will definitely require lots of code — and lots of code means less maintainability and less focus on writing business logic.

But why should someone need to go to this path when both JPA and Hibernate provide automatic auditing, which we can be easily configured in your project? Basically the following information are maintained with database auditing.

  • created user (who created)
  • created date (when created)
  • last updated user (who updated)
  • last updated date (when updated)

 

Auditing with Spring Data JPA

Spring Data JPA provides some set of convenient and useful annotations for database auditing. It helps to keep a track of who did the change and when it was done.

Here in this article, I will discuss how we can configure JPA to automatically persist the audit related columns (created and modified information) for any entity with following annotations. (All these annotations are owned by the Spring Data JPA)

  • @CreatedBy
  • @CreatedDate
  • @LastModifiedBy
  • @LastModifiedDate

Continue reading “Spring Data JPA : Auditing with @CreatedBy, @CreatedDate, @LastModifiedBy and @LastModifiedDate”

Spring Boot : Spring Data JPA Pagination

 

Introduction

The purpose of this article is to demonstrate the Pagination and its related features with Spring Boot and Spring Data JPA. Therefore in order to keep this article simple and well focused, i will discuss only the pagination related topics with Spring Data JPA. Here i have assumed that you have a prior (basic) knowledge of Spring Boot and Spring Data JPA and you know how to develop a basic application.

 

The source code and Project Structure 

The source code related to this article can be found at GitHub. Click here to download it.

Screen Shot 2018-07-10 at 4.14.03 PM.png

 

Running the application

The following command can be executed to run the application.

mvn spring-boot:run

Now the sample application is up and running.

 

UserController


import com.springbootdev.examples.jpa.examples.dto.response.user.UserResponse;
import com.springbootdev.examples.jpa.examples.model.User;
import com.springbootdev.examples.jpa.examples.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
@RestController
public class UserController
{
@Autowired
private UserService userService;
@PostMapping("/users")
public Map<String, Object> createUsers(@RequestBody User user)
{
int numOfRecords = 100;
String name = user.getName();
//creating 100 sample users
for (int index = 1; index <= numOfRecords; index++) {
User userNew = new User();
userNew.setName(name + " " + index);
userService.create(userNew);
}
Map<String, Object> response = new HashMap<String, Object>();
response.put("num_users_added", numOfRecords);
return response;
}
@GetMapping("/users")
public UserResponse getUsers(Pageable pageable)
{
Page page = userService.findUsers(pageable);
return new UserResponse(page);
}
@GetMapping("/users2")
public UserResponse getUsers2()
{
int pageNumber = 3;
int pageSize = 2;
Page page = userService.findUsers(PageRequest.of(pageNumber, pageSize));
return new UserResponse(page);
}
}

 

Lets look at each method in detailed.

 

Creating dummy data set

You can create the dummy set of data required to run this application by making following REST api endpoint invocation.

POST    http://localhost:8080/users

Screen Shot 2018-07-10 at 4.09.06 PM.png

createUsers :- As described above, this endpoint is used to create set of dummy data required to run this demonstration application.

Now your users table of the targeted database is populated with some dummy data entries.  Lets look at the paginated REST api endpoints implementation in detailed.

 

Pagination: – User specified page and page size  

In here the the related page that need to be fetched and page size (number of items need to be fetched) can be specified runtime (when the REST endpoint is invoked)

 

GET  http://localhost:8080/users?page=0&size=5

Screen Shot 2018-07-10 at 4.44.50 PM.png

The page number should start from zero and it may increase based on the number of records available and page size.

Here you can see that the page is 0 (first page)  and size (number of items per page)  is 5. You can invoke the above REST api endpoints by changing page and size parameters.

Lets look at the code of the method responsible for handling above REST Api invocation.

@GetMapping("/users")
public UserResponse getUsers(Pageable pageable) 
{
   Page page = userService.findUsers(pageable);
   return new UserResponse(page);
}

 

Notice that we haven’t passed RequestParams to our handler method . When the endpoint /users?page=0&size=5 is hit, Spring would automatically resolve the page and size parameters and create a Pageable instance with those values . We would then pass this Pageable instance to the Service layer ,which would pass it to our Repository layer .

 

 

Pagination: – Application specified page and page size  

In here the, page and page size is set by the application itself. The user does not have to provide any parameter and it is delated in the application with some pre-defined classes.

 

GET  http://localhost:8080/users2

Here is the method responsible for handling above REST api invocation. (Note: “users2“)

@GetMapping("/users2")
public UserResponse getUsers2()
{
 int pageNumber = 3;
 int pageSize = 2;

 Page page = userService.findUsers(PageRequest.of(pageNumber, pageSize));
 return new UserResponse(page);
}

 

Here you can see that the page is 3  and size (number of items per page)  is 2.  After invoking above endpoint, you will get the following result.

Screen Shot 2018-07-10 at 5.01.03 PM.png

 

 

UserRepository is not directly extended from PagingAndSortingRepository

If you look a the the source code of the UserRepository class, you will notice that it is not directly inherited from the PagingAndSortingRepository. You might be wondering how this pagination works without extending the PagingAndSortingRepository.

Let me explain. UserRepository is extended from the JpaRepository.

If you examine the source code of the JpaRepository, you will notice that it is extended from PagingAndSortingRepository. Therefore any repository which is inherited from the JpaRepository will have the related methods and functionalities related to pagination.

 

Pagination related details (more)

If you go through the source code, you can find three classes (application specific) that are developed to encapsulate the pagination related details. They can be listed as follows. (please find some time to go through those classes)

  • PaginationDetails
  • NextPage
  • PreviousPage

Those three classes help to display the paginated related details in well formatted and descriptive manner as follows.

Screen Shot 2018-07-10 at 5.13.37 PM.png

 

We have called the method of PagingAndSortingRepository:

Page<T> findAll(Pageable pageable);
It returns a Page. Page contains the information about pagination and actual data being retrieved.
getContent() method of Page can be used to get the data as a List.

In addition, Page has set of  methods (inherited from  Slice)  those can be used to retrieve different pagination related details.

 

If you have any queries, related to this article, please feel free to drop a comment or contact me.

 

What are the uses of @EntityScan and @EnableJpaRepositories annotations?

 

Sometimes you may have noticed that some of the spring application projects (specially spring boot applications) uses @EntityScan and @EnableJpaRepositories annotations as a part of configuring Spring Data JPA support for the application.

But some of the spring boot applications managed to complete their configurations and run the applications with Spring Data JPA WITHOUT those two annotations.

You might be having a little confusion about the real usages of those two annotations and when to use them? that is fine.  The purpose of this article is to describe about the real usages of those two annotations and giving a full picture/idea of how to and when to use them properly.

 

What is the Spring Boot main application package?

It is the package that contains the Spring Boot main configuration class that is annotated with @SpringBootApplication annotation.

@SpringBootApplication annotation

This annotation automatically provides the features of the following annotations

  • @Configuration
  • @EnableAutoConfiguration
  • @ComponentScan

 

Spring Boot Auto-Configuration Feature with @EnableAutoConfiguration

If you want to get the maximum advantage of spring boot’s auto configuration feature, it is expected to put all your class packages under spring boot main application package (directly in main package or indirectly as sub packages).

The @EnableAutoConfiguration will scan the main package and its sub packages when executing the spring boot auto configuration feature for class path dependencies. If any class or package that is outside from the main application package and it is required for completing auto configuration for some dependency, then should be declared in the main configuration class properly (with related annotation).

Then the @EnableAutoConfiguration will scan for those declared packages for detecting the required classes in the process of completing/doing the auto configuration for the application dependency declared in the class path. Those can de described as follows.

 

@EnableJpaRepositories

This will enable the JPA repositories that contains in the given package(s).

For instance, Enabling auto configuration support for Spring Data JPA required to know the path of the JPA the repositories. By default, it will scan only the main application package and its sub packages for detecting the JPA repositories. Therefore, if the JPA repositories are placed under the main application package or its sub package, then it will be detected by  the @EnableAutoConfiguration as a part of auto configuring the spring based configurations. If the repository classes are not placed under the main application package or its sub package, then the relevant repository package(s) should be declared in the main application configuration class with @EnableJpaRepositories annotation. Then this will enable the JPA repositories contains in the given/declared package(s).

e.g:-

@EnableJpaRepositories(basePackages = "com.springbootdev.examples.jpa.repositories")

 

 

@EntityScan 

If the entity classes are not placed in the main application package or its sub package(s), then it is required to declare the package(s) in the main configuration class with @EntityScan annotation. This will tells spring boot to where to scan for detecting the entities for the application. Basically @EnableAutoConfiguration will scan the given package(s) for detecting the entities.

e.g:-

@EntityScan(basePackages = "com.springbootdev.examples.entity")

 

Lets look at some fun  and real code examples. Here i am not going to explain you the Spring Data JPA here. It has already been discussed in my following article.

Click here to go to Spring Data JPA example

 

You can download the source code of this article from GitHub.

Click here to download.

 

If you open the project in your IDE, you will notice that repository and entity packages are not placed in the main application package.

Screen Shot 2017-12-31 at 2.24.47 PM.png

 

main application package

com.springbootdev.examples

 

JPA repository classes are  in package

com.springbootdev.domain.repository

 

entity classes are in package 

com.springbootdev.domain.entity

 

Therefore the entity classes location and JPA repositories location should be declared and enabled with @EntityScan and   @EnableJpaRepositories annotations respectively. Otherwise the application will fail to load.

Please refer the following Spring Boot main configuration class.


import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
@SpringBootApplication
@EntityScan(basePackages = {"com.springbootdev.domain.entity"})
@EnableJpaRepositories(basePackages = {"com.springbootdev.domain.repository"})
public class SpringBootDataJpaExampleApplication
{
public static void main(String[] args)
{
SpringApplication.run(SpringBootDataJpaExampleApplication.class, args);
}
}

 

Spring Boot and MySQL: Simple CRUD Application (REST Api) with Spring Data JPA

 

Today i am going to explain how to develop simple CRUD (Create,Retrieve,Update and Delete data) application with Spring Boot and  Spring Data JPA.  the application will be simple web application and which will expose few REST endpoints for CRUD related operations.

 

What is Spring Data JPA?

Spring Data JPA is a sub project that comes under the Spring Data project and it is under the umbrella/shelter of Spring family.  This will implements the JPA (Java Persistence Api) specification and have classes and methods for accessing and managing the data tier of the application. It tries to simplify the complexity associated with the data access layer of the application.  As i have already mentioned, Spring Data JPA  has following objectives.

  • provides a better implementation for the JPA Specification.
  • avoid the complexity associated with the Data Access layer of the application (by providing a framework that is easy to implement and  having robust features)

 

So it is enough with theories. lets explore and play with the framework.  As usual.  i have used the https://start.spring.io/  for generating the project.

Screen Shot 2017-12-30 at 7.30.47 PM.png

As you can see in the generation wizard, we are using two dependencies other than web dependency. That is JPA and MySQL.

JPA – Represents the maven dependency for Spring Data JPA.

MySQL – Represents the maven dependency for mysql-jdbc-connector 

 

So Lets Open the pom.xml file and look at the dependency section for identifying those dependencies.


<dependencies>
<!–Spring Data JPA Dependency–>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!–Spring Web Dependency–>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!–MySQL JDBC Connector –>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>

view raw

pom.xml

hosted with ❤ by GitHub

 

What we are going to build?

We will do CRUD related operations with a user object and ultimately we will build a REST api application with four endpoints  for CRUD related operations.

  • HTTP POST    /api/users   For creating the user
  • HTTP PUT     /api/users   For updating the user
  • HTTP DELETE    /api/users/:user_id   For deleting the user
  • HTTP GET    /api/users   For retrieving all the users

 

Where do we start?

Spring Data JPA is all about creating Entities and Repositories.

Entity lives in between java object (java class) and relational table. It helps to map the java object to relational table and relational table back to java object.

Repository helps to perform CRUD related operations with Entity.

The actual definition of Entity and Repository might be different from the explanation that i have given. Basically i just wanted to simply point out the exact purpose/role of Entity and Repository.

So Lets create our Entity and Repository.

 

Creating the Entity

The Entity class will be the following class.


import javax.persistence.*;
@Entity
@Table(name = "user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
private String country;
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

view raw

User.java

hosted with ❤ by GitHub

 

Things to Remember

The most important thing that you should note here is that we have used java persistence api (JPA annotations) based annotation here. No any framework specific annotation used. This is to compatible with other JPA implemented/supported ORM frameworks. For instance, if you have developed the data layer to compliant/adherent with JPA specification, then it will be possible to switch with different JPA implementations with minimal effort.

 

Creating the Repository

Creating a JPA based repository is very simple. You have to create an interface that extends the JpaRepository interface by providing the generic parameter, the Entity class and ID type.  Here is the sample code for implementing UserRepository.


import com.springbootdev.domain.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
public interface UserRepository extends JpaRepository<User, Long>
{
}

User – is the entity class

Long – is the data type of the ID (Primary Key).

All the methods required for the CRUD related operations will be inherited from the super level/parent interfaces. Therefore we do not have to explicitly add or implement CRUD related methods.

Now we have created both Entity class and Repository. Next step is to configure/provide the mysql connection details. It can be provided in the application.properties file.


## Spring DATA SOURCE Configurations
spring.datasource.url = jdbc:mysql://localhost:3306/spring_examples_db?useSSL=false
spring.datasource.username = root
spring.datasource.password = test123
## Hibernate Properties
# The SQL dialect makes Hibernate generate better SQL for the chosen database
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect
# Hibernate ddl auto (create, create-drop, validate, update)
spring.jpa.hibernate.ddl-auto = update

This will try to connect to the database  “spring_examples_db” running on local mysql server “localhost”  on port “3306”  by providing the username “root” and password “test123“.  You should change these properties based on your mysql server details.

 

Now we should implement our REST endpoints for creating, updating, removing and listing users etc…. Please refer the below UserController.


import com.springbootdev.examples.entity.User;
import com.springbootdev.examples.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api")
public class UserController {
@Autowired
private UserRepository userRepository;
@PostMapping("/users")
public User create(@RequestBody User user)
{
return userRepository.save(user);
}
@GetMapping("/users")
public List<User> findAll()
{
return userRepository.findAll();
}
@PutMapping("/users/{user_id}")
public User update(@PathVariable("user_id") Long userId, @RequestBody User userObject)
{
User user = userRepository.findOne(userId);
user.setName(userObject.getName());
user.setCountry(userObject.getCountry());
return userRepository.save(user);
}
@DeleteMapping("/users/{user_id}")
public List<User> delete(@PathVariable("user_id") Long userId)
{
userRepository.delete(userId);
return userRepository.findAll();
}
@GetMapping("/users/{user_id}")
public User findByUserId(@PathVariable("user_id") Long userId)
{
return userRepository.findOne(userId);
}
}

 

Creating the User

POST     /api/users

Here is the sample postman request and response.

Screen Shot 2017-12-31 at 11.37.01 AM.png

 

 @PostMapping("/users")
 public User create(@RequestBody User user)
 {
      return userRepository.save(user);
 }

 

@RequestBody – The body of the HTTP request will be mapped to the User class.

@ResponseBody – The retuned object will be mapped to the body of the HTTP response.

 

Retrieving all the users

GET  /api/users

Screen Shot 2017-12-31 at 11.47.03 AM.png

 

 

Retrieving a specific User/Resource

GET  /api/users/{user_id}

 

Screen Shot 2017-12-31 at 11.48.44 AM.png

 

Updating a Specific  User/Resource

PUT  /api/users/{user_id}

 

Screen Shot 2017-12-31 at 11.50.05 AM.png

 

Removing a Specific User/Resource

DELETE  /api/users/{user_id}

 

Screen Shot 2017-12-31 at 12.02.25 PM.png

Once the requested resource is successfully removed, it will return a list of available users.

Ok. we have completed all the REST endpoints for managing users with Spring Data JPA.

Finally we will look at the Spring Boot main configuration class.


import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringBootDataJpaExampleApplication
{
public static void main(String[] args)
{
SpringApplication.run(SpringBootDataJpaExampleApplication.class, args);
}
}

The configuration class is very simple and we have fully utilized the advantages of spring boot and no additional configurations have been made. We have taken fully advantage of spring boot auto configuration feature.

 

Why didn’t we use @EntityScan or @EnableJpaRepositories annotations here?

The package that contains the main Spring Boot Application configuration class (that is annotated with @SpringBootApplication) is known as the main application package.

If the entity classes and repository classes are placed under the spring boot main application package or its sub package, the spring boot will take care of detecting and scanning of entities and repositories.  In such case wed do not have to explicitly declare the packages of Entities and Repositories with @EntityScan and @EnableJpaRepositories annotations respectively.

Please refer the below project structure.

Screen Shot 2017-12-31 at 12.30.41 PM.png

If you look at the project file structure, you can notice that entity and repository packages are sub packages of the main application package. Therefore the entities and repositories placed under those packages will be automatically scanned and detected when the spring boot application is loaded.

If you want to know more about @EntityScan and @EnableJpaRepositories annotations, please go my article publish on that.

 

The full source code of this article can be found at GitHub.

Click here to Download The Source Code.

 

Build and Running the application

 

Build the application

mvn clean install -DskipTests

 

Run The application

java -jar target/spring-boot-data-jpa-example-0.0.1-SNAPSHOT.jar

 

Hope this article is helpful for you.

Thanks

Chathuranga Tennakoon

 

Spring Data JPA : Many To Many relationship mapping with extra columns

 

In a previous article (click here to visit it),  we have discussed about how to map many to many relationship with @ManyToMany annotation.  The @ManyToMany annotation is useful only in situations where we need to map only the many to many relationship between entities. It helps to generate a joined table with only the foreign keys that refer the participant tables.

But there are some situations where we need to add some extra columns to the joined table (newly generated intermediate table). In such case, we cannot use the @ManyToMany annotation for mapping many to many relationship between entities.

In this article, i am going to show you how to map many to many relationship and add extra column(s) to the joined table.

 

Please refer the following ER diagram.

erdplus-diagram.png

 

You can see that the relationship has two attributes known as from_date and to_date. When we convert the above ER-Diagram into the relational table structure, we should get the following tables.

Screen Shot 2018-03-20 at 11.09.14 PM.png

you can see that the attributes in the relationship has been added to the joined(intermediate) table which represents the relationship .

 

Lets implement this relationship with Spring Data JPA Project. The full source code of this article can be found at GitHub.

 

With @ManyToMany annotation, we just maintain only two entities. The intermediate(joined) table is generated as a result of mapping the many to many relationship between two entities.

But in this approach (adding extra columns) we need to maintain the relationship as a separate entity.  So here we will be maintaining three separate entities.

Lets look at each class in detailed.

 

StudentCourseEnrollment

This is used to represent the many to many relationship between Student and Course entities. All the extra columns that need to be added for the joined table will be declared here.


import javax.persistence.*;
import java.io.Serializable;
@Entity
@Table(name = "student_courses")
@IdClass(StudentCourseId.class)
public class StudentCourseEnrollment implements Serializable
{
@Id
@ManyToOne
@JoinColumn(name = "course_id", referencedColumnName = "id")
private Course course;
@Id
@ManyToOne
@JoinColumn(name = "student_id", referencedColumnName = "id")
private Student student;
@JoinColumn(name = "from_date")
private String fromDate;
@JoinColumn(name = "to_date")
private String toDate;
//TODO add getters and setters for all properties
}

 

As you can see that all the extra columns known as from_date and to_date has been declared here. In addition the relationship from StudentCourseEnrollment to Student and Course has been declared.

  • Many StudentCourseEnrollment entries can refer a single entry in the Course. Therefore the relationship from StudentCourseEnrollment to Course is many-to-one
  • Many StudentCourseEnrollment entries can refer a single entry in the Student. Therefore the relationship from StudentCourseEnrollment to Student is many-to-one

 

The joined table (intermediate table) will have a composite primary key which is composed with student_id and course_id.  Since it is a composite key, it will be declared in a separate class and added with @IdClass annotation.


import java.io.Serializable;
public class StudentCourseId implements Serializable
{
private Long course;
private Long student;
//TODO add getters and setters for all above properties
}

 

If you observe the StudentCourseId class properly, you may have noticed following points.

  • It contains only the properties that are required to form the primary key (composite primary key) of the StudentCourseEnrollment entity.
  • The property names of StudentCourseId class are exactly the same as the names in the related properties of the StudentCourseEnrollment class.
  • It must implements Serializable interface.

 

Student


import javax.persistence.*;
import java.util.List;
@Entity
@Table(name = "students")
public class Student
{
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
private String country;
private String email;
@Column(name = "created_at")
private String createdAt;
@OneToMany(mappedBy = "student")
private List<StudentCourseEnrollment> studentCourseEnrollments;
//TODO add getters and setters for all properties
}

view raw

Student.java

hosted with ❤ by GitHub

 

Here we are not going to map the direct relationship from Student to Course. We have used an intermediate entity called StudentCourseEnrollment. The relationship from Student to StudentCourseEnrollment is one-to-many. One student can enroll for many number of courses. The same theory is applied to the relationship from Course to StudentCourseEnrollment too.  mappedBy attributes is used to denote the property who owns the relationship.

 


import javax.persistence.*;
import java.util.List;
@Entity
@Table(name = "courses")
public class Course
{
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String title;
private String description;
private String major;
@Column(name = "created_at")
private String createdAt;
@OneToMany(mappedBy = "course")
private List<StudentCourseEnrollment> courseEnrollments;
//TODO add getters and setters for all properties
}

view raw

Course.java

hosted with ❤ by GitHub

 

Run and Test the source code.

You can get the source code related to this project from GitHub (Click here).  Please change the database configuration in application.properties based on your development environment. The run the following command to build the project.

mvn spring-boot:run

 

After executing the above command successfully, the table structure will be created with all declared properties.

Screen Shot 2018-03-21 at 11.25.42 AM.png

 

Here i have shown you how to map many to many relationship with extra columns for the joined table. In here we have not used @ManyToMany annotation and we have used some alternative different approach.

 

Spring Data JPA : Many To Many relationship mapping using @ManyToMany annotation

 

In this article, i am going to discuss how to map many to many relationship using @ManyToMany annotation available in JPA (Java Persistence Api).

 

Lets look at the following ER-Diagram.

erdplus-diagram (1).png

This ER-Diagram describes the relationship between Students and Courses. According to the diagram, the relationship can be described as below.

  • A student can enroll for many courses.
  • A course can be enrolled by many students.

Therefore the relationship between Students and Courses is many to many.

 

If we convert this relationship into relational database model, the table structure should looks like below.

Screen Shot 2018-03-20 at 4.31.41 PM

 

Lets implement this relationship with Spring Data JPA Project. The full source code of this article can be found at GitHub.

 

Here is the Student class.


import javax.persistence.*;
import java.io.Serializable;
import java.util.List;
@Entity
@Table(name = "students")
public class Student implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
private String country;
private String email;
@Column(name = "created_at")
private String createdAt;
@ManyToMany
@JoinTable(
name = "student_courses",
joinColumns = {@JoinColumn(name = "student_id", referencedColumnName = "id")},
inverseJoinColumns = {@JoinColumn(name = "course_id", referencedColumnName = "id")}
)
private List<Course> courses;
//TODO add getters and setters for all properties
}

view raw

Student.java

hosted with ❤ by GitHub

 

In Student class we are going the map the relationship from Student to Course. As you are already aware, it is a many to many relationship and there should be an intermediate table (joined table)  to map the relationship.  Therefore we have used the @JoinTable annotation to define the joined/intermediate table with its properties.

The name of the joined table (newly created intermediate table) will be “student_courses” as declared. In addition, there will be two join columns.

  • student_id – This will refer the “id” column of the  “students” table.
  • course_id – This will refer the “id” column of the “courses” table.

 

Here is the Course class.


import javax.persistence.*;
import java.io.Serializable;
import java.util.List;
@Entity
@Table(name = "courses")
public class Course implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String title;
private String description;
private String major;
@Column(name = "created_at")
private String createdAt;
@ManyToMany(mappedBy = "courses")
private List<Student> students;
//TODO getters and setters for all properties
}

view raw

Course.java

hosted with ❤ by GitHub

 

In Course class, we just declare only the relationship from Course to Student. Since the relationship is many to many, it is just declared with @ManyToMany annotation.  We are not going to map the relationship as it is already mapped by Student class and it is denoted with mappedBy attribute.

 

What is “mappedBy” attribute here?

mappedBy attributes defines the owner of the relationship. As you can see that relationship has been defined and owned by the courses property of the Student class. Therefore in the Course class it is declared with mappedBy=”courses”.

This will tells that the relationship has been mapped and owned by the courses property of the Student class.

 

Run and Test the source code.

You can get the source code related to this project from GitHub (Click here).  Please change the database configuration in application.properties based on your development environment. The run the following command to build the project.

mvn spring-boot:run

 

After executing the above command successfully, the table structure will be created with all defined properties.

Screen Shot 2018-03-20 at 8.40.37 PM.png

 

Here you can see that the joined table contains only the foreign key for reference entities/tables. we have not added any extra column for the joined table.

In next article, we will look at how to map  many to many relationship with extra columns for the joined table.