Возможно ли, чтобы hibernate генерировал обновление из операторов значений для postgresql? - PullRequest
0 голосов
/ 18 июня 2020

Учитывая postgresql таблицу

               Table "public.test"  
  Column  |            Type             | Modifiers   
----------+-----------------------------+-----------  
 id       | integer                     | not null  
 info     | text                        |   

И следующие значения:

# select * from test;  
 id |     info     
----+--------------
  3 | value3        
  4 | value4
  5 | value5

Как вы, возможно, знаете, с postgresql вы можете использовать такие операторы для обновления кратных строк с разными значениями :

update test set info=tmp.info from (values (3,'newvalue3'),(4,'newvalue4'),(5,'newvalue5')) as tmp (id,info) where test.id=tmp.id; 

И это приводит к тому, что таблица обновляется в отдельных запросах до:

# select * from test;  
 id |     info     
----+--------------
  3 | newvalue3        
  4 | newvalue4
  5 | newvalue5

Я искал повсюду, как как заставить спящий режим генерировать такие операторы для запросов на обновление. Я знаю, как заставить его работать для запросов на вставку (с параметром reWriteBatchedInserts jdb c и параметрами конфигурации пакетной конфигурации гибернации).

Но возможно ли это для запросов на обновление или мне нужно самому писать собственный запрос? Независимо от того, что я делаю, спящий режим всегда отправляет отдельные запросы на обновление в базу данных (я ищу postgresql журналы операторов сервера для этого подтверждения).

2020-06-18 08:19:48.895 UTC [1642] LOG:  execute S_6: BEGIN
2020-06-18 08:19:48.895 UTC [1642] LOG:  execute S_8: update test set info = $1 where id = $2
2020-06-18 08:19:48.895 UTC [1642] DETAIL:  parameters: $1 = 'newvalue3', $2 = '3'
2020-06-18 08:19:48.896 UTC [1642] LOG:  execute S_8: update test set info = $1 where id = $2
2020-06-18 08:19:48.896 UTC [1642] DETAIL:  parameters: $1 = 'newvalue4', $2 = '4'
2020-06-18 08:19:48.896 UTC [1642] LOG:  execute S_8: update test set info = $1 where id = $2
2020-06-18 08:19:48.896 UTC [1642] DETAIL:  parameters: $1 = 'newvalue4', $2 = '5'
2020-06-18 08:19:48.896 UTC [1642] LOG:  execute S_1: COMMIT

Я всегда нахожу, что это во много раз быстрее выдает один массовый запрос на обновление, чем несколько отдельных обновлений, нацеленных на отдельные строки. Со многими отдельными запросами на обновление, даже если они отправляются в пакете драйвером jdb c, они все равно должны обрабатываться последовательно сервером, поэтому это не так эффективно, как один запрос на обновление, ориентированный на несколько строк. Так что, если у кого-то есть решение, которое не требует написания собственных запросов для моих сущностей, я был бы очень рад!


Обновить

Для дальнейшего уточнения моих вопрос хочу добавить уточнение. Я ищу решение, которое не отказывалось бы от функции грязной проверки Hibernate для обновлений сущностей. Я пытаюсь избежать написания запросов на пакетное обновление вручную в общем случае, когда необходимо обновить несколько базовых c полей с разными значениями в списке сущностей. В настоящее время я изучаю SPI гибернации, чтобы увидеть, выполнимо ли это. org.hibernate.engine.jdbc.batch.spi.Batch кажется подходящим местом, но я еще не совсем уверен, потому что я никогда ничего не делал с SPI спящего режима). Приветствуются любые идеи!


1 Ответ

1 голос
/ 19 июня 2020

Для этого можно использовать Blaze-Persistence , который представляет собой построитель запросов поверх JPA, который поддерживает многие расширенные функции СУБД поверх модели JPA.

Это не так. еще поддерживает предложение FROM в DML, но оно скоро появится в следующем выпуске: https://github.com/Blazebit/blaze-persistence/issues/693

Между тем вы можете использовать CTE для этого. Сначала вам нужно определить сущность CTE (концепция Blaze-Persistence):

@CTE
@Entity
public class InfoCte {
  @Id Integer id;
  String info;
}

Я предполагаю, что ваша модель сущности выглядит примерно так

@Entity
public class Test {
  @Id Integer id;
  String info;
}

Затем вы можете использовать Blaze-Persistence как это:

criteriaBuilderFactory.update(entityManager, Test.class, "test")
  .with(InfoCte.class, false)
    .fromValues(Test.class, "newInfos", newInfosCollection)
    .bind("id").select("newInfos.id")
    .bind("info").select("newInfos.info")
  .end()
  .set("info")
    .from(InfoCte.class, "cte")
    .select("cte.info")
    .where("cte.id").eqExpression("test.id")
  .end()
  .whereExists()
    .from(InfoCte.class, "cte")
    .where("cte.id").eqExpression("test.id")
  .end()
.executeUpdate();

Это создаст запрос SQL, подобный следующему

WITH InfoCte(id, info) AS(
  SELECT t.id, t.info
  FROM (VALUES(1, 'newValue', ...)) t(id, info)
)
UPDATE test
SET info = (SELECT cte.info FROM InfoCte cte WHERE cte.id = test.id)
WHERE EXISTS (SELECT 1 FROM InfoCte cte WHERE cte.id = test.id)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...