【问题标题】:JPA: join tables but after that still makes sequence of queriesJPA:连接表,但之后仍然进行查询序列
【发布时间】:2014-06-27 07:54:28
【问题描述】:

如果这太愚蠢或不完整,我很抱歉。
我想进行一次查询,一次连接所有表。

@NamedQuery(name="User.findByLogin",
        query="SELECT u FROM User u " +
          "left outer join fetch u.userInfo ui " +
              "left outer join fetch u.freeEvents fe " +
              "left outer join fetch u.paidEvents pe " +
              "left outer join fetch fe.action fea " +
              "left outer join fetch pe.action pea " +
              "where u.login = :login")

它可以工作(没有错误,没有警告,生成了 sql),并且 SQL 很好,并且在 mysql 控制台中检索了所有适当的数据。但在那之后,它发出许多请求以通过 id 查找操作,并且看起来 hibernate 不知道它已经获取了所有需要的数据或者不知道如何处理它。
具有两个表的所有其他类似联接均有效且按预期工作。
顺便说一下,freeEvent 和paidEvent 是@Inheritance(strategy=InheritanceType.SINGLE_TABLE),它们的区别在于“rate_type”列。我尝试了急切和懒惰的提取,以及休眠 @Fetch 注释。
sql 本身:

select * #---lots of fields here, all valid
  from Users user0_ left outer join UserInfos userinfo1_ on user0_.`user_info_id`=userinfo1_.id
            left outer join Events freeevents2_ on user0_.id=freeevents2_.`user_id` and freeevents2_.rate_type=1
            left outer join FreeActions freeaction4_ on freeevents2_.`action_id`=freeaction4_.`id`
            left outer join Events paidevents3_ on user0_.id=paidevents3_.`user_id` and paidevents3_.rate_type=2
            left outer join PaidActions paidaction5_ on paidevents3_.`action_id`=paidaction5_.`id` where user0_.`login`='testuser'

问题:问题出在哪里?我的类是否注释不佳,或者这是正常的休眠行为,在哪里可以找到解决方案?

实体:(没有 getter、setter 和未被任何人映射的字段)

@Entity
@Table(name="Events")
@PersistenceUnit(name="default")
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="rate_type", discriminatorType=DiscriminatorType.INTEGER)
public class AbstractEvent {

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

    @ManyToOne(fetch=FetchType.LAZY)
    private models.User user;

    @Column(name="rate_type", insertable=false, updatable=false)
    private byte rateType;
}

@Entity
@DiscriminatorValue(RateTypeHelper.RATE_TYPE_FREE) //it just a constant of type integer == 1
@PersistenceUnit(name="default")
public class FreeEvent extends models.events.AbstractEvent {

    @OneToOne(cascade=CascadeType.PERSIST, fetch=FetchType.LAZY)
    @JoinColumn(name="`action_id`")
    private FreeAction action;

    public FreeAction getAction() {
        return action;
    }

    public void setAction(FreeAction action) {
        this.action = action;
    }
}

@Entity
@DiscriminatorValue(RateTypeHelper.RATE_TYPE_PAID)
@PersistenceUnit(name="default")
public class PaidEvent extends models.events.AbstractEvent {

    @OneToOne(cascade=CascadeType.PERSIST, fetch=FetchType.LAZY)
    @JoinColumn(name="`action_id`")
    private PaidAction action;

    public PaidAction getAction() {
        return action;
    }

    public void setAction(PaidAction action) {
        this.action = action;
    }
}

注意:这里的抽象动作现在不使用了。它没有任何字段或方法。只是为了未来的需要而空的班级。

@Entity
@Table(name="FreeActions")
@PersistenceUnit(name="default")
@NamedQueries({
    @NamedQuery(name="FreeAction.findByUserId",
                query="select a from FreeAction a " +
                      "left outer join fetch a.event e " +
                      "where e.user = :user_id")
})
public class FreeAction extends AbstractAction {

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @Column(name="`id`")
    private Long Id;

    @OneToOne(mappedBy="action", fetch=FetchType.LAZY)
    private models.events.FreeEvent event;
}

@Entity
@Table(name="PaidActions")
@PersistenceUnit(name="default")
@NamedQueries({
    @NamedQuery(name="PaidAction.findByUserId",
                query="select a from PaidAction a " +
                      "left outer join fetch a.event e " +
                      "where e.user = :user_id")
})

public class PaidAction extends AbstractAction {    
    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @Column(name="`id`")
    private Long Id;

    @OneToOne(mappedBy="action", fetch=FetchType.LAZY)
    private models.events.PaidEvent event;
}

@Entity
@Table(name="Users")
@PersistenceUnit(name="default")
@NamedQueries({
    @NamedQuery(name="findFullUserById",
                query="select u from User u "+ 
                      "left outer join fetch u.userInfo ui " +
                      "where u.id = :id"),
    @NamedQuery(name="User.findByLoginPassword",
                query="SELECT u FROM User u left outer join fetch u.userInfo ui " +
                      "where u.login = :login and u.password = :password"),
    @NamedQuery(name="User.findByLogin",
                query="SELECT u FROM User u " +
                      "left outer join fetch u.userInfo ui " +
                      "left outer join fetch u.freeEvents fe " +
                      "left outer join fetch u.paidEvents pe " +
                      "left outer join fetch fe.action fea " +
                      "left outer join fetch pe.action pea " +
                      "where u.login = :login")
})
public class User {

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

    @OneToMany(fetch=FetchType.LAZY)
    @JoinColumn(name="`user_id`")
    private Set<FreeEvent> freeEvents;

