it-swarm.com.ru

Как использовать hibernate @DynamicUpdate с данными весны jpa?

Я использую Spring Data-JPA. Я хочу обновить только один столбец.

Мой репозиторий есть;

public interface UserRepository extends JpaRepository<User,Long> {
}

мой сервис есть;

public User save(User user) {
    return userRepository.save(user);
}

моя сущность;

@Entity
@DynamicUpdate(true)
public class User implements Serializable {
    // column definitions, etc.
}

Как я могу обновить только один столбец в User?

4
Ravi Developer

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

Поэтому попробуйте сделать следующее, чтобы подтвердить правильное поведение @DynamicUpdate ;

В сервисе;

@Transactional
public User save(User newUser) {
    User currentUser = userRepository.get(newUser.getId());
    // handle merging of updated columns onto currentUser manually or via some mapping tool
    currentUser.setName(newUser.getName());
    return userRepository.save(currentUser);
}

Используя вышеуказанную логику вместе с аннотацией динамического обновления, вы сможете увидеть обновление столбца только с именем, если у вас не включен какой-либо аудит или используется столбец @Version 'ed. 

Если обновленные столбцы всегда одинаковы, то лучше использовать updatable = false для столбцов, которые не являются целью обновления в их определениях @Column, поскольку использование @DynamicUpdate очень неэффективно, поскольку он генерирует каждый sql заново, никогда не используя кэшированные sqls. Но будьте осторожны, используя эту функцию, так как вы вообще не сможете обновить эти столбцы. 

Я не рекомендую использовать @Query, если у вас нет случая, когда собственный JPA/Hibernate недостаточен, но если у вас есть сценарий использования для обновления только целевого набора столбцов, это лучший выбор и самый эффективный.

Если обновленных столбцов много и они могут сильно различаться, либо определите логику ручного отображения между newUser и currentUser, либо используйте некоторые инструменты отображения, такие как Orika , у меня есть похожая автоматизированная структура, в которой сотни объектов пропатчены через эти картографы, позволяющие чрезвычайно универсальный способ обработки многих операций CRUD.

3
buræquete

Прежде всего я хочу объяснить, почему @DynamicUpdate не работает для вас. Поэтому я должен заметить, как работает @DynamicUpdate:

Аннотация @DynamicUpdate используется для указания что оператор SQL UPDATE должен генерироваться всякий раз, когда сущность модифицируется. По умолчанию Hibernate использует кэшированный оператор UPDATE, который устанавливает все столбцы таблицы. Когда сущность аннотируется с помощью @DynamicUpdate аннотация, PreparedStatement будет включать в себя только столбцы, значения которых были изменены. ( подробнее )

Например, предположим, что User имеет свойство с именем name.

Поэтому, если вы впервые сохранили пользователя с определенным именем, теперь вы передаете null в качестве значения @DynamicUpdate, примет его как изменение и попытается обновить имя до null.

Первое решение:

В качестве первого решения, если вы хотите, чтобы @DynamicUpdate работал, сначала вы должны заполнить все остальные свойства User старыми значениями, а затем сохранить его.

Pros: Сгенерированный запрос SQL просто обновляет желаемое свойство.

Cons: Hibernate должен генерировать соответствующую строку SQL каждый раз, и, таким образом, на стороне Hibernate снижается производительность.

Второе решение:

Вы можете обновить User с помощью пользовательского запроса:

@Modifying
@Query("UPDATE User SET name=(:name) WHERE id=(:id)")
public void updateName(@Param("name")String name, @Param("id")Long id);

Третье решение:

В качестве последнего решения я могу предложить вам использовать updatable = false. Это заполнит свойство в самый первый момент, когда объект будет вставлен.

  @Column(name = "create_date", nullable = false, updatable = false)
    private Instant createDate;
1
Spara