Спящий режим: Как писать запросы на объединение, включая многоуровневые?

#java #hibernate #hql

Вопрос:

Я пытаюсь написать запрос HQL, который похож на соединение MySQL. Ниже приведены мои сущности. Как вы можете видеть ниже, я не использую аннотации в своих 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;
    }
}
 

VehicleModel

 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;
    }

}
 

SparePart

 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;
    }

}
 

Here are my XML mappings

Product.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>
 

VehicleModel.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-запроса. Как я могу это сделать?

Комментарии:

1. Я не вижу никаких сопоставлений JPA/Hibernate в классах моделей.

2. Это потому, что он использует XML для сопоставления. Тогда нет необходимости в аннотациях.

3. Извините. Пожалуйста, покажите XML, который нам нужен, чтобы узнать, как вы сопоставили отношения

4. @SimonMartinelli: Извините, я обновил свой вопрос с помощью XML.

5. @ewramner: Я обновил свой вопрос с помощью XML

Ответ №1:

Вы должны использовать JOIN FETCH, чтобы сообщить JPA/Hibernate, что он должен его загрузить.

Из документов Hibernate:

Если вы забудете ПРИСОЕДИНИТЬСЯ ко всем активным ассоциациям, Hibernate выдаст дополнительный выбор для каждого из них, что, в свою очередь, может привести к проблемам с N 1 запросом.

По этой причине вам следует предпочесть ЛЕНИВЫЕ ассоциации.

 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

Комментарии:

1. Удивительные. Спасибо тебе, Саймон.

Ответ №2:

Вы можете использовать реализацию критериев hibernate, и с помощью псевдонима, к которому вы можете присоединиться, ниже приведен некоторый справочный код, который может помочь

 Criteria c = session.createCriteria(Stock.class, "stock");
c.createAlias("stock.product", "product");//it is like inner join
c.createAlias("product.spare_part","spare_part");
c.createAlias("product.vehicle_model","vehicle_model");
return c.list();