Unlock Spring Data JPA: Master Many-to-Many Relationships Now!

We will delve into the following topics:

We will explore the following subjects:

  • Unidirectional many-to-many connections.
  • CRUD functions.

Let's start by modeling the entities.Entity modeling in progressNext, we will see how Hibernate constructs the tables for us.Hibernate table creationA junction table is generated as a result.

Since this is a Many-to-Many, Unidirectional relationship, the definition in the User entity is as follows, while the Role entity will not include any related definition.

The crucial point to highlight is the relationship definition in the User Entity.

@ManyToMany(targetEntity = Role.class, cascade = CascadeType.ALL)

private List<Role> roles;

Now, let's see how this is implemented in code. All CRUD functions are executed in the following code. We will adjust the UPDATE methods in the next article. The code has been discussed in previous articles, and a video tutorial is also available for a comprehensive explanation of the code.

User Entity

package com.notyfyd.entity;import javax.persistence.*;import java.util.List;@Entity@Table(name = "t_user")public class User {    @Id    @GeneratedValue(strategy = GenerationType.IDENTITY)    private Long userId;    private String firstName;    private String lastName;    private String contactNumber;    @Column(unique = true)    private String email;    @ManyToMany(targetEntity = Role.class, cascade = CascadeType.ALL)    private List<Role> roles;    public List<Role> getRoles() {        return roles;    }    public void setRoles(List<Role> roles) {        this.roles = roles;    }    public Long getUser Id() {        return userId;    }        public void setUser Id(Long userId) {        this.userId = userId;    }        public String getFirstName() {        return firstName;    }        public void setFirstName(String firstName) {        this.firstName = firstName;    }        public String getLastName() {        return lastName;    }        public void setLastName(String lastName) {        this.lastName = lastName;    }        public String getContactNumber() {        return contactNumber;    }        public void setContactNumber(String contactNumber) {        this.contactNumber = contactNumber;    }        public String getEmail() {        return email;    }        public void setEmail(String email) {        this.email = email;    }}

Role Entity

package com.notyfyd.entity;import javax.persistence.*;@Entity@Table(name = "t_role")public class Role {    @Id    @GeneratedValue(strategy = GenerationType.IDENTITY)    private Long id;    private String name;    private String description;    public Long getId() {        return this.id;    }    public void setId(Long id) {        this.id = id;    }    public String getName() {        return this.name;    }    public void setName(String name) {        this.name = name;    }    public String getDescription() {        return this.description;    }    public void setDescription(String description) {        this.description = description;    }}

Role Administration Controller

package com.notyfyd.controller;import com.notyfyd.entity.User;import com.notyfyd.repository.UserRepository;import com.notyfyd.service.UserService;import org.springframework.http.ResponseEntity;import org.springframework.web.bind.annotation.*;import java.util.List;@RestControllerpublic class UserController {    private final UserService userService;    private final UserRepository userRepository;    public UserController(UserService userService, UserRepository userRepository) {        this.userService = userService;        this.userRepository = userRepository;    }    /     * Creates a new user.     *      * @param user the user to be created     * @return the response entity containing the created user     */    @PostMapping("/user/create")    public ResponseEntity<Object> createUser(@RequestBody User user) {        return userService.createUser(user);    }    /     * Retrieves a user by ID.     *      * @param id the ID of the user to be retrieved     * @return the user with the specified ID, or null if not found     */    @GetMapping("/user/details/{id}")    public User getUser(@PathVariable Long id) {        return userRepository.findById(id).orElse(null);    }    /     * Retrieves a list of all users.     *      * @return the list of users     */    @GetMapping("/user/all")    public List<User> getUsers() {        return userRepository.findAll();    }    /     * Updates an existing user.     *      * @param id   the ID of the user to be updated     * @param user the updated user     * @return the response entity containing the updated user     */    @PutMapping("/user/update/{id}")    public ResponseEntity<Object> updateUser(@PathVariable Long id, @RequestBody User user) {        return userService.updateUser(user, id);    }    /     * Deletes a user by ID.     *      * @param id the ID of the user to be deleted     * @return the response entity containing the deletion result     */    @DeleteMapping("/user/delete/{id}")    public ResponseEntity<Object> deleteUser(@PathVariable Long id) {        return userService.deleteUser(id);    }}

Function Regulator

package com.notyfyd.controller;import com.notyfyd.entity.Role;import com.notyfyd.repository.RoleRepository;import com.notyfyd.service.RoleService;import org.springframework.http.ResponseEntity;import org.springframework.web.bind.annotation.*;import java.util.List;/ * Handles REST requests related to roles. */@RestControllerpublic class RoleController {    private final RoleService roleService;    private final RoleRepository roleRepository;    /     * Initializes the controller with the required services.     *     * @param roleService  the role service instance     * @param roleRepository the role repository instance     */    public RoleController(RoleService roleService, RoleRepository roleRepository) {        this.roleService = roleService;        this.roleRepository = roleRepository;    }    /     * Creates a new role.     *     * @param role the role to be created     * @return the response entity     */    @PostMapping("/role/create")    public ResponseEntity createRole(@RequestBody Role role) {        return roleService.addRole(role);    }    /     * Deletes a role by its ID.     *     * @param id the ID of the role to be deleted     * @return the response entity     */    @DeleteMapping("/role/delete/{id}")    public ResponseEntity deleteRole(@PathVariable Long id) {        return roleService.deleteRole(id);    }    /     * Retrieves a role by its ID.     *     * @param id the ID of the role to be retrieved     * @return the role instance, or null if not found     */    @GetMapping("/role/details/{id}")    public Role getRole(@PathVariable Long id) {        return roleRepository.findById(id).orElse(null);    }    /     * Retrieves all roles.     *     * @return a list of all roles     */    @GetMapping("/role/all")    public List getRoles() {        return roleRepository.findAll();    }    /     * Updates a role by its ID.     *     * @param id the ID of the role to be updated     * @param role the updated role instance     * @return the response entity     */    @PutMapping("/role/update/{id}")    public ResponseEntity updateRole(@PathVariable Long id, @RequestBody Role role) {        return roleService.updateRole(id, role);    }}

Role Data Access Interface

package com.notyfyd.repository;import com.notyfyd.entity.Role;import org.springframework.data.jpa.repository.JpaRepository;import org.springframework.stereotype.Repository;import java.util.Optional;/ * Facilitates data retrieval and manipulation for roles. */@Repositorypublic interface RoleRepository extends JpaRepository<Role, Long> {    /     * Retrieves a role based on its designated name.     *     * @param name the name of the role to be retrieved     * @return an Optional instance containing the role, if found     */    Optional findByName(String name);}

User Data Access Interface

package com.notyfyd.repository;import com.notyfyd.entity.User;import org.springframework.data.jpa.repository.JpaRepository;import org.springframework.stereotype.Repository;import java.util.Optional;/ * A specialized repository interface for user data management,  * leveraging JpaRepository for fundamental CRUD operations. */@Repositorypublic interface UserRepository extends JpaRepository<User, Long> {    /     * Fetches a user by their specified email address,      * returning an Optional instance to accommodate potential non-existence.     *      * @param email the email address to be searched     * @return an Optional containing the user, if located     */    Optional findByEmail(String email);}

User Service

