简体   繁体   English

Spring Data JPA在两个查询中加载一对一关系

[英]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: 更新我正在使用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. 并且我注意到了以下事情-如果我使用方法findById()进行搜索, 则按预期进行,但是如果我使用findByPhoneNumber(),则它将不起作用。

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. 您在实体上指定的FetchType仅在EntityManager上使用不接受任何类型查询的方法时使用。 This is what you are effectively doing when calling findById() . 这是您在调用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. 但是,如果在使用查询方法作为findByPhoneNumber()时像Spring Data那样为您指定查询,则该查询将用于确定获取策略。

But you can control that by specifying an entity graph. 但是您可以通过指定实体图来控制它。 You'd add an @NamedEntityGraph annotation to your entity class. 您将在实体类中添加@NamedEntityGraph批注。 And an @EntityGraph annotation to your query method referencing that named entity graph. 还有一个@EntityGraph注释,用于引用该命名实体图的查询方法。

I have prepared a simple example over at Github 在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: 进行一个查询的唯一方法..并且仅在您的情况下加载UserIdentity是将关系设置为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 如果符合您的逻辑设计,则可以将Role.id用作主键,也可以将其作为外键,并使用@MapsId ,如本文所述: link

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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