    @OneToMany(fetch=FetchType.LAZY)
    @JoinColumn(name="`user_id`")
    private Set<PaidEvent> paidEvents;

    @OneToOne(cascade=CascadeType.PERSIST, fetch=FetchType.LAZY)
    @JoinColumn(name="`user_info_id`")
    private UserInfo userInfo;
}

@Entity
@Table(name="UserInfos")
@PersistenceUnit(name="default")
public class UserInfo {

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

    @OneToOne(mappedBy="userInfo")
    private User user;
}

【问题讨论】:

  • 你可以发布你的实体
  • 是否只是未获取的操作引用? JPA/JPQL 不支持嵌套连接获取,因此 Hibernate 可能只是忽略了 fe.action fea 和 pe.action pea 连接的 Fetch 部分,而不是抛出验证类型异常。除了更改映射之外,其他提供程序还有其他方法来支持嵌套获取,例如查询提示 - Hibernate 可能有类似的非 JPA 方法。

标签: java sql hibernate jpa join


【解决方案1】:

你为什么要使用左外连接? JPA 了解作为 User 成员的集合将使用连接来获取它们,尽管在需要时会很懒惰?

现在您看到更多请求的事实可能是因为您有 FetchType.LAZY,并且在您调用 getFreeEvents()、getPaidEvents()、getUserInfo() 之一时,hibernate 将触发另一个 sql 查询请求检索它们,因为此时它们尚未加载。

【讨论】:

  • 我使用左外连接,因为我想在一个请求中检索两个具有不同列的表。我用过 LAZY 和 EAGER,我知道。此外,当使用 fetch join 时,它总是 EAGER,有时我想获得类的一部分,例如,只是 User 没有 UserInfo 只是为了检查用户是否存在于数据库中。
  • 那些实体已经是用户的一部分。如果您想在获取用户时检索值,请使用 EAGER。它打破了你有 LAZY 的逻辑,然后使用 namedquery 急切地加载集合
  • 当我将 AbstractEvent 的子级与任何其他类连接时,它甚至使用 EAGER 的简单提取连接也使用选择序列。当我以相同的方式将 User 与 UserInfo 连接时,使用 fetch join(无论是内部、外部。左、右)它只进行一个查询,但是当我使用 FreeEvent 并将它与某些东西连接时,它首先准备一个包含所有数据的大 SQL ,然后又是许多请求。
【解决方案2】:

解决方案。 例如,我有 2 个类 - 用户和角色。当我创建这样的实体时

@Entity
@Table(name="Users")
@PersistenceUnit(name="default")
@javax.persistence.SequenceGenerator(name = "USERS_SEQ", sequenceName = "USERS_SEQ")
@NamedQueries({
    @NamedQuery(name="User.findByLoginPassword",
                query="SELECT u FROM User u left outer join fetch u.role r " +
                      "where u.login = :login and u.password = :password"),
    @NamedQuery(name="User.findByLogin",
                query="SELECT u FROM User u left outer join fetch u.role r " +
                      "WHERE u.login = :login"),
    @NamedQuery(name="User.findAll",
                query="SELECT u FROM User u left outer join fetch u.role r"),
    @NamedQuery(name="User.findByRoleId",
                query="SELECT u FROM User u left outer join fetch u.role r " +
                      "where r.id = :role_id")
}) 
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "USERS_SEQ")
    @Column(name="id")
    private Long Id;
    @Required
    private String login;
    @Required
    private String password;

    @ManyToOne
    @Required
    private Role role;
}

@Entity
@Table(name="Roles")
@javax.persistence.SequenceGenerator(name = "ROLES_SEQ", sequenceName = "ROLES_SEQ")
@NamedQueries({
    @NamedQuery(name="Role.findEmptyRole",
                query="SELECT r FROM Role r " +
                      "where r.canRead=" + FALSE + " and r.canWrite=" + FALSE + " and r.canExecute=" + FALSE + " and r.canAdministrate=" + FALSE),
    @NamedQuery(name="Role.findAdminRole",
                query="SELECT r FROM Role r " +
                      "where r.canRead=" + TRUE + " and r.canWrite=" + TRUE + " and r.canExecute=" + TRUE + " and r.canAdministrate=" + TRUE)
}) 
public class Role {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "ROLES_SEQ")
    @Column(name="id")
    private Long Id;

    @Column(name="name")
    private String name;
    @Column(name="r")
    private boolean canRead;
    @Column(name="w")
    private boolean canWrite;
    @Column(name="x")
    private boolean canExecute;
    @Column(name="a")
    private boolean canAdministrate;

    @OneToMany(fetch=FetchType.LAZY)
    @JoinColumn(name="role_id", referencedColumnName="id")
    private List<User> owners;
}

每次我得到用户时,不管是 EAGER 还是 LAZY,或者休眠 @Fetch 或 @Required,我都会收到 2 个请求。但是当我不使用字段时

List<User> owners 

在 Role 中,然后 FETCH 起作用(同样,无论天气如何我使用 LAZY 或 EAGER)。

我的课也是如此。我删除了指向所有者的反向链接,

//@OneToOne(mappedBy="action")
//private models.events.FreeEvent event;

//@OneToOne(mappedBy="action")
//private models.events.PaidEvent event;

等等。它现在使用一个请求通过 join fetch 获取所有数据。

如果我不了解 JPA 的某些机制,我很抱歉。

【讨论】:

    猜你喜欢
    • 2021-09-26
    • 2011-09-12
    • 2023-03-06
    • 1970-01-01
    • 1970-01-01
    • 2012-11-27
    • 1970-01-01
    • 2021-06-04
    相关资源
    最近更新 更多