简体   繁体   中英

Spring Data JPA loads one to one relationship in two queries

I have following classes:

@Data
@Entity
@Table(name = "user_info")
public class UserIdentity implements UserDetails {
  @Id
  private Long id;
  private String username;
  private String password;
  private String facebookId;
  private String phoneNumber;
  private Timestamp unblockDate;
  @OneToOne(fetch = FetchType.EAGER, optional = false)
  @Fetch(FetchMode.JOIN)
  @JoinColumn(name = "role_id")
  private Role role;

  }

@Data
@Entity
@Table(name = "role")
public class Role {
  @Id
  private Long id;
  private String name;
  @ManyToMany
  @Fetch(FetchMode.JOIN)
  @JoinTable(name = "role_permission", joinColumns = @JoinColumn(name = "role_id", referencedColumnName = "id"), inverseJoinColumns = @JoinColumn(name = "permission_id", referencedColumnName = "id"))
  private List<Permission> permissions;
}

@Data
@Entity
@Table(name = "permission")
public class Permission {
  @Id
  private Long id;
  private String name;
}

And when I'm querying for user identity I can see these two queries generated in log:

Hibernate: select useridenti0_.id as id1_3_, useridenti0_.facebook_id as facebook2_3_, useridenti0_.password as password3_3_, useridenti0_.phone_number as phone_nu4_3_, useridenti0_.role_id as role_id7_3_, useridenti0_.unblock_date as unblock_5_3_, useridenti0_.username as username6_3_ from user_info useridenti0_ where useridenti0_.facebook_id=?
Hibernate: select role0_.id as id1_1_0_, role0_.name as name2_1_0_, permission1_.role_id as role_id1_2_1_, permission2_.id as permissi2_2_1_, permission2_.id as id1_0_2_, permission2_.name as name2_0_2_ from role role0_ left outer join role_permission permission1_ on role0_.id=permission1_.role_id left outer join permission permission2_ on permission1_.permission_id=permission2_.id where role0_.id=?

But I expect to see only one query. Can you please help me to find out what I'm doing wrong?

UPDATE I'm using CrudRepositor:

public interface UserIdentityRepository extends CrudRepository<UserIdentity, Long> {
  UserIdentity findByFacebookId(String facebookId);
  UserIdentity findByPhoneNumber(String phoneNumber);
}

and I noticed following thing - if I search using method findById() it works as expected, but if I use findByPhoneNumber() then it doesn't work.

The FetchType you are specifying on your entities is only used when using methods on the EntityManager that don't take any kind of query. This is what you are effectively doing when calling findById() .

But if you specify a query as Spring Data does for you when you use a query method as findByPhoneNumber() that query is used to determine the fetch strategy.

But you can control that by specifying an entity graph. You'd add an @NamedEntityGraph annotation to your entity class. And an @EntityGraph annotation to your query method referencing that named entity graph.

I have prepared a simple example over at Github

I used the trivial entity graph

@NamedEntityGraph(name = "joined", includeAllAttributes = true)

It fetches everything eagerly, which might actually be what you want.

The only way to have one query.. and it would only load the UserIdentity in your case is to set the relationship as LAZY:

@OneToOne(fetch = FetchType.LAZY, optional = false)
@Fetch(FetchMode.JOIN)
@JoinColumn(name = "role_id")
private Role role;

and then load it upon first usage in a transactional method.

If it meets your logical design you could make the Role.id a primary key and also a foreign key and use @MapsId as described in this article: link

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