package com.notyfyd.service;import com.notyfyd.entity.Role;import com.notyfyd.repository.RoleRepository;import org.springframework.http.ResponseEntity;import org.springframework.stereotype.Service;@Servicepublic class RoleService {    private RoleRepository roleRepository;    public RoleService(RoleRepository roleRepository) {        this.roleRepository = roleRepository;    }    /** Create a new role  */    public ResponseEntity<Object> addRole(Role role)  {        Role newRole = new Role();        newRole.setName(role.getName());        newRole.setDescription(role.getDescription());        Role savedRole = roleRepository.save(newRole);        if(roleRepository.findById(savedRole.getId()).isPresent()) {            return ResponseEntity.accepted().body("Successfully Created Role ");        } else            return ResponseEntity.unprocessableEntity().body("Failed to Create specified Role");    }    /** Delete a specified role given the id */    public ResponseEntity<Object> deleteRole(Long id) {        if(roleRepository.findById(id).isPresent()){            roleRepository.deleteById(id);            if(roleRepository.findById(id).isPresent()){                return ResponseEntity.unprocessableEntity().body("Failed to delete the specified record");            } else return ResponseEntity.ok().body("Successfully deleted specified record");        } else            return ResponseEntity.unprocessableEntity().body("No Records Found");    }    /** Update a Role */    public ResponseEntity<Object> updateRole(Long id, Role role) {        if(roleRepository.findById(id).isPresent()){            Role newRole = roleRepository.findById(id).get();            newRole.setName(role.getName());            newRole.setDescription(role.getDescription());            Role savedRole = roleRepository.save(newRole);            if(roleRepository.findById(id).isPresent())                return ResponseEntity.accepted().body("Role saved successfully");            else return ResponseEntity.badRequest().body("Failed to update Role");        } else return ResponseEntity.unprocessableEntity().body("Specified Role not found");    }}

Client Assistance

package com.notyfyd.service;import com.notyfyd.entity.User;import com.notyfyd.repository.RoleRepository;import com.notyfyd.repository.UserRepository;import org.springframework.http.ResponseEntity;import org.springframework.stereotype.Service;import org.springframework.transaction.annotation.Transactional;@Servicepublic class UserService {    private UserRepository userRepository;    private RoleRepository roleRepository;    public UserService(UserRepository userRepository, RoleRepository roleRepository) {        this.userRepository = userRepository;        this.roleRepository = roleRepository;    }    /** Create a new User */    public ResponseEntity<Object> createUser(User model) {        User user = new User();        if (userRepository.findByEmail(model.getEmail()).isPresent()) {            return ResponseEntity.badRequest().body("The Email is already Present, Failed to Create new User");        } else {            user.setFirstName(model.getFirstName());            user.setLastName(model.getLastName());            user.setMobile(model.getMobile());            user.setEmail(model.getEmail());            user.setRoles(model.getRoles());            User savedUser = userRepository.save(user);            if (userRepository.findById(savedUser.getId()).isPresent())                return ResponseEntity.ok("User Created Successfully");            else return ResponseEntity.unprocessableEntity().body("Failed Creating User as Specified");        }    }    /** Update an Existing User */    @Transactional    public ResponseEntity<Object> updateUser(User user, Long id) {        if(userRepository.findById(id).isPresent()) {            User newUser = userRepository.findById(id).get();            newUser.setFirstName(user.getFirstName());            newUser.setLastName(user.getLastName());            newUser.setMobile(user.getMobile());            newUser.setEmail(user.getEmail());            newUser.setRoles(user.getRoles());            User savedUser = userRepository.save(newUser);            if(userRepository.findById(savedUser.getId()).isPresent())                return  ResponseEntity.accepted().body("User updated successfully");            else return ResponseEntity.unprocessableEntity().body("Failed updating the user specified");        } else return ResponseEntity.unprocessableEntity().body("Cannot find the user specified");    }    /** Delete an User*/    public ResponseEntity<Object> deleteUser(Long id) {        if (userRepository.findById(id).isPresent()) {            userRepository.deleteById(id);            if (userRepository.findById(id).isPresent())                return ResponseEntity.unprocessableEntity().body("Failed to Delete the specified User");            else return ResponseEntity.ok().body("Successfully deleted the specified user");        } else return ResponseEntity.badRequest().body("Cannot find the user specified");    }}

We will revisit the update technique in our forthcoming edition.

application.properties

server.port=2003spring.datasource.driver-class-name= org.postgresql.Driverspring.datasource.url= jdbc:postgresql://192.168.64.6:30432/jpa-testspring.datasource.username = postgresspring.datasource.password = rootspring.jpa.show-sql=truespring.jpa.hibernate.ddl-auto=create

Access the source code at https://github.com/gudpick/jpa-demo/tree/many-to-many-unidirecctional-starter

Watch the instructional video tutorials

<iframe src=”https://www.youtube.com/embed/e9HwTpn9mgA” allow=”accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture” allowfullscreen></iframe>


Ava Parker

102 Blog posts

Comments