最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

java - Spring Boot @OneToOne bidirectional relationship lazy loading does not work - Stack Overflow

programmeradmin0浏览0评论

i have three entities USER ,RefreshToken, and FotPassword

public class User {
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Id
    private Long id;
    private String email;
    private String password;

    @OneToOne(mappedBy = "user",fetch = FetchType.LAZY)
    private RefreshToken refreshToken;
    @Enumerated(EnumType.STRING)
    private Role role;

    @OneToOne(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true ,fetch = FetchType.LAZY)
    private FotPassword fotPassword;

      
    // getter setters



    }
    
@Entity
public   class RefreshToken {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer tokenId;

    @Column(nullable = false, length = 500)
    private String refreshToken;

    @Column(nullable = false)
    private Instant expirationTime;

    @OneToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "user_id", referencedColumnName = "id")
    private User user;

    //getter setters
@Entity
public class FotPassword {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    @Column(nullable = false)
    private Long otp;
    private Date expirationTime;
    @OneToOne(fetch = FetchType.LAZY)
    private User user;
    
    // getter setters
}

and i want to use this method to generate otp and send email to user when he fets his password.

@PostMapping("/verifyMail/{email}")
    public ResponseEntity<String> verifyEmail(@PathVariable String email) {
        long otp = fotPasswordService.generateAndStoreOtp(email);
        emailService.sendOtpEmail(email, otp);
        return ResponseEntity.ok("Email sent for verification");
    }
public void sendOtpEmail(String email, long otp) {
        SimpleMailMessage message = new SimpleMailMessage();
        message.setTo(email);
        message.setFrom("[email protected]");
        message.setSubject("OTP for Fot Password Request");
        message.setText("This is the OTP for your Fot Password request: " + otp);

        mailSender.send(message);
    }
public long generateAndStoreOtp(String email) {
        User user = userRepository.findByEmail(email)
                .orElseThrow(() -> new UsernameNotFoundException("Please provide a valid email!"));

        int otp = otpGenerator(); // Generates a 6-digit OTP

        String redisKey = "otp:" + email; // Store OTP with user's email as key

        // Store OTP with 70 seconds expiration
        redisTemplate.opsForValue().set(redisKey, otp, Duration.ofSeconds(70));

        return otp;
    }


But the problem is when i execute userRepository.findByEmail() method, it hit the db three times instead of only one. Because i annotated FetchType.LAZY in *@OneToOne annotations. So i expect this method hit the db only once. Can anyone help me out about this issue. And im not master in spring data jpa.

Hibernate: select u1_0.id,u1_0.email,u1_0.password,u1_0.role from users u1_0 where u1_0.email=?
Hibernate: select fp1_0.id,fp1_0.expiration_time,fp1_0.otp,fp1_0.user_id from fot_password fp1_0 where fp1_0.user_id=?
Hibernate: select rt1_0.token_id,rt1_0.expiration_time,rt1_0.refresh_token,rt1_0.user_id from refresh_token rt1_0 where rt1_0.user_id=?

i have three entities USER ,RefreshToken, and FotPassword

public class User {
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Id
    private Long id;
    private String email;
    private String password;

    @OneToOne(mappedBy = "user",fetch = FetchType.LAZY)
    private RefreshToken refreshToken;
    @Enumerated(EnumType.STRING)
    private Role role;

    @OneToOne(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true ,fetch = FetchType.LAZY)
    private FotPassword fotPassword;

      
    // getter setters



    }
    
@Entity
public   class RefreshToken {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer tokenId;

    @Column(nullable = false, length = 500)
    private String refreshToken;

    @Column(nullable = false)
    private Instant expirationTime;

    @OneToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "user_id", referencedColumnName = "id")
    private User user;

    //getter setters
@Entity
public class FotPassword {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    @Column(nullable = false)
    private Long otp;
    private Date expirationTime;
    @OneToOne(fetch = FetchType.LAZY)
    private User user;
    
    // getter setters
}

and i want to use this method to generate otp and send email to user when he fets his password.

@PostMapping("/verifyMail/{email}")
    public ResponseEntity<String> verifyEmail(@PathVariable String email) {
        long otp = fotPasswordService.generateAndStoreOtp(email);
        emailService.sendOtpEmail(email, otp);
        return ResponseEntity.ok("Email sent for verification");
    }
public void sendOtpEmail(String email, long otp) {
        SimpleMailMessage message = new SimpleMailMessage();
        message.setTo(email);
        message.setFrom("[email protected]");
        message.setSubject("OTP for Fot Password Request");
        message.setText("This is the OTP for your Fot Password request: " + otp);

        mailSender.send(message);
    }
public long generateAndStoreOtp(String email) {
        User user = userRepository.findByEmail(email)
                .orElseThrow(() -> new UsernameNotFoundException("Please provide a valid email!"));

        int otp = otpGenerator(); // Generates a 6-digit OTP

        String redisKey = "otp:" + email; // Store OTP with user's email as key

        // Store OTP with 70 seconds expiration
        redisTemplate.opsForValue().set(redisKey, otp, Duration.ofSeconds(70));

        return otp;
    }


But the problem is when i execute userRepository.findByEmail() method, it hit the db three times instead of only one. Because i annotated FetchType.LAZY in *@OneToOne annotations. So i expect this method hit the db only once. Can anyone help me out about this issue. And im not master in spring data jpa.

Hibernate: select u1_0.id,u1_0.email,u1_0.password,u1_0.role from users u1_0 where u1_0.email=?
Hibernate: select fp1_0.id,fp1_0.expiration_time,fp1_0.otp,fp1_0.user_id from fot_password fp1_0 where fp1_0.user_id=?
Hibernate: select rt1_0.token_id,rt1_0.expiration_time,rt1_0.refresh_token,rt1_0.user_id from refresh_token rt1_0 where rt1_0.user_id=?
Share Improve this question asked Mar 2 at 15:12 hasannn2666hasannn2666 791 silver badge5 bronze badges 2
  • See this article: thorben-janssen/hibernate-tip-lazy-loading-one-to-one – scrhartley Commented Mar 2 at 16:40
  • Thanks for answering. But it did not work for me. I tried both solutions. But still try to fetch other entities :) But i learnt why it happens... Thanks. – hasannn2666 Commented Mar 2 at 16:55
Add a comment  | 

1 Answer 1

Reset to default 3

According to Hibernate documentation.

Bidirectional @OneToOne lazy association

Although you might annotate the parent-side association to be fetched lazily, Hibernate cannot honor this request since it cannot know whether the association is null or not.

The only way to figure out whether there is an associated record on the child side is to fetch the child association using a secondary query. Because this can lead to N+1 query issues, it’s much more efficient to use unidirectional @OneToOne associations with the @MapsId annotation in place.

However, if you really need to use a bidirectional association and want to make sure that this is always going to be fetched lazily, then you need to enable lazy state initialization bytecode enhancement.

According to the documentation this is the expected behavior and if you want to have bidirectional relationship of OneToOne, you will have to try and fine tune it through bytecode enhancement.

So you can try adding the property hibernate.enhancer.enableLazyInitialization: true but as mentioned in the documentation, this setting is deprecated without an expected replacement.

发布评论

评论列表(0)

  1. 暂无评论