Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/401.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java Hibernate HQL连接获取非递归获取_Java_Hibernate_Jpa_Hql - Fatal编程技术网

Java Hibernate HQL连接获取非递归获取

Java Hibernate HQL连接获取非递归获取,java,hibernate,jpa,hql,Java,Hibernate,Jpa,Hql,我有以下查询和方法 private static final String FIND = "SELECT DISTINCT domain FROM Domain domain LEFT OUTER JOIN FETCH domain.operators LEFT OUTER JOIN FETCH domain.networkCodes WHERE domain.domainId = :domainId"; @Override public Domain find(Long domainId)

我有以下查询和方法

private static final String FIND = "SELECT DISTINCT domain FROM Domain domain LEFT OUTER JOIN FETCH domain.operators LEFT OUTER JOIN FETCH domain.networkCodes WHERE domain.domainId = :domainId";

@Override
public Domain find(Long domainId) {
    Query query = getCurrentSession().createQuery(FIND);
    query.setLong("domainId", domainId);
    return (Domain) query.uniqueResult();
}
域作为

@Entity
@Table
public class Domain {
    @Id
    @GenericGenerator(name = "generator", strategy = "increment")
    @GeneratedValue(generator = "generator")
    @Column(name = "domain_id")
    private Long domainId;

    @Column(nullable = false, unique = true)
    @NotNull
    private String name;

    @Column(nullable = false)
    @NotNull
    @Enumerated(EnumType.STRING)
    private DomainType type;

    @OneToMany(cascade = {
            CascadeType.PERSIST,
            CascadeType.MERGE
    }, fetch = FetchType.EAGER)
    @JoinTable(joinColumns = {
            @JoinColumn(name = "domain_id")
    }, inverseJoinColumns = {
            @JoinColumn(name = "code")
    })
    @NotEmpty
    @Valid // needed to recur because we specify network codes when creating the domain
    private Set<NetworkCode> networkCodes = new HashSet<>();

    @ManyToMany(fetch = FetchType.EAGER)
    @JoinTable(joinColumns = {
            @JoinColumn(name = "parent", referencedColumnName = "domain_id")
    }, inverseJoinColumns = {
            @JoinColumn(name = "child", referencedColumnName = "domain_id")
    })
    private Set<Domain> operators = new HashSet<>();
    // more
}
我猜这是因为我加入了操作符
元素,但它们必须自己加入


是否有一个HQL查询可以同时执行这两个任务?

您已将关联标记为“渴望”。因此,无论您在查询中做什么,Hibernate都将加载所有关联的域和加载域的网络代码。它将加载其他域的域和网络代码等,直到所有集合加载返回已加载的空集合或实体


要避免这种情况,请将集合设置为惰性(默认情况下)。然后加载一个包含运营商和网络代码的域就会加载它。

如果你知道你的树中只有两个级别,你有没有想过加入更深的一个级别。像下面这样的

SELECT DISTINCT domain FROM Domain domain 
  LEFT OUTER JOIN FETCH domain.operators operators1 
  LEFT OUTER JOIN FETCH domain.networkCodes 
  LEFT OUTER JOIN FETCH operators1.operators operators2 
  LEFT OUTER JOIN FETCH operators1.networkCodes
WHERE domain.domainId = :domainId

虽然没有很好的记录,但您是否尝试过设置? 您可以通过使用Criteria API:
domainCriteria.setFetchMode(“运算符”,JOIN)
或在关系定义处使用
@Fetch(JOIN)
来完成此操作


注释(并且只有看起来的注释)还允许设置获取模式
SUBSELECT
,这至少应该限制Hibernate最多执行3个查询。不知道您的数据集,我认为这应该是您的方法,因为这些表上的大型连接看起来不太健康。最好自己去弄清楚,我想…

因为您已经为
网络代码和
操作符指定了
FetchType.EAGER
,无论何时查询
,hibernate都会加载
网络代码和
操作符
。这就是
EAGER
抓取模式的全部思想

因此,您可以将查询简单更改为以下内容:

private static final String FIND
    = "SELECT DISTINCT domain"
    + " FROM Domain domain"
    + " WHERE domain.domainId = :domainId";
API详细信息


干杯

我的第一个观察结果是,如果您的映射表示必须急切地加载连接,则不需要编写包含连接的HQL查询

但是,如果不想使用联接,可以告诉Hibernate使用抓取策略作为子选择

Hibernate根据指定的映射生成用于在启动期间加载对象的SQL查询,并将其缓存。然而,在您的例子中,您与self和任意深度有一对多嵌套关系,因此hibernate似乎不可能事先做出决定 要正确获取的sql语句。因此,它需要根据运行时查询的父域的深度发送多个联接查询

