首页 > 解决方案 > Hibernate:如何编写包括多级的联接查询?

问题描述

我正在尝试编写一个类似于 MySQL Join 的 HQL 查询。以下是我的实体。正如你在下面看到的,我没有在我的 Pojos 中使用注释。相反,我使用 XML 来进行映射。

库存

public class Stock implements java.io.Serializable {

    private Integer idstock;
    @JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
    private Product product;
    private int quantity;
    private Date dateCreated;
    private Date lastUpdated;

    public Stock() {
    }

    public Stock(Product product, int quantity) {
        this.product = product;
        this.quantity = quantity;
    }

    public Stock(Product product, int quantity, Date dateCreated, Date lastUpdated) {
        this.product = product;
        this.quantity = quantity;
        this.dateCreated = dateCreated;
        this.lastUpdated = lastUpdated;
    }

    public Integer getIdstock() {
        return this.idstock;
    }

    public void setIdstock(Integer idstock) {
        this.idstock = idstock;
    }

    public Product getProduct() {
        return this.product;
    }

    public void setProduct(Product product) {
        this.product = product;
    }

    public int getQuantity() {
        return this.quantity;
    }

    public void setQuantity(int quantity) {
        this.quantity = quantity;
    }

    public Date getDateCreated() {
        return this.dateCreated;
    }

    public void setDateCreated(Date dateCreated) {
        this.dateCreated = dateCreated;
    }

    public Date getLastUpdated() {
        return this.lastUpdated;
    }

    public void setLastUpdated(Date lastUpdated) {
        this.lastUpdated = lastUpdated;
    }

}

产品

public class Product implements java.io.Serializable {

    private Integer idproduct;
    @JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
    private SparePart sparePart;
    @JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
    private VehicleModel vehicleModel;
    private double unitPrice;
    private String qrcode;
    private boolean enable;
    private Integer minimumStockLevel;
    private Integer stockReorderLevel;

    public Product() {
    }

    public Product(SparePart sparePart, VehicleModel vehicleModel, double unitPrice, String qrcode, boolean enable) {
        this.sparePart = sparePart;
        this.vehicleModel = vehicleModel;
        this.unitPrice = unitPrice;
        this.qrcode = qrcode;
        this.enable = enable;
    }

    public Product(SparePart sparePart, VehicleModel vehicleModel, double unitPrice, String qrcode, boolean enable, Integer minimumStockLevel, Integer stockReorderLevel) {
        this.sparePart = sparePart;
        this.vehicleModel = vehicleModel;
        this.unitPrice = unitPrice;
        this.qrcode = qrcode;
        this.enable = enable;
        this.minimumStockLevel = minimumStockLevel;
        this.stockReorderLevel = stockReorderLevel;
    }

    public Integer getIdproduct() {
        return this.idproduct;
    }

    public void setIdproduct(Integer idproduct) {
        this.idproduct = idproduct;
    }

    public SparePart getSparePart() {
        return this.sparePart;
    }

    public void setSparePart(SparePart sparePart) {
        this.sparePart = sparePart;
    }

    public VehicleModel getVehicleModel() {
        return this.vehicleModel;
    }

    public void setVehicleModel(VehicleModel vehicleModel) {
        this.vehicleModel = vehicleModel;
    }

    public double getUnitPrice() {
        return this.unitPrice;
    }

    public void setUnitPrice(double unitPrice) {
        this.unitPrice = unitPrice;
    }

    public String getQrcode() {
        return this.qrcode;
    }

    public void setQrcode(String qrcode) {
        this.qrcode = qrcode;
    }

    public boolean getEnable() {
        return this.enable;
    }

    public void setEnable(boolean enable) {
        this.enable = enable;
    }

    public Integer getMinimumStockLevel() {
        return this.minimumStockLevel;
    }

    public void setMinimumStockLevel(Integer minimumStockLevel) {
        this.minimumStockLevel = minimumStockLevel;
    }

    public Integer getStockReorderLevel() {
        return this.stockReorderLevel;
    }

    public void setStockReorderLevel(Integer stockReorderLevel) {
        this.stockReorderLevel = stockReorderLevel;
    }
}

车辆型号

public class VehicleModel implements java.io.Serializable {

    private Integer idvehicleModel;
    private String modelName;
    private String code;
    private boolean enable;

    public VehicleModel() {
    }

    public VehicleModel(String modelName, boolean enable) {
        this.modelName = modelName;
        this.enable = enable;
    }

    public Integer getIdvehicleModel() {
        return this.idvehicleModel;
    }

    public void setIdvehicleModel(Integer idvehicleModel) {
        this.idvehicleModel = idvehicleModel;
    }

    public String getModelName() {
        return this.modelName;
    }

    public void setModelName(String modelName) {
        this.modelName = modelName;
    }

    public boolean getEnable() {
        return this.enable;
    }

    public void setEnable(boolean enable) {
        this.enable = enable;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

}

零部件

public class SparePart implements java.io.Serializable {

    private Integer idsparePart;
    private String sparePartName;
    private String code;
    private boolean enable;

    public SparePart() {
    }

    public SparePart(String sparePartName, boolean enable) {
        this.sparePartName = sparePartName;
        this.enable = enable;
    }

    public Integer getIdsparePart() {
        return this.idsparePart;
    }

    public void setIdsparePart(Integer idsparePart) {
        this.idsparePart = idsparePart;
    }

