Подтвердить что ты не робот

Org.hibernate.loader.MultipleBagFetchException: не может одновременно извлекать несколько пакетов

Ниже приведен мой код. Здесь я использую несколько списков для извлечения данных из базы данных. При извлечении данных из запроса hql он показывает исключение.

Класс Pojo

public class BillDetails implements java.io.Serializable {

private Long billNo;
// other fields
@LazyCollection(LazyCollectionOption.FALSE)
private List<BillPaidDetails> billPaidDetailses = new ArrayList<BillPaidDetails>();
private Set productReplacements = new HashSet(0);
@LazyCollection(LazyCollectionOption.FALSE)
private List<BillProduct> billProductList = new ArrayList<BillProduct>();
//getter and setter
}

hmb.xml файл

<class name="iland.hbm.BillDetails" table="bill_details" catalog="retail_shop">
        <id name="billNo" type="java.lang.Long">
            <column name="bill_no" />
            <generator class="identity" />
        </id>
 <bag name="billProductList" table="bill_product" inverse="true" lazy="false" fetch="join">
            <key>
                <column name="bill_no" not-null="true" />
            </key>
            <one-to-many class="iland.hbm.BillProduct" />
        </bag>
        <bag name="billPaidDetailses" table="bill_paid_details" inverse="true" lazy="false" fetch="select">
            <key>
                <column name="bill_no" not-null="true" />
            </key>
            <one-to-many class="iland.hbm.BillPaidDetails" />
        </bag>
        <set name="productReplacements" table="product_replacement" inverse="true" lazy="false" fetch="join">
            <key>
                <column name="bill_no" not-null="true" />
            </key>
            <one-to-many class="iland.hbm.ProductReplacement" />
        </set>
    </class>

Запрос Hql

String hql = "select distinct bd,sum(bpds.amount) from BillDetails as bd "
                    + "left join fetch bd.customerDetails as cd "
                    + "left join fetch bd.billProductList as bpd "
                    + "left join fetch bpd.product as pd "
                    +"left join fetch bd.billPaidDetailses as bpds "
                    + "where bd.billNo=:id "
                    + "and bd.client.id=:cid ";

Я пытаюсь выполнить запрос для извлечения данных из базы данных, но это показывает org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple bags Как разрешить этот

4b9b3361

Ответ 1

Как объясняется в этой статье, Hibernate не позволяет получать более одной сумки, потому что это может привести к декартовому продукту.

Вы можете изменить пакеты на наборы и добавить атрибут order-by="id" для имитации поведения упорядоченного списка:

private Set<BillPaidDetails> billPaidDetailses = new LinkedHashSet<>();
private Set<BillProduct> billProductList = new LinkedHashSet<>();

<set name="billProductList" table="bill_product" 
     inverse="true" lazy="false" fetch="join" order-by="id">
    <key>
        <column name="bill_no" not-null="true" />
    </key>
    <one-to-many class="iland.hbm.BillProduct" />
</set>

<set name="billPaidDetailses" table="bill_paid_details" 
     inverse="true" lazy="false" fetch="select" order-by="id">
    <key>
        <column name="bill_no" not-null="true" />
    </key>
    <one-to-many class="iland.hbm.BillPaidDetails" />
</set>

Но только потому, что вы можете, это не значит, что вы должны.

Что вы можете сделать, это выбрать не более одной коллекции в исходном запросе SQL, в то время как другие коллекции извлекаются впоследствии с использованием вторичных запросов. Таким образом, вы можете избежать декартово произведение.

Другой вариант - использовать многоуровневую выборку от дочерних до родительских объектов.

Ответ 2

Для меня у меня была такая же ошибка, и я решил, добавив аннотацию спящего режима @Fetch

@OneToMany(mappedBy="parent", fetch=FetchType.EAGER)
@Fetch(value = FetchMode.SUBSELECT)
private List<Child> childs;

Ответ 3

Вы можете только join-fetch следовать за одним отношением для сущности (либо billPaidDetailses, либо billProductList).

Рассмотрите возможность использования ленивых ассоциаций и загрузки коллекций, когда они нужны, ИЛИ используя ленивые ассоциации и загружая коллекции вручную с помощью Hibernate.initialize(..). По крайней мере, это был тот вывод, к которому я пришел, когда у меня была аналогичная проблема.

В любом случае это потребует нескольких запросов к базе данных.

Ответ 4

Лучшим решением является переход на Set. Однако, если вы не можете заменить List на Set (как в моем случае, было тяжелое использование тегов JSF, характерных для Lists), и если вы можете использовать аннотации Hibernate, вы можете указать @IndexColumn (name = "INDEX_COL"). Это решение работало лучше для меня, переход на Set потребовал бы тонны рефакторинга.

Итак, ваш код будет примерно таким:

@IndexColumn (name = "INDEX_COL")
private List<BillPaidDetails> billPaidDetailses = new ArrayList<BillPaidDetails>();

@IndexColumn (name = "INDEX_COL")
private List<BillProduct> billProductList = new ArrayList<BillProduct>();

Как предложил Игорь в комментариях, вы также можете создать прокси-методы для возврата списков. Я не пробовал это, но был бы хорошей альтернативой, если вы не можете использовать аннотации Hibernate.

Ответ 5

Я использовал новую аннотацию @OrderColumn вместо @IndexColumn (устарело, см.: https://docs.jboss.org/hibernate/orm/5.2/javadocs/org/hibernate/annotations/IndexColumn.html), и теперь она работает.

Аннотируйте одну из коллекций с помощью @OrderColumn, например

@ManyToMany(cascade = CascadeType.ALL)
@OrderColumn
private List<AddressEntity> addresses = Lists.newArrayList();

@Builder.Default
@ManyToMany(cascade = CascadeType.ALL)
private List<BankAccountEntity> bankAccounts = Lists.newArrayList();

Ответ 6

Я считаю, что использование аннотированного метода @PostLoad в сущности наиболее полезно, я бы сделал что-то вроде

@PostLoad
public void loadCollections(){
     int s1 = productReplacements.size();
     int s2 = billProductList.size();
}

таким образом я могу точно контролировать загрузку и инициализацию коллекций в той же транзакции, в которой была загружена сущность.

Ответ 7

В запросе вы получите слишком много данных, и HIbernate не может загрузить их все. Уменьшите свой запрос и/или настройте свои сущности для получения только необходимых данных.