Автор оригинала: Vlad Mihalcea.
Вступление
Один из моих подписчиков в Твиттере попросил меня ответить на следующий вопрос о StackOverflow .
Цель этой статьи-объяснить различные методы управления инструкцией UPDATE
SQL, которая выполняется всякий раз, когда объект изменяется уровнем доступа к данным.
Модель предметной области
Давайте предположим, что у нас есть следующая Запись
сущность:
@Entity(name = "Post") @Table(name = "post") public class Post { @Id private Long id; private String title; private long score; @Column( name = "created_on", nullable = false, updatable = false ) private Timestamp createdOn; @Transient private String creationTimestamp; public Post() { this.createdOn = new Timestamp( System.currentTimeMillis() ); } public String getCreationTimestamp() { if(creationTimestamp == null) { creationTimestamp = DateTimeFormatter .ISO_DATE_TIME.format( createdOn.toLocalDateTime() ); } return creationTimestamp; } @Override public String toString() { return String.format( "Post{\n" + " id=%d\n" + " title='%s'\n" + " score=%d\n" + " creationTimestamp='%s'\n" + '}', id, title, score, getCreationTimestamp() ); } //Getters and setters omitted for brevity }
Столбцы только для вставки
Сущность Post
имеет атрибут CreatedOn
, который следует устанавливать только при первом создании сущности. Любое последующее ОБНОВЛЕНИЕ
не позволяет изменять этот столбец базы данных, поэтому обновляемый
атрибут связанного @Столбца
аннотации имеет значение false
.
@Column( name = "created_on", nullable = false, updatable = false ) private Timestamp createdOn;
Для атрибута сущности, который никогда не должен изменяться после сохранения сущности (столбцы только для вставки), следует использовать @Столбец(обновляемый)
.
Вычисляемые атрибуты сущности
Атрибут Post
сущность CreatedOn
является Временная метка
, которую мы, возможно, захотим распечатать в журналах приложений, используя ISO_DATE_TIME
DateTimeFormatter
. Чтобы избежать вычисления представления объекта String
каждый раз, когда нам нужно регистрировать эту сущность, атрибут отметка времени создания
будет хранить это значение.
@Transient private String creationTimestamp;
Однако мы не хотим, чтобы этот атрибут сохранялся. Следовательно, нам нужно аннотировать его с помощью @Transient
. Таким образом, Hibernate будет игнорировать этот атрибут при переводе изменений состояния сущности в инструкцию SQL .
Предполагая, что мы сохранили следующую сущность:
doInJPA(entityManager -> { Post post = new Post(); post.setId(1L); post.setTitle("High-Performance Java Persistence"); entityManager.persist(post); });
При извлечении, регистрации и изменении этой сущности:
doInJPA(entityManager -> { Post post = entityManager.find(Post.class, 1L); LOGGER.info("Fetched post: {}", post); post.setScore(12); });
Получается следующий результат:
SELECT p.id AS id1_0_0_, p.created_on AS created_2_0_0_, p.score AS score3_0_0_, p.title AS title4_0_0_ FROM post p WHERE p.id = 1 -- Fetched post: Post{ id=1 title='High-Performance Java Persistence' score=0 creationTimestamp='2016-10-10T16:48:25.566' } UPDATE post SET score = 12, title = 'High-Performance Java Persistence' WHERE id = 1
Как вы можете видеть, ни создатель
, ни Метка времени создания
не включены в инструкцию UPDATE
SQL.
Для вычисляемых атрибутов, которые никогда не должны сохраняться в связанной таблице базы данных, следует использовать @Переходный период
.
Динамические обновления
Предыдущая инструкция UPDATE
включает все столбцы таблицы, даже если фактически изменяется только подмножество. Использование одного и того же оператора SQL полезно при использовании кэширования операторов JDBC . Однако, если таблица базы данных сильно проиндексирована, мы не хотим обновлять некоторые записи индекса, которые не были изменены, как объяснил Маркус Винанд .
По этой причине Hibernate предлагает аннотацию @DynamicUpdate
. Все, что нам нужно сделать, это добавить эту аннотацию на уровне сущности:
@Entity(name = "Post") @Table(name = "post") @DynamicUpdate public class Post { //Code omitted for brevity }
Теперь при выполнении предыдущего тестового случая, в котором был изменен атрибут оценка
, выполняется следующая инструкция UPDATE
:
UPDATE post SET score = 12, WHERE id = 1
Блестяще!
Чтобы обновить только измененные столбцы, сущность должна использовать аннотацию @DynamicUpdate
, которая также обязательна при использовании оптимистической блокировки без версий .
Гибкость очень важна при работе с платформой доступа к данным, и по этой причине Hibernate предлагает несколько вариантов, позволяющих избежать обновления определенных столбцов или обновлять только те столбцы, которые были изменены.