简体   繁体   中英

How can I fix N+1 problem in Hibernate & Spring Data JPA?

I am trying to extract a list of all users from a database (Postgres), using Hibernate & Spring Data JPA. But when the logs from Hibernate are enabled, I see that I get not one select, as I expect, but several:

Hibernate: select user0_.id as id1_1_, user0_.name as name2_1_ from user user0_
Hibernate: select roles0_.user_id as user_id1_2_0_, roles0_.roles_id as roles_id2_2_0_, role1_.id as id1_0_1_, role1_.name as name2_0_1_ from user_roles roles0_ inner join role role1_ on roles0_.roles_id=role1_.id where roles0_.user_id=?
Hibernate: select roles0_.user_id as user_id1_2_0_, roles0_.roles_id as roles_id2_2_0_, role1_.id as id1_0_1_, role1_.name as name2_0_1_ from user_roles roles0_ inner join role role1_ on roles0_.roles_id=role1_.id where roles0_.user_id=?
Hibernate: select roles0_.user_id as user_id1_2_0_, roles0_.roles_id as roles_id2_2_0_, role1_.id as id1_0_1_, role1_.name as name2_0_1_ from user_roles roles0_ inner join role role1_ on roles0_.roles_id=role1_.id where roles0_.user_id=?
Hibernate: select roles0_.user_id as user_id1_2_0_, roles0_.roles_id as roles_id2_2_0_, role1_.id as id1_0_1_, role1_.name as name2_0_1_ from user_roles roles0_ inner join role role1_ on roles0_.roles_id=role1_.id where roles0_.user_id=?

Here is all the code related to database entities:

@Entity
@Table(name = "t_users")
public class User {

    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    private Long id;
    private String name;

    @ManyToMany(fetch = FetchType.LAZY)                   
    private Set<Role> roles;
 }

@Entity
@Table(name = "t_roles")
public class Role {

    @Id
    @GeneratedValue(strategy= GenerationType.AUTO)
    private Long id;

    private String name;
 }
public interface UserRepository extends CrudRepository<User, Long> {

    List<User> findAllBy();
}

Tell me, please, how can I get rid of the extra selects so that only 1 remains in the end?

There is fetch = FetchType.LAZY in the @ManyToMany association

@ManyToMany(fetch = FetchType.LAZY)                   
private Set<Role> roles;

Hibernate shouldn't load roles. And additional selects shouldn't appear.

Possible reasons:

  1. Roles loaded afterwards by lazy loading. You can add log entry just after the first select. Also you can remove @Transactional annotation to have LazyInitializationException .

  2. You use a debugger. Roles can be loaded by debugger, because it uses, for example, toString() from User .

  3. Any method of User , not only getRoles().something() , can case lazy loading. For example toString() .

You can try to debug and see when the first role select appears.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM