1

I am using springboot(spring-boot-starter-parent 2.3.0 version) along with Jpa. I have User class which is parent and UserEmails as a child entity.

User Entity

@Entity
@Table(name = "user")
public class User extends BaseEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;

    @NotEmpty(message = "Name cannot be empty")
    @Column(name = "name")
    private String name;

    @OneToMany(mappedBy = "user",fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    @JsonManagedReference
    private List<UserEmails> userEmails = new ArrayList<UserEmails>();
 
    //setters and getters

     public void setUserEmails(List<UserEmails> userEmails) {
       this.userEmails= userEmails;
       this.userEmails.forEach(userEmail->{
         userEmail.setUser(this);
       });
}
}

UserRoles Entity

@Entity
@Table(name = "user_emails")
public class UserEmails extends BaseEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;
    
    @Column(name="email")
    private String email;

    @JsonBackReference
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "user_id" , referencedColumnName = "id", nullable=true)
    private User user;

    //getters and setters
}

The issue is that, even though i have used fetch type as lazy at the both the sides of the relationship, still when i call getUserById or getAllUsers, jpa loads user emails as well.

UserServiceImpl

@Service
public class UserServiceImpl extends BaseServiceImpl implements UserService {

    private static final Logger logger = LoggerFactory.getLogger(UserServiceImpl.class);

    @Autowired
    UserDAO userDAO;

   @Override
    public User getUserById(int id) {
        logger.info("Executing getUserById() in user service");
        User user = null;
        
        try {
            user = userDAO.findById(id);
        } catch (DataAccessException e) {
            String message = "Some message"
            throw new Exception(message, e);
        }

        if (user == null) {
              String message = "Some message"
              throw new ResourceNotFoundException(message);
        }
        return user;
    }
} 

UserDAO

@Repository
public interface UserDAO extends JpaRepository<User, Long>{
    

    public User findById(int id);

}
coder12349
  • 437
  • 2
  • 12
  • 25

1 Answers1

0

Yes it can happen, this is happening if you use persisted object (in your case object of instance User) as return value in one of your controller (for example ResponseEntity<User> getUser(Long id) at the REST URL /user/{id}) If you have a similar REST controller lazy fetch of UserEmails will occur. This lazy fetch will occur basically at the return statement from the controller.

You have two possibilities:

  1. to define DTO object, for example, UserDTO, and mapper to map between User and UserDTO of

  2. to use other annotations like @JsonIgnoreProperties and @JsonProperty in User class:

         @Entity 
         @Table(name = "user") 
         @JsonIgnoreProperties({"userEmails"})
         public class User extends BaseEntity {
    
         @Id
         @GeneratedValue(strategy = GenerationType.IDENTITY)
         private int id;
    
         @NotEmpty(message = "Name cannot be empty")
         @Column(name = "name")
         private String name;
    
         @OneToMany(mappedBy = "user",fetch = FetchType.LAZY, cascade = CascadeType.ALL)
         @JsonManagedReference
         @JsonProperty("userEmails")
         private List<UserEmails> userEmails = new ArrayList<UserEmails>();
    
         //setters and getters
    
          public void setUserEmails(List<UserEmails> userEmails) {
            this.userEmails= userEmails;
            this.userEmails.forEach(userEmail->{
              userEmail.setUser(this);
            });
          }}
    

    .

Boris Kukec
  • 611
  • 3
  • 8
  • Thanks! for your reply. I tried as per your suggesstion and i can still see user emails being loaded eagerly. Basically i tried to print the user object in the service class itself i can see query logs being executed for fetching user emails as well. Seems like lazy loading is not being fetched from the return statment of the controller but from the service or dao itself( user = userDAO.findById(id)). – coder12349 Feb 03 '21 at 06:12
  • How did you print the user object? Because every time you touch a getUserEmails fetch will occur. Secondly, I hope you use log4jm if so add ` ` To determine exactly where lazy fetch occurs. Every time I had a similar problem, it was my mistake, usually made in a hurry. Sorry I didn't help you. – Boris Kukec Feb 03 '21 at 07:54
  • I just used system.out.println(user). Also i am using sl4j for logging. I got a solution from the below link, https://stackoverflow.com/questions/57169361/spring-boot-jpa-lazy-loading-is-not-working-for-one-to-one-mapping They are suggesting to use HttpMessageConverter , which prevents Serializable of lazy loaded objects, if there is no data – coder12349 Feb 03 '21 at 10:51