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

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

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

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

Возможно ли это?

Ответ 1

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

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

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

Просто дайте пример в псевдокоде:

В 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 в Hibernate может дать вам что-то вроде этого:

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

Я подозреваю, что вам кажется, что вам нужна функция пакетной обработки, потому что вы делаете что-то подобное (на основе вашего примера, в котором вы используете массовое обновление для отдельной записи). Однако НЕ, как это должно быть сделано в Hibernate/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 Bulk Update предназначено для другого использования, которое является чем-то вроде массового обновления для БД, чтобы повлиять на множество записей, например:

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 в таком прецеденте, чтобы вы могли управлять выпуском запросов.

Ответ 2

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

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

Ответ 3

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

Возможно ли это?

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

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

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

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

hibernate.jdbc.batch_size 20 Hibernate отключает вставку в JDBC прозрачно, если вы используете генератор идентификационных идентификаторов.

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

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

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

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();

Ответ 4

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

  • executeUpdate для массового удаления всех клиентов с идентификатором > 1000.
  • читать объект клиента с идентификатором = 1001.

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

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

Ответ 5

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

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

 @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();

        ...

 }