在我看来,您似乎认为HQL和在您的示例中生成的SQL/('s)可以具有一对一的对应关系,而这是不正确的 对。使用HQL可以查询对象,orm根据 映射,或者您也可以在运行时指定它们(例如,映射中的惰性关联可以被查询api覆盖,但反之亦然)。 您可以告诉orm要加载什么(我的标记是急切的还是懒惰的)以及如何急切地加载(使用join/sub-select)

更新

当我对您的域模型运行以下查询时

SELECT DISTINCT domain FROM Domain domain LEFT OUTER JOIN FETCH domain.operators LEFT OUTER JOIN FETCH domain.networkCodes WHERE domain.domainId = :domainId";
我可以看到networkCode和operator集合都是实例PersistentSet(这是Hibernate包装器),并且都已将属性初始化为true。此外,在底层会话上下文中,我可以看到列出了域和运算符的域。 那么,是什么让你认为他们不急于加载

这就是我的域名的样子

@Entity
@Table
public class Domain {
    @Id
    @GenericGenerator(name = "generator", strategy = "increment")
    @GeneratedValue(generator = "generator")
    @Column(name = "domain_id")
    private Long domainId;

    @Column(nullable = false, unique = true)   
    private String name;

    @Column(nullable = false)    
    @Enumerated(EnumType.STRING)
    private DomainType type;

    @OneToMany(mappedBy = "domain",cascade = {
            CascadeType.PERSIST,
            CascadeType.MERGE
    }, fetch = FetchType.EAGER)   
    private Set<NetworkCode> networkCodes = new HashSet<NetworkCode>();

    @ManyToMany(mappedBy="parent",fetch = FetchType.EAGER, cascade=CascadeType.ALL)
    private Set<Domain> operators = new HashSet<Domain>();
    // more

    @ManyToOne  
    private Domain parent;

    public String getName() {
        return name;
    }


    public void setName(String name) {
        this.name = name;
    }


public DomainType getType() {
        return type;
    }

    public void setType(DomainType type) {
        this.type = type;
    }


    public Set<Domain> getOperators() {
        return operators;
    }


    public Long getDomainId() {
        return domainId;
    }


    public void setDomainId(Long domainId) {
        this.domainId = domainId;
    }


    public void setOperators(Set<Domain> operators) {
        this.operators = operators;
    }

    public void addDomain(Domain domain){
        getOperators().add(domain);
        domain.setParent(this);
    }


    public Domain getParent() {
        return parent;
    }


    public void setParent(Domain parent) {
        this.parent = parent;
    }

    public void addNetworkCode(NetworkCode netWorkCode){
        getNetworkCodes().add(netWorkCode);
        netWorkCode.setDomain(this);
    }
@实体
@桌子
公共类域{
@身份证
@GenericGenerator(name=“generator”,strategy=“increment”)
@GeneratedValue(generator=“generator”)
@列(name=“domain\u id”)
私有长域ID;
@列(nullable=false,unique=true)
私有字符串名称;
@列(nullable=false)
@枚举(EnumType.STRING)
私有域类型;
@OneToMany(mappedBy=“域”,级联={
cascade type.PERSIST,
级联类型合并
},fetch=FetchType.EAGER)
私有集networkCodes=新HashSet();
@ManyToMany(mappedBy=“parent”,fetch=FetchType.EAGER,cascade=CascadeType.ALL)
私有集运算符=新HashSet();
//更多
@许多酮
私有域父节点;
公共字符串getName(){
返回名称;
}
公共void集合名(字符串名){
this.name=名称;
}
公共域类型getType(){
返回类型;
}
公共void集合类型(域类型){
this.type=type;
}
公共集getOperators(){
返回操作员;
}
公共长getDomainId(){
返回域ID;
}
公共无效setDomainId(长域ID){
this.domainId=domainId;
}
公共无效集合运算符(集合运算符){
这个。操作员=操作员;
}
public void addDomain(域){
getOperators().add(域);
domain.setParent(此);
}
公共域getParent(){
返回父母;
}
public void setParent(域父级){
this.parent=parent;
}
public void addNetworkCode(NetworkCode NetworkCode){
getNetworkCodes().add(网络代码);
netWorkCode.setDomain(此);
}

Hibernate关系使用不同的获取策略

Hibernate提供了4种检索数据的策略:

选择

@OneToMany(mappedBy="tableName", cascade=CascadeType.ALL)
@Column(name="id") 
@Fetch(FetchMode.SELECT)
 @OneToMany(mappedBy="tableName", cascade=CascadeType.ALL)
 @Column(name="id")
 @Fetch(FetchMode.SUBSELECT)
在这个方法中,有多个SQL被激发。第一个SQL被激发 用于检索父表中的所有记录。其余记录为 为检索每个父记录的记录而激发。这基本上是 N+1问题。第一个查询从数据库中检索N条记录,在 这种情况下,N个父记录。对于每个父记录,将检索一个新的查询 因此,对于N个父对象,
@OneToMany(mappedBy="tableName", cascade=CascadeType.ALL)
@Column(name="id")
@Fetch(FetchMode.JOIN) 
 @OneToMany(mappedBy="tableName", cascade=CascadeType.ALL)
 @Column(name="id")
 @Fetch(FetchMode.SUBSELECT)
@OneToMany(mappedBy="tableName", cascade=CascadeType.ALL)
@Column(name="id")
@@BatchSize(size=2)