Глава 12. Сущности «только для чтения»
Оглавление- 12.1. Создание постоянных сущностей «только для чтения»
-
12.2. Влияние «только для чтения» на тип свойства
- 12.2.1. Простые свойства
- 12.2.2. Однонаправленные ассоциации
- 12.2.3. Двунаправленные ассоциации
Важно
Использование Hibernate сущностей «только для чтения» может отличаться от того, что вы могли встретить в другом месте. Неправильное использование может привести к неожиданным результатам.
Когда сущность является «только для чтения»:
- Hibernate не делает «грязную» проверку простых свойств сущности или односторонних ассоциаций.
- Hibernate не будет обновлять простые свойства или обновляемые односторонние ассоциации.
- Hibernate не будет обновлять версию сущности, доступной только для чтения, если изменены только простые свойства или однонаправленные обновляемые ассоциации.
В некотором смысле, Hibernate рассматривает сущности, доступные только для чтения, как сущности, которые доступны не «только для чтения».
- Hibernate каскадирует операции с ассоциациями, как определено в отображении сущностей.
- Hibernate обновляет версию, если у сущности есть коллекция с изменениями, которые «загрязняют» сущность;
- Сущность только для чтения может быть удалена.
Даже если сущность не доступена только для чтения, её ассоциация коллекции может быть затронута, если она содержит сущность, доступную только для чтения.
Подробнее о влиянии сущностей, доступных только для чтения, на разные типы свойств и ассоциаций, см. Раздел 12.2 «Влияние «только для чтения» на тип свойства».
Подробнее о том, как сделать сущности доступными только для чтения см. в разделе 12.1 «Создание постоянных сущностей «только для чтения»
Hibernate выполняет некоторую оптимизацию для сущностей только для чтения:
- Экономит время выполнения, не проводя «грязные» проверки простых свойств или односторонних ассоциаций.
- Сохраняет память, удаляя снимки (snapshots) базы данных.
12.1. Создание постоянных сущностей «только для чтения»
Только постоянные сущности могут быть доступны только для чтения. Переходные и отсоединённые сущности должны быть переведены в постоянное состояние, прежде чем их можно будет сделать доступными только для чтения.
Hibernate предоставляет следующие способы создания постоянных сущностей только для чтения:
- вы можете отобразить класс сущности как неизменяемый (immutable); когда сущность неизменяемого класса становится постоянной, Hibernate автоматически делает её доступной только для чтения. См. раздел 12.1.1 «Сущности неизменяемых классов» для получения подробной информации.
- вы можете изменить значение по умолчанию, чтобы сущности, загруженные в сессию с помощью Hibernate, автоматически становились только для чтения. См. раздел 12.1.2 «Загрузка постоянных сущностей как «только для чтения» для получения подробной информации.
- вы можете сделать Query или Criteria HQL только для чтения, так что сущности, загруженные когда Query или Criteria выполняются, прокручиваются или итерируются, автоматически становились доступными только для чтения. См. раздел 12.1.3 «Загрузка сущностей «только для чтения» из Query/Criteria HQL» для получения подробной информации
- вы можете создать постоянную сущность, которая уже находится в сессии только для чтения. См. раздел 12.1.4 «Создание постоянной сущности только для чтения» для получения подробной информации
12.1.1. Сущности неизменяемых (immutable) классов
Когда экземпляр сущности неизменяемого класса становится постоянным, Hibernate автоматически делает его доступным только для чтения.
Сущность неизменяемого класса может быть создана и удалена так же, как сущность изменяемого класса.
Hibernate рассматривает постоянную сущность неизменяемого класса так же, как постоянную сущность только для чтения изменяемого класса. Единственное исключение состоит в том, что Hibernate не позволит изменить сущность неизменяемого класса, чтобы он не был доступен только для чтения.
12.1.2. Загрузка постоянных сущностей как «только для чтения»
Заметка
Сущности неизменяемых классов автоматически загружаются как «только для чтения».
Для измения поведения по умолчанию, чтобы Hibernate загружал экземпляры сущностей изменяемых классов в сессию и автоматически делал их доступными только для чтения, вызовите:
Session.setDefaultReadOnly( true );
Для измения значения по умолчанию, чтобы сущности, загруженные Hibernate, не были доступны только для чтения, вызовите:
Session.setDefaultReadOnly( false );
Вы можете определить текущую настройку, вызвав:
Session.isDefaultReadOnly();
Если isDefaultReadOnly()
возвращает true, сущности, загруженные одним из следующим методов,
автоматически становятся доступными только для чтения:
- Session.load()
- Session.get()
- Session.merge()
- выполнение, прокрутка или итерация Query и Criteria HQL; для переопределения этой установки для конкретного Query или Criteria HQL см. раздел 12.1.3. «Загрузка сущностей «только для чтения» из Query/Criteria HQL»
Изменение этого значения по умолчанию не влияет на:
- постоянные сущности уже в сессии, когда было изменено значение по умолчанию
-
постоянные сущности, обновлённые через
Session.refresh()
; обновлённая постоянная сущность будет доступна только для чтения, если она была доступена только для чтения, перед обновлением -
постоянные сущности, добавленные приложением через
Session.persist()
,Session.save()
,Session.update()
иSession.saveOrUpdate()
12.1.3. Загрузка сущностей «только для чтения» из Query/Criteria HQL
Заметка
Сущности неизменяемых классов автоматически загружаются как «только для чтения».
Если Session.isDefaultReadOnly()
возвращает false (по умолчанию), когда выполняется
Query или Criteria HQL, сущности и прокси изменяемых классов, загружаемых запросом, не будут
доступны только для чтения.
Вы можете переопределить это поведение, чтобы сущности и прокси, загруженные Query или Criteria HQL, автоматически делались только для чтения.
Для Query HQL вызовите:
Query.setReadOnly( true );
Query.setReadOnly(true)
необходимо вызывать перед Query.list()
,
Query.uniqueResult()
, Query.scroll()
или Query.iterate()
.
Для Criteria HQL вызовите:
Criteria.setReadOnly( true );
Criteria.setReadOnly(true)
необходимо вызвать до Criteria.list()
,
Criteria.uniqueResult()
или Criteria.scroll()
.
Сущности и прокси, которые существуют в сессии перед возвратом Query или Criteria HQL, не затрагиваются.
Неинициализированные постоянные коллекции, возвращаемые запросом, не затрагиваются. Позже, когда
коллекция будет инициализирована, сущности, загруженные в сессию, будут доступны только для чтения,
если Session.isDefaultReadOnly()
вернет значение true.
Использование Query.setReadOnly(true)
или Criteria.setReadOnly(true)
хорошо работает,
когда один Query или Criteria HQL загружает все сущности и инициализирует все прокси и коллекции,
которые в приложении должны быть доступны только для чтения.
Когда невозможно загрузить и инициализировать все необходимые сущности в одном Query или Criteria, вы можете временно изменить значение сессии по умолчанию для загрузки сущностей как только для чтения, прежде чем запрос будет выполнен. Затем вы можете явно инициализировать прокси и коллекции, прежде чем восстанавливать настройки по умолчанию.
Session session = factory.openSession(); Transaction tx = session.beginTransaction();
setDefaultReadOnly( true ); Contract contract = ( Contract ) session.createQuery( "from Contract where customerName = 'Sherman'" ) .uniqueResult(); Hibernate.initialize( contract.getPlan() ); Hibernate.initialize( contract.getVariations() ); Hibernate.initialize( contract.getNotes() ); setDefaultReadOnly( false ); ... tx.commit(); session.close();
Если Session.isDefaultReadOnly()
возвращает true, вы можете использовать
Query.setReadOnly(false)
и Criteria.setReadOnly(false)
, чтобы переопределить
этот параметр сессии и загрузить сущности, которые не «только для чтения».
12.1.4. Создание постоянной сущности «только для чтения»
Заметка
Постоянные сущности неизменяемых классов автоматически делаются как «только для чтения».
Чтобы сделать постоянную сущность или прокси доступным только для чтения, вызовите:
Session.setReadOnly(entityOrProxy, true)
Чтобы изменить сущность только для чтения или прокси изменяемого класса, чтобы она больше не была доступной только для чтения, вызовите:
Session.setReadOnly(entityOrProxy, false)
Важно
Когда сущность или прокси, доступные только для чтения, изменяются, они перестают быть «только для чтения». Hibernate предполагает, что текущее состояние сущности, доступной только для чтения, соответствует представлению базы данных. Если это не так, то любые не сброшенные (non-flushed) изменения, сделанные до или в то время, когда сущность была доступна только для чтения, будут проигнорированы.
Чтобы отбросить любые несброшенные (non-flushed) изменения и сделать постоянную сущность совместимой с своим представлением в базе данных, вызовите:
session.refresh( entity );
Чтобы сбросить (flush) изменения, сделанные до или в то время, когда сущность была доступна только для чтения и сделать представление базы данных согласованным с текущим состоянием постоянной сущности, вызовите:
// вытеснить сущность, доступную только для чтения, так, чтобы она стала отсоединённой session.evict( entity ); // сделать отсоединённую сущность (с несброшенными (non-flushed) изменениями) постоянной session.update( entity ); // теперь сущность больше не доступна для чтения и её изменения могут быть сброшены (flush) s.flush();
12.2. Влияние «только для чтения» на тип свойства
В следующей таблице показано, как влияют различные типы свойств, делая сущность доступной только для чтения.
Тип свойства/ассоциации | Изменения, сброшены (flush) в БД? |
---|---|
Простой (Простые свойства) | нет* |
Однонаправленная «один-к-одному» Однонаправленная «многие-к-одному» (Раздел Однонаправленные «один-к-одному» и «многие-к-одному» ) |
нет* |
Однонаправленная «один-ко-многим» Однонаправленная «многие-ко-многим» (Раздел Однонаправленные «один-ко-многим» и «многие-ко-многим» ) |
да |
Двунаправленная «один-к-одному» (Раздел Двунаправленная «один-к-одному») |
только если владеющая сущность «только для чтения»* |
Двунаправленная «один-ко-многим» Двунаправленная «многие-к-одному» обратная (inverse) коллекция необратная (non-inverse) коллекция (Раздел Двунаправленные «один-ко-многим» и «многие-к-одному» ) |
только добавленные/удалённые сущности, которые не доступны для чтения* |
Двунаправленная «многие-ко-многим» (Раздел Двунаправленная «многие-ко-многим») |
да |
*Поведение отличается, когда сущность, обладающий свойством/ассоциацией, доступна только для чтения, по сравнению с тем, когда она доступна не «только для чтения».
12.2.1. Простые свойства
Когда постоянный объект доступен только для чтения, Hibernate не делает «грязную» проверку простых свойств.
Hibernate не будет синхронизировать изменение состояния простых свойств с базой данных. Если у вас есть автоматическое управление версиями, Hibernate не будет увеличивать версию, если будут изменены какие-либо простые свойства.
Session session = factory.openSession(); Transaction tx = session.beginTransaction();
// получить контракт и сделать его доступным только для чтения Contract contract = ( Contract ) session.get( Contract.class, contractId ); session.setReadOnly( contract, true );
// contract.getCustomerName() сейчас "Sherman" contract.setCustomerName( "Yogi" ); tx.commit();
tx = session.beginTransaction();
contract = ( Contract ) session.get( Contract.class, contractId ); // contract.getCustomerName() всё ещё "Sherman" ... tx.commit(); session.close();
12.2.2. Однонаправленные ассоциации
12.2.2.1. Однонаправленные «один-к-одному» и «многие-к-одному»
Hibernate обрабатывает однонаправленные ассоциации «один-к-одному» и «многие-к-одному» таким же образом, когда владеющая сущность доступна только для чтения.
Мы используем термин однонаправленная односторонняя ассоциация, ссылаясь на функциональность, которая является общей для однонаправленных ассоциаций «один-к-одному» и «многие-к-одному».
Hibernate не делает «грязную» проверку однонаправленных односторонних (single-ended) ассоциаций, когда владеющая сущность доступна только для чтения.
Если вы измените ссылку сущности только для чтения на однонаправленную одностороннюю ассоциацию на null или ссылку на другую сущность, это изменение не будет сброшено (flush) в базу данных.
Заметка
Если сущность имеет неизменяемый класс, то его ссылки на однонаправленные односторонние ассоциации должны быть назначены, когда эта сущность впервые создается. Поскольку сущность автоматически создается только для чтения, эти ссылки не могут быть обновлены.
Если используется автоматическое управление версиями, Hibernate не будет увеличивать версию из-за локальных изменений в однонаправленных односторонних ассоциациях.
В следующих примерах Contract имеет однонаправленную связь «многие-ко-многим» с Plan. Contract выполняет каскадом операции сохранения и обновления в ассоциации.
Ниже показано, что изменение ссылки на связь «один-к-одному» для сущности «только для чтения» на значение null не влияет на представление сущности в базе данных.
// получить контракт с существующим планом tx = session.beginTransaction(); Contract contract = ( Contract ) session.get( Contract.class, contractId ); // сделать контракт доступным только для чтения. session.setReadOnly( contract, true ); // установить его план в null. contract.setPlan( null ); tx.commit(); // получить тот же контракт. tx = session.beginTransaction(); contract = ( Contract ) session.get( Contract.class, contractId ); // contract.getPlan () по-прежнему ссылается на исходный план. tx.commit(); session.close();
Следующий пример показывает, что, несмотря на то, что обновление ассоциации «многие-к-одному» у сущности, доступной только для чтения, не влияет на представление сущности в базе данных, flush все еще каскадирует операцию сохранения-обновления в локально измененную ассоциацию.
// получить контракт с существующим планом; tx = session.beginTransaction(); Contract contract = ( Contract ) session.get( Contract.class, contractId ); // сделать договор доступным только для чтения. session.setReadOnly( contract, true ); // перейти на новый план. Plan newPlan = new Plan( "new plan" contract.setPlan( newPlan); tx.commit(); // получить тот же контракт. tx = session.beginTransaction(); contract = ( Contract ) session.get( Contract.class, contractId ); newPlan = ( Contract ) session.get( Plan.class, newPlan.getId() ); // contract.getPlan () по-прежнему ссылается на исходный план. // newPlan не имеет значения null, поскольку он сохранялся, когда // предыдущая транзакция была сохранена (commit); tx.commit(); session.close();
12.2.2.2. Однонаправленные «один-ко-многим» и «многие-ко-многим»
Hibernate обрабатывает однонаправленные ассоциации «один-ко-многим» и «многие-ко-многим», принадлежащие сущности, доступной только для чтения, так же, как и принадлежащие сущности, которая доступна не «только для чтения».
Hibernate делает «грязную» проверку однонаправленных ассоциаций «один-ко-многим» и «многие-ко-многим».
Коллекция может содержать сущности, которые доступны только для чтения, а также сущности, которые доступны не «только для чтения».
Сущности могут быть добавлены и удалены из коллекции; изменения сбрасываются (flush) в базу данных.
Если используется автоматическое управление версиями, Hibernate будет обновлять версию из-за изменений в коллекции, если они «загрязняют» владеющую сущность.
12.2.3. Двунаправленные ассоциации
12.2.3.1. Двунаправленная «один-к-одному»
Если сущность, доступная только для чтения, имеет двунаправленную ассоциацию «один-к-одному»:
- Hibernate не делает «грязную» проверку ассоциации.
- обновления, которые изменяют ссылку ассоциации на null или ссылаются на другую сущность, не будут сброшены (flush) в базу данных.
- Если используется автоматическое ведение версий, Hibernate не будет увеличивать версию из-за локальных изменений в ассоциации.
Заметка
Если сущность имеет неизменяемый класс и ему принадлежит двунаправленная ассоциация «один-к-одному», тогда её ссылка должна быть присвоена, когда эта сущность впервые создается. Поскольку сущность автоматически создается только для чтения, эти ссылки не могут быть обновлены.
Когда владелец доступен не «только для чтения», Hibernate рассматривает ассоциацию с сущностью только для чтения так же, как если ассоциация связана с сущностью, которая доступна не «только для чтения».
12.2.3.2. Двунаправленные «один-ко-многим» и «многие-к-одному»
Сущность, доступная только для чтения, не влияет на двунаправленную связь «один-ко-многим»/«многие-к-одному», если:
- сущность только для чтения находится на стороне «один-ко-многим», используя обратную (inverse) коллекцию;
- сущность только для чтения находится на стороне «один-ко-многим», используя необратную (non-inverse) коллекцию;
- сторона «один-ко-многим» использует необратную (non-inverse) коллекцию, содержащую сущность только для чтения
Когда сторона «один-ко-многим» использует обратную коллекцию:
- сущность только для чтения, может быть добавлена только в коллекцию, когда она создана;
- сущность только для чтения, может быть удалена только из коллекции с помощью удаления сироты (orphan) или путем явного удаления сущности.
12.2.3.3. Двунаправленная «многие-ко-многим»
Hibernate обрабатывает двунаправленные ассоциации «многие-ко-многим», принадлежащие сущности, доступной только для чтения, так же, как когда она принадлежит сущности, который доступна не «только для чтения».
Hibernate выполняет «грязные» проверки двунаправленных ассоциаций «многие-ко-многим».
Коллекция по обе стороны ассоциации может содержать сущности, которые доступны только для чтения, а также сущности, которые доступны не «только для чтения».
Сущности добавляются и удаляются с обеих сторон коллекции; изменения сбрасываются (flush) в базу данных.
Если используется автоматическое ведение версий, Hibernate обновит версию из-за изменений в обеих сторонах коллекции, если они загрязняют сущность, владеющую соответствующими коллекциями.