    public String getSparePartName() {
        return this.sparePartName;
    }

    public void setSparePartName(String sparePartName) {
        this.sparePartName = sparePartName;
    }

    public boolean getEnable() {
        return this.enable;
    }

    public void setEnable(boolean enable) {
        this.enable = enable;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

}

这是我的 XML 映射

产品.hbm.xml

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<!-- Generated Sep 4, 2020 1:35:36 PM by Hibernate Tools 4.3.1 -->
<hibernate-mapping>
    <class name="beans.Product" table="product" catalog="aaa" optimistic-lock="version">
        <id name="idproduct" type="java.lang.Integer">
            <column name="idproduct" />
            <generator class="identity" />
        </id>
        <many-to-one name="sparePart" class="beans.SparePart" fetch="select">
            <column name="idspare_part" not-null="true" />
        </many-to-one>
        <many-to-one name="vehicleModel" class="beans.VehicleModel" fetch="select">
            <column name="idvehicle_model" not-null="true" />
        </many-to-one>
        <property name="unitPrice" type="double">
            <column name="unit_price" precision="22" scale="0" not-null="true">
                <comment>This is the central price for a product. This can change according to the market values.</comment>
            </column>
        </property>
        <property name="qrcode" type="string">
            <column name="qrcode" length="45" not-null="true" />
        </property>
        <property name="enable" type="boolean">
            <column name="enable" not-null="true" />
        </property>
        <property name="minimumStockLevel" type="java.lang.Integer">
            <column name="minimum_stock_level" />
        </property>
        <property name="stockReorderLevel" type="java.lang.Integer">
            <column name="stock_reorder_level" />
        </property>
    </class>
</hibernate-mapping>

Stock.hbm.xml

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<!-- Generated Sep 4, 2020 1:35:36 PM by Hibernate Tools 4.3.1 -->
<hibernate-mapping>
    <class name="beans.Stock" table="stock" catalog="aaa" optimistic-lock="version">
        <id name="idstock" type="java.lang.Integer">
            <column name="idstock" />
            <generator class="identity" />
        </id>
        <many-to-one name="product" class="beans.Product" fetch="select">
            <column name="idproduct" not-null="true" />
        </many-to-one>
        <property name="quantity" type="int">
            <column name="quantity" not-null="true" />
        </property>
        <property name="dateCreated" type="timestamp">
            <column name="date_created" length="0" />
        </property>
        <property name="lastUpdated" type="timestamp">
            <column name="last_updated" length="0" />
        </property>
    </class>
</hibernate-mapping>

SparePart.hbm.xml

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<!-- Generated Sep 4, 2020 1:35:36 PM by Hibernate Tools 4.3.1 -->
<hibernate-mapping>
    <class name="beans.SparePart" table="spare_part" catalog="aaa" optimistic-lock="version">
        <id name="idsparePart" type="java.lang.Integer">
            <column name="idspare_part" />
            <generator class="identity" />
        </id>
        <property name="sparePartName" type="string">
            <column name="spare_part_name" length="100" not-null="true" />
        </property>
        <property name="code" type="string">
            <column name="code" length="100"/>
        </property>
        <property name="enable" type="boolean">
            <column name="enable" not-null="true" />
        </property>
    </class>
</hibernate-mapping>

车辆模型.hbm.xml

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<!-- Generated Sep 4, 2020 1:35:36 PM by Hibernate Tools 4.3.1 -->
<hibernate-mapping>
    <class name="beans.VehicleModel" table="vehicle_model" catalog="aaa" optimistic-lock="version">
        <id name="idvehicleModel" type="java.lang.Integer">
            <column name="idvehicle_model" />
            <generator class="identity" />
        </id>
        <property name="modelName" type="string">
            <column name="model_name" length="100" not-null="true" />
        </property>
        <property name="code" type="string">
            <column name="code" length="100"/>
        </property>
        <property name="enable" type="boolean">
            <column name="enable" not-null="true" />
        </property>
    </class>
</hibernate-mapping>

现在我有以下查询。

public List<Stock> getAllStock(Session session) {
        Query query = session.createQuery("FROM Stock s");
        List<Stock> list = (List<Stock>) query.list();
        return list;
    }

这给了我,

  1. Stock
  2. Product每个Stock
  3. SparePart每个Product
  4. VehicleModel每个Product

然而,由于著名的 n+1 问题,这非常慢。为了从每个表中获取数据,这段代码生成了一个 SQL 查询,产生了大量的 sql 查询。您拥有的数据越多,生成的查询就越多。因此,这是一个超级缓慢的过程。目前需要 40 秒。

相反,我需要编写 HQL 连接并使用单个 SQL 查询获取数据。我怎样才能做到这一点?

标签: javahibernatehql

解决方案


您应该使用 JOIN FETCH 告诉 JPA/Hibernate 它应该加载它。

从休眠文档:

如果您忘记 JOIN FETCH 所有 EAGER 关联,Hibernate 将为每个关联发出辅助选择,这反过来又会导致 N+1 查询问题。

因此,您应该更喜欢 LAZY 关联。

select s from Stock s join fetch s.product p 
                      join fetch p.sparePart sp
                      join fetch p.vehicleModel v

另请阅读文档:https ://docs.jboss.org/hibernate/orm/5.5/userguide/html_single/Hibernate_User_Guide.html#best-practices-fetching-associations


推荐阅读