Hibernate hql, выполняет несколько операторов обновления в одном запросе

Я хочу выполнить несколько операторов обновления в одном запросе в hibernate Hql. как ниже:

hql = " update Table1 set prob1=null where id=:id1; "
                + " delete from Table2 where id =:id2 ";
...
query.executeUpdate();

в том же executeUpdate вызов я хочу обновить записи в Table1 и удалить записи из Table2.

это возможно?

5 ответов


короче говоря, то, что вы ищете, похоже на пакетирование в JDBC. Thich не предоставляется Hibernate для запроса массового обновления, и я сомневаюсь, что он когда-либо будет рассмотрен для Hibernate.

из моего прошлого опыта, функция дозирования для HQL редко бывает полезна в реальной жизни. Может показаться странным, что что-то полезно в SQL+JDBC, но не в HQL. Я попытаюсь объяснить.

обычно, когда мы работаем с Hibernate (или другим подобным ORM), мы работаем против сущностей. Hibernate будет отвечать за синхронизацию состояния наших объектов с БД,что в большинстве случаев может помочь в повышении производительности. Однако в Hibernate мы не изменяем состояние отдельного объекта с помощью запроса массового обновления.

просто приведите пример, в псевдо-коде:

в JDBC вы можете сделать что-то вроде (Я пытаюсь имитировать то, что вы показываете в своем примере):

List<Order> orders = findOrderByUserId(userName);
for (Order order: orders) {
    if (order outstanding quantity is 0) {
        dbConn.addBatch("update ORDER set STATE='C' where ID=:id", order.id);
    } else if (order is after expriation time) {
        dbConn.addBatch("delete ORDER where ID=:id", order.id);
    }
}
dbConn.executeBatch();

наивный перевод из логики JDBC в спящий режим может дать вам что-то вроде этого:--6-->

List<Order> orders = findOrderByUserId(userName);
for (Order order: orders) {
    if (order outstanding quantity is 0) {
        q = session.createQuery("update Order set state='C' where id=:id");
        q.setParameter("id", order.id);
        q.executeUpdate();
    } else if (order is after expriation time) {
        q = session.createQuery("delete Order where id=:id");
        q.setParameter("id", order.id);
        q.executeUpdate();
    }
}

Я подозреваю, что вы думаете, что вам нужна функция дозирования, потому что вы делаете что-то подобное (на основе вашего примера, который вы используете массовое обновление для отдельной записи). Однако это не как это должно быть сделано в спящем режиме / JPA

(на самом деле лучше обернуть доступ к слою персистентности через репозиторий, здесь я просто упрощаю изображение)

List<Order> orders = findOrderByUserId(userName);
for (Order order: orders) {
    if (order.anyOutstanding()) {
        order.complete();    // which internally update the state
    } else if (order.expired) {
        session.delete(order);
    }
}

session.flush();   // or you may simply leave it to flush automatically before txn commit

таким образом, Hibernate является интеллектуальным достаточно обнаружить измененные/удаленные / вставленные сущности и использовать пакет JDBC для выполнения операций DB CUD в flush(). Что еще более важно, это вся цель ORM: мы хотим предоставить поведенчески богатые сущности для работы, для которых внутреннее изменение состояния сущностей может быть "прозрачно" отражено в постоянном хранилище.

массовое обновление HQL предназначено для другого использования, что похоже на одно массовое обновление для БД, чтобы повлиять на множество записей, например:

q = session.createQuery("update Order set state='C' " 
                        + " where user.id=:user_id "
                        + " and outstandingQty = 0 and state != 'C' ");
q.setParameter("user_id", userId);
q.executeUpdate();

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

Я не могу пропустить, что есть случаи, когда вам действительно нужно выпустить много запросов на обновление, которые не подходят для осмысленного поведения сущности. В таком случае вы можете пересмотреть, является ли Hibernate правильным инструментом для использования. Вы можете рассмотрите возможность использования чистого JDBC в таком случае, чтобы иметь контроль над тем, как выдаются запросы.


нет, это невозможно, потому что Hibernate используетPreparedStatements для этого (что хорошо из-за переменных привязки) и PreparedStatements не поддерживают пакеты, состоящие из нескольких различных операторов.

PreparedStatement может только пакетные различные комбинации переменных привязки для одного оператора, который Hibernate использует для пакетной вставки/обновления при сбросе изменяется контекст сохранения (сеанс).


в том же вызове executeUpdate я хочу обновить записи в Table1 и удалить записи из таблицы 2.

это возможно?

executeUpdate() выполняет один запрос на обновление. Поэтому нет, вы не можете этого сделать. Вы должны выполнить столько запросов на обновление таблиц, чтобы обновить/удалить. Кроме того, это делает код чище, если вы разделяете запросы:

  • запросы легче читать, а настройка параметров читается и не подверженный ошибкам.
  • для отладки выполнения запроса было бы проще понять, обрабатываются ли запросы один за другим в своем собственном executeUpdate()

это не означает, что запросы должны обязательно передаваться один за другим.
Пакетная обработка-это функция, предоставляемая Hibernate для повышения производительности при выполнении многократных запросов. Вы должны включить функцию, чтобы использовать его. hibernate.jdbc.batch_size свойство должно быть задано соответствующее значение.

если вы занимаетесь пакетной обработкой, вам нужно будет включить использование дозирования JDBC. Это абсолютно необходимо, если вы хотите достигните оптимального представления. Установите размер пакета JDBC в разумный количество (10-50, например):

спящий режим.интерфейс jdbc.batch_size 20 Hibernate отключает пакетирование вставки на Уровень JDBC прозрачно, если вы используете генератор идентификаторов идентификаторов.

помимо официальная документация :

Hibernate отключает пакетирование вставки на уровне JDBC прозрачно, если используется генератор идентификаторов.

тем не менее, в вашем случае это будет бесполезно, потому что, как объяснил Драган Божанович, вы обновляете/удаляете разные таблицы в своих запросах. Таким образом, он создаст столько пакетных исполнений, сколько запрашиваемых таблиц.
Таким образом, вы должны выполнить каждый запрос индивидуально. Просто commit () транзакция, когда вы считаете, что она должна быть :

hql = "update Table1 set prob1=null where id=:id1;"
...
query.setParameter("id1",...);
query.executeUpdate();
hql = "delete from Table2 where id =:id2";
...
query.executeUpdate();
query.setParameter("id2",...);
..
tx.commit();

SQL, сгенерированный массовыми обновлениями/удалениями JPA, т. е. вызовами javax.стойкость.Запрос.executeUpdate () не может быть упакован Hibernate при передаче в JDBC. @DraganBozanovic и @AdrianShum уже объяснили это, но чтобы добавить к своим комментариям: executeUpdate () возвращает int (количество объектов, обновленных или удаленных) - независимо от промывки сеанса гибернации, как int может быть возвращен без вызова базы данных немедленно и синхронно? На языке JPQL/бумага HQL/SQL с необходимо будет оценить клиентскую сторону, что невозможно, потому что объекты, подлежащие массовому обновлению/удалению, возможно, даже не были прочитаны в сеансе гибернации. Кроме того, если обновление/удаление не были выполнены на базе сразу же последующие запросы для чтения в сущностях JPA могут получить устаревшие данные. Пример:

  1. executeUpdate для массового удаления всех клиентов с ID > 1000.
  2. чтение сущности клиента с ID = 1001.

Если executeUpdate в 1 было разрешено отложить до тех пор, пока чтение в 2, то вы получите неправильный ответ (клиент все еще существует).

вам либо нужно прочитать сущности в использовании JPA, обновить их и позволить Hibernate генерировать обновление SQL (которое он может пакетировать), либо вызвать JDBC напрямую для выполнения пакетных обновлений.


почему бы не выполнить два запроса отдельно в транзакционный метод

по аннотировать метод с @транзакций, если запрос выполнить не удается, другой не выполнит.

 @Transactional(propagation = Propagation.REQUIRED, readOnly = false)

 public void executeQuery(Obj1 obj1) {

 String query="update table1 set actualRepaymentAmount=expectedRepaymentAmount,active='Y'  where loanCaseId = '"+caseId+"'";
        sessionFactory.getCurrentSession().createQuery(query).executeUpdate();

 query="update table2 set loanStatus='C' where loanCaseId = '"+caseId+"'";  
    sessionFactory.getCurrentSession().createQuery(query).executeUpdate();

        ...

 }