07 Aug 2014

A collection was no longer referenced by the owner

The full error message looks like this:

org.hibernate.HibernateException: A collection with cascade="all-delete-orphan" 
was no longer referenced by the owning entity instance: 
org.javatalks.training.hibernate.entity.bag.Book.comments`.

The error says that Entity had a Collection with all-delete-orphan configured. Then instead of changing the original Collection a brand new Collection was set. Here is the code that’s responsible for handling such cases in Hibernate (class org.hibernate.engine.internal.Collections):
if ( e != null && e.getStatus() != Status.DELETED && e.getStatus() != Status.GONE ) {
	throw new HibernateException(
			"A collection with cascade=\"all-delete-orphan\" was no longer referenced by the owning entity instance: " +
			loadedPersister.getRole()
	);
}

The code intentionally checks whether the Collection is referenced by any Entity or not (in case the Owner was removed). Unfortunately code doesn’t explain why this is a problem - this is probably a way to ensure the user didn’t accidentally overwrite the Collection. Anyway the correct way of removing Collections or their items is to use remove()/removeAll(). Here are 2 cases you can reproduce the error (in Groovy):

Collection is overwritten and the Owner gets updated

@Test
    void 'when updating, if orphan collection is dereferenced, exception thrown'() {
        List<Comment> comments = [new Comment(body: "comment1"), new Comment(body: "comment2")]
        Book original = new Book(title: "with comments", comments: comments)
        bookDao.save(original).flushSession()

        try {
            //it's not allowed to replace collection with cascade=orphan
            original.comments = [new Comment(body: 'new')]
            bookDao.saveOrUpdate(original).flushSession()
            assert false
        } catch (HibernateException e) {
            assert e.message.startsWith('A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance')
        }
    }

Owner gets merged, but new Collection is null

This is a tricky one. Usually if we merge a detached Entity the collection is updated correctly - those elements that are now absent - they get deleted from resulting Owner. But if new Collection is null..

@Test
    void 'when merging, if new collection is null, exception thrown'() {
        List<Comment> comments = [new Comment(body: "a"), new Comment(body: "b")]
        Book original = new Book(title: "with comments", comments: comments)
        bookDao.save(original).flushAndClearSession()

        try {
            bookDao.merge(new Book(id: original.id, comments: null))//exception only due to null
            bookDao.flushSession()
            assert false
        } catch (HibernateException e) {
            assert e.message.startsWith('A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance')
        }
    }

Read more
09 Jun 2014

Кэш Hibernate

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

Hibernate обращается к кэшу в следующих случаях:

  • Приложение выполняет поиск сущности по идентификатору

  • Приложение выполняет ленивую загрузку коллекции

Кэши разделяются в зависимости от области видимости (scope) на следующие виды:

  • Session scope cache (Кэш привязанный к транзакции, действительный только пока транзакция не завершиться. Каждая транзакция имеет свой кэш, следовательно, доступ к данному кэшу не может быть осуществлён в несколько потоков)

  • Process scope cache (Кэш привязанный к определённому процессу конкретной JVM и общий для многих транзакций с возможностью параллельного доступа)

  • Cluster scope cache (Кэш, общий для нескольких процессов на одной машине или нескольких машин в составе кластера).

По сути, Transaction scope cache представляет собой кэш первого уровня hibernate, кэш же второго уровня может быть реализован либо в области видимости процесса илибо как распределённый кэш.

Общий алгоритм работы кэша hibernate:

  1. Когда сессия пытается загрузить объект в первую очередь он ищется в кэше первого уровня
  2. Если в кэше первого уровня присутствует нужный объект, он возвращается как результат выполнения метода
  3. Если в кэше первого уровня объект не был найден, он ищется в кэше второго уровня
  4. Если объект сохранён в кэше второго уровня, он сохраняется так же в кэш первого уровня и возвращается в качестве результата метода
  5. Если в кэше второго уровня объекта так же не находится, то делается запрос к базе данных, и результат записывается сразу в оба кэша.

Ниже подробенее рассмотрим работу кэша первого и второго уровня.

Кэш первого уровня

Кэш первого уровня в hibernate связан с объектом сессии, он включён по умолчанию и нет возможности отключить его. Когда вы передаёте объект в метод save(), update() или saveOrUpdate(), а так же когда пытаетесь обратиться к нему с помощью методов load(), get(), scroll(), list(), iterate() выполняется добавление элемента в кэш сессии и следующий раз, когда нужно будет произвести повторную выборку данного объекта из БД в текущей сессии обращения к БД уже не произойдёт. Объект будет взят из кэша.

Обнуление кэша происходит после закрытия сессии. Так же, содержимым кэша можно управлять используя методы класса Session:

  • contains() - проверяет сохранён ли объект в кэше

  • flush() - синхронизирует содержимое кэша с базой данных

  • evict() - удаляет объект из кэша

  • clear() - обнуляет кэш

Кэш второго уровня

Кэш второго уровня в hibernate может быть настроен как кэш процесса, так и как распеределённый кэш (в рамках JVM или кластера). В отличие от кэша первого уровня, использование кэша второго уровня является опциональным. Он может быть как включён так и отключён.

В кэше второго уровня сущности хранятся в разобранном состоянии (что-то наподобие сериализованного состояния, однако, используемый алгоритм намного быстрее сериализации). Соответственно, доступ к объектам, сохранённым в кэше второго уроня осуществляется не по сслыке, а по значению. Обусловлено это ещё и тем, что доступ к сущности может осуществляться из параллельных транзакций. Так, каждая транзакция будет иметь свою копию данных.

Учитывая вышеперечисленное, были разработаны следующие стратегии паралельного доступа к кэшу второго уровня:

  • read only - используется для данных, которые часто читаются но никогда не изменяются. Изменение данных при использовании этой стратегии приведёт к исключению. Испльзование данного подхода в распределённом кэше позволяет не волноваться об синхронизации данных, однако может привести к сильной загрузке базы на начальном этапе работы кластера. Связано это с тем, что неизменяемые данные необходимы обычно большенству узлов кластера, и каждый из этих узлов будет обращаться напрямую к базе данных, пока не закэширует все необходимые объекты.

  • nonstrict read write - используется для данных, которые изменяются очень редко. При параллельном доступе к данным из разных транзакций данная стратегия не даёт никакой гарантии, что в кэше будут сохранены актуальные данные, так как Hibernate никак не изолирует от других транзакций данные во время изменения. Следовательно, чтение, которое выполняется параллельно изменению может получить доступ к данным, которые ещё не были закоммичены. Не слудует использовать данную стратегию, если небольшая вероятность считывания устаревших данных критична для приложения. При использовании распределённого кэша вероятность работы с устаревшими данными сильно увеличивается, так как устаревшие данные могут быть отражены в кэше многих узлов кластера и сохраняться там достаточно долго (до следующего обновления).

  • read write - используется для данных которые гораздо чаще читаются, чем обновляются, однако, устаревание которых критично для приложения. При этом подходе, данные блокируются для чтения во время их изменении с использованием “soft-lock” механизма. При попытке обратиться к заблокированным данным в данном случае будет произведён запрос к БД. Блокировака будет снята только после коммита. Данная стратегия обеспечивает уровень изоляции транзакций read commited. Использоване этого попхода для распределённого кэша имеет существенные недостатки. Так, если в кластере имеется возможность изменения одних и тех же данных разными узлами, довольно часто могут случаться блокировки устаревших данных в кэше, что сводит на нет приемущества использования данной стратегии. Для того чтобы избавиться от этого нужно использовать кэш-провайдер, поддерживающий блокировки.

  • transactional - используется, когда необходима изоляция транзакций вполоть до уровня repeatable read. Так же как и предыдущие используется для данных, которые гораздо чаще читаются нежели обновляются.

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

Ни одна из вышеперечисленных стратегий не реализуется самим хибернэйтом. Для этого используются провайдеры кэша, основные из которых:

  • EhCache - изначально разрабатывался как кэш уровня процесса, однако, в последник версиях появилась возможность реализации так же распределённого кэша. Имеется поддрержка кэша запросов и выгрузки данных на диск.

  • OpenSymphony OSCache - реализует кэш только на уровне процесса. Поддерживает кэш запросов и выгрузку данных на диск.

  • SwarmCache - распределённый кэш, который базирется на JGroups. Нет поддержки кэша запросов.

  • JBoss Cache — может быть как локальным так и распределённым. Это полностью транзакционный кэш с возможностью репликации и инвалидаци, а так же с возможностью обмена как синхронными так и асинхронными валидационными сообщениями.

Однако, не каждый провайдер совместим с каждым типом стратегии параллельного доступа (смотри таблицу ниже)

Read-only Nonstrict-read-write Read-write Transactional
EhCache Yes Yes Yes No
OSCache Yes Yes Yes No
SwarmCache Yes Yes No No
Jboss Cache Yes No No Yes

Стоит заметить, что для удобства поиска, сущности разных классов, а так же сущности, принадлежащие разным коллекциям, сохраняются в разных регионах кэша. Под регионом, в данном случае, понимается некая именованная область кэша. Имя региона совпадает с именем класса, в случае кэширования классов, или с именем каласса объеденённым с именем свойства, в случае кэширования коллекций. Типичная настройка региона кэша на примере EhCache приведена наже:

<cache name="org.example.model.Entity"
	maxElementsInMemory="500" 
	timeToIdleSeconds="0" 
	timeToLiveSeconds="0" 
	eternal="true" 
	overflowToDisk="false" 
/>

Рассмотрим подробнее каждый из параметров:

  1. name — имя региона кэша
  2. maxElemetsInMemory — максимально возможное число объектов, хранимых в памяти
  3. timeToIdleSeconds — задаёт время жизни объекта в кэше с последнего обращения
  4. timeToLiveSeconds — задаёт время жизни объекта в кэше, начиная с его помещения туда
  5. eternal — атрибут определяющий будут ли элементы удаляться из кэша по истечении таймаута. Если этот атрибут установлен в true, то таймаут будет игнорироваться
  6. overflowToDisk — атрибут, определяющий, будут ли объекты выгружаться на диск при достижении maxElemetsInMemory. Имеет смысл включать, если БД и приложение развёрнуты на разных машинах.

Механизмы синхронизации данных в распределённом кэше

Приложения, которыми пользуется параллельно большое количество людей могут потребовать гораздо больей вычислительной мощности, чем может предоставить единичный сервер. В таком случае, несколько физических машин объеденяются в кластер. Для увеличения общей производительности кластера иногда полезно использовать распределённый кэш hibernate. В таком случае, каждый процесс хранит в своём кэше локальную копию недавно используемых данных. Поддержание актуальности этих данных осуществляется засчёт сетевого взаимодействия процессов с использованием протокола TCP либо UDP.

Механизмы поддержания актуальности данных:

  • копирование (replication) - если состояние сущности было изменнено, то её новое состояние будет разослано каждому члену кластера, что ведёт к повышенному потреблению трафика. А так же принуждает разные узлы кластера сохранять в свём кэше одинаковые данные, что не всегда полезно, так как, обычно, разные члены кластера работают с разными данными.

  • анулирование (invalidation) - если состояние сущности было изменено, то всем узлам кластера рассылается сообщение, указывающее на то, что сущность с определённым идентификатором была изменена. После получения сообщения другие члены кластера проверяют свой кэш на предмет наличия данной сущности, и, если таковая там находися, она удаляется из кэша. Данный подход не создаёт такой большой нагрузки на сеть как предыдущий, а основным его недостатком является то, что если изменённая сущность снова понадобится, она будет загружена запросом к базе данных, а это может привести к дополнительной нагрузке на БД. Однако, данные, которые необходимы многим узлам кластера обычно изменяются очень и очень редко.

Механизмы передачи сообщений о состоянии объектов:

  • синхронный - после отправи сообщения об изменении объекта в кэше доступ к кэшу блокируется, пока не будет получено подтверждение от всех узлов кластера.

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

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

Полезные ссылки:

Read more
30 Apr 2014

A collection was no longer referenced by the owner

Полная ошибка выглядит так:

org.hibernate.HibernateException: A collection with cascade="all-delete-orphan" 
was no longer referenced by the owning entity instance: 
org.javatalks.training.hibernate.entity.bag.Book.comments`.

Собственно ошибка говорит о том, что была у Сущности коллекция, стоял значит у нее all-delete-orphan, а тут вместо этой коллекции Сущности подсунули другую. Код, ответственный за такой случай находится в классе org.hibernate.engine.internal.Collections:
if ( e != null && e.getStatus() != Status.DELETED && e.getStatus() != Status.GONE ) {
	throw new HibernateException(
			"A collection with cascade=\"all-delete-orphan\" was no longer referenced by the owning entity instance: " +
			loadedPersister.getRole()
	);
}

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

Коллекция перезаписывается и владелец update’ится

@Test
    void 'when updating, if orphan collection is dereferenced, exception thrown'() {
        List<Comment> comments = [new Comment(body: "comment1"), new Comment(body: "comment2")]
        Book original = new Book(title: "with comments", comments: comments)
        bookDao.save(original).flushSession()

        try {
            //it's not allowed to replace collection with cascade=orphan
            original.comments = [new Comment(body: 'new')]
            bookDao.saveOrUpdate(original).flushSession()
            assert false
        } catch (HibernateException e) {
            assert e.message.startsWith('A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance')
        }
    }

Владелец merge’ится, однако новая коллекция - null

А вот этот случай хитрый. Обычно если мы merge’им детачнутую сущность, то коллекция нормально обновляется - отсутствующие в новой коллекции элементы будут удаляться. Но не тут-то было когда коллекция является null’ом:

@Test
    void 'when merging, if new collection is null, exception thrown'() {
        List<Comment> comments = [new Comment(body: "a"), new Comment(body: "b")]
        Book original = new Book(title: "with comments", comments: comments)
        bookDao.save(original).flushAndClearSession()

        try {
            bookDao.merge(new Book(id: original.id, comments: null))//exception only due to null
            bookDao.flushSession()
            assert false
        } catch (HibernateException e) {
            assert e.message.startsWith('A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance')
        }
    }

Read more
21 Dec 2012

Пример настройки Database Connection Pool

Собственно хочу поделиться и описать конфигурацию DB Pool’a который мы используем в JTalks:

<!--Создаем Ленивый DataSource, который по возможности не будет вытягивать настоящее--> 
    <!--соединение к БД из настоящего пула до тех пор пока это правда необходимо. Например, -->
    <!--если Hibernate достает данные из кеша и не делает запрос в БД, нам не нужно--> 
    <!--настоящее соединение. Однако если не использовать ленивый DataSource, то соединение--> 
    <!--будет вытянуто и транзакция будет начата если метод помечен @Transactional-->
  <bean id="dataSource" class="org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy">
    <property name="targetDataSource">
    <!--Это собственно настоящий DB Pool. Многие говорят что пул, который Hibernate--> 
    <!--использует по умолчанию не является серьезным и использование его в production-->
    <!--коде не допустимо. Однако C3P0 как раз не является пулом по умочанию, у Hibernate -->
    <!--есть свой внутренний пул для "поиграться", однако это не C3P0 как многие думают! С другими-->
    <!--же пулами не сравнивал на самом деле, поэтому не могу сказать какой лучше,--> 
    <!--C3P0 используется исторически во многих приложениях с Hibernate. -->
      <bean class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
        <property name="driverClass" value="${jdbc.driverClassName}"/>
        <property name="jdbcUrl" value="${jdbc.url}"/>
        <property name="user" value="${JCOMMUNE_DB_USER:root}"/>
        <property name="password" value="${JCOMMUNE_DB_PASS:root}"/>
        <!--Эта опция показывает сколько PreparedStatement'ов должно быть закешировано-->
        <!--на стороне Java, в самом Connection Pool. Имейте в виду что СУБД может -->
        <!--требовать дополнительной настройки для того чтоб эта опция показала себя эффективной-->
        <property name="maxStatements" value="1500"/>
        <!--А это сколько PreparedStatement'ов каждое соединение может кешировать для себя -->
        <property name="maxStatementsPerConnection" value="50"/>
        <!--Вот это самая сложная опция и для того чтоб ее правильно настроить,-->
        <!--нам нужны нагрузочные тесты а также рабочее в PROD приложение-->
        <!--Есть разные стратегии по работе с соединениями которые влияют на оптимальный-->
        <!--размер DB Pool'a, подробней читайте прекрасную книгу Release It!-->
        <property name="maxPoolSize" value="50"/>
        <!--MySQL прибивает соединение если оно не использовалось какое-то время.--> 
        <!--По умолчанию это 8 часов. Дабы избежать неприятных исключений нам -->
        <!--нужно бомбардировать MySQL запросами каждый N часов (минут)-->
        <property name="idleConnectionTestPeriod" value="3600"/>
        <!--Если мы испытываем большую нагрузку и/или запросы выполняются очень долго,-->
        <!--мы сможем обслуживать 50 пользователей одновременно (размер пула), однако -->
        <!--при увеличении нагрузки, клиенты начнут выстраиваться в очередь и просто ждать-->
        <!--заблокированные в synchronized методах внутри DB Pool'a. Дабы мы не оказались-->
        <!--в ситуации, когда мы заблокированы надолго и приложение совсем не отвечает, -->
        <!--через 10 секунд простаивания в очереди выбрасывается исключение и сервер разгружается-->
        <!--от лишних запросов. Да, это исключение и это неприятно, однако лучше отвалится -->
        <!--несколько клиентов (особенно если это DDoS'еры поганые) нежели сайт будет в полной отключке.-->
        <property name="checkoutTimeout" value="10000"/>
        <!--Также соединение может издохнуть. Пул может проверять его на работоспособность-->
        <!--в то время как приложение запрашивает соединение и наоборот - когда возвращает обратно.-->
        <!--Первое - более надежное, второе - более быстрое.-->
        <property name="testConnectionOnCheckin" value="true"/>
      </bean>
    </property>
  </bean>


com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: The last packet successfully received from the server was 64,129,968 milliseconds ago.  The last packet sent successfully to the server was 64,129,968 milliseconds ago. is longer than the server configured value of 'wait_timeout'. You should consider either expiring and/or testing connection validity before use in your application, increasing the server configured values for client timeouts, or using the Connector/J connection property 'autoReconnect=true' to avoid this problem.
    at com.mysql.jdbc.Util.handleNewInstance(Util.java:411)
    at com.mysql.jdbc.SQLError.createCommunicationsException(SQLError.java:1116)
    at com.mysql.jdbc.MysqlIO.send(MysqlIO.java:3851)
Read more
18 Dec 2012

Hibernate. Аннотации против XML

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

Хотя в интернетах можно найти много споров по поводу того какой подход лучше: аннотации или XML - они в большей своей степени субъективны и зависят от личных религиозных соображений. Хотелось бы здесь описать объективные причины когда и почему один из способов может быть лучше или хуже с практической стороны.
Итак, плюсы аннотаций:

  • Это движение к стандартизации если говорить про JPA-аннотации. Это может быть неправдой если вы используете Hibernate аннотации, и вам это придется делать если маппинг будет хотя бы чуточку сложный. Однако это все равно ближе к стандарту нежели XML (конечно есть JPA XML, однако никогда не видел его применения на практике, поэтому речь здесь веду именно об hbm.xml).

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

Минусы аннотаций:

  • Аннотациями вы “загрязняете” свои классы. Это может быть неправдой если у вас достаточно простой маппинг и, особенно, когда имена сущностей совпадают с именами таблиц (что редкость..). Представьте что вам еще нужно JAXB, Lucene маппинги на сущностях делать. В какой-то момент эти все аннотации трудно будет читать.

  • Вам придется писать HQL запросы прямо в Java-классах, что неизбежно приведет к конкатенации оных. В общем читабельность таких запросов ниже плинтуса.

В то же время минусы XML:

  • Появляются те самые доп файлы.

  • Формат ниразу не стандартный, и это затруднит переход к другим JPA реализациям.

Ну и наконец плюсы XML:

  • Оставляет классы чистыми аки совесть младенца. Это особенно приятно в случае со сложными маппингами.

  • Там можно оставлять длинные HQL запросы, и они не так будут смущать глаз.

  • XML проще для начинающих, потому как есть XSD/DTD, автоподсказки и т.п. С аннотациями же нужно точно знать что куда можно ставить, и в редких случаях нагуглить решение совсем не тривиально.

  • Есть возможность замапить одну и ту же Java сущность на несколько таблиц. Пример: представьте что у вас есть продукт (StoreItem) у которого может быть набор свойств. Эти свойства могут быть самыми разными: картинка, цвет, масса и пр. Все они должны иметь определения (хотя бы название -> значение). Причем значения еще могут иметь какие-то умолчания. Итого, появляется таблица PROPERTY_DEFINITION в которую помещаются все возможные свойства и их значения. Ну и конечно есть таблица со значениями этих свойств для конкретных продуктов ITEM_PROPERTY. Когда создается StoreItem мы копируем какие-то свойства из PROPERTY_DEFINITION и помещаем их в ITEM_PROPERIY, на которую уже ссылается конкретный StoreItem. Итого мы имеем класс Property, который может маппиться на обе таблицы. Аннотации не смогут дать такую гибкость, в то время как XML может использовать entity-name.

  • Если вы делите общие классы между несколькими проектами помещая их в jar-файлы, вполне возможно, что проекты захотят по-разному мапить эти сущности на таблицы БД. Иногда это важно и у вас просто не остается выбора кроме как использовать XML (прошу заметить что я не призываю к такому дизайну, и даже наоборот, считаю подход с общими классами неправильным, однако такое случается на практике, и иногда мы просто это получаем в руки).

  • Также бывают случаи, что классы генерируются по XSD или каким-то другим определениям типов. Такие классы могут не находится в репозитории и создаваться во время сборки. Если вы хотите замапить такие классы, у вас не остается выбора кроме как XML. Однако имейте в виду, что вы скорей всего не хотите оказаться в такой ситуации, обычно DB Entities и DTO - это разные классы, иначе рефакторинг одной из сторон будет усложнять жизнь, если делаете так, то только на начальных этапах проекта с заметкой “на рефакторинг”.

  • БД-нейтральность. Если наше приложение должно поддерживать несколько СУБД, на аннотациях мы бы писали два модуля с разными сущностями и разным маппингом, потому как в реальной жизни Hibernate не настолько СУБД-нейтрален, те же стратегии для генерации PK: sequence vs. identity. В случае же с XML мы можем создать два модуля с разными маппингами.

Read more
18 Dec 2012

Логирование в Hibenrate + MySQL. Конфигурация Log4j/logback

Как логировать генерируемые Hibernate’ом запросы? Как логировать параметры запросов? Может ли лог Hibernate’a лгать?

Если вы работаете с Hibernate, вам скорей всего понадобится видеть, то ли для оптимизации, то ли для дебага какие запросы и зачем он генерирует. Следующие свойства следует описать в hibernate.cfg.xml ну или передать во время конфигурирования SessionFactory.

Самое важное, это возможность увидеть SQL генерируемый Хибом:

<property name="show_sql">true</property>

Однако запросы появятся только в консоли. Дабы по-настоящему логировать SQL запросы, нужно указать следующий логер в log4j: org.hibernate.SQL=TRACE. Обратите внимание на заглавные буквы SQL.

Идем дальше. С этим параметром Хиб будет выводить комментарии в лог о том, зачем тот или иной запрос выполняется - загрузка ли какой-то сущности, удаление ли, просто синхронизация и т.п.:

<property name="use_sql_comments">true</property>

А следующая опция будет форматировать выводимый запрос, иначе запрос будет в одну строку:

<property name="format_sql">true</property>

А такой логер нужно настроить чтоб Hibernate логировал значения передаваемые в PreparedStatement вместо ? и :name

log4j.logger.org.hibernate.type = TRACE

А вот так будет выглядеть файл конфигурации для logback (groovy-версия):
import ch.qos.logback.classic.encoder.PatternLayoutEncoder
import ch.qos.logback.core.ConsoleAppender

import static ch.qos.logback.classic.Level.*

appender("consoleAppender", ConsoleAppender) {
    encoder(PatternLayoutEncoder) {
        pattern = "[%d{HH:mm:ss.SSS}] [%thread] [%-5level] [%logger{36}] - %msg%n"
    }
}

/**
 * This would allow us to see parameters passed into prepared statements
 */
logger("org.hibernate.type.descriptor.sql.BasicBinder", TRACE)
/**
 * We don't want to include the whole org.hibernate.type package to get rid of noise, 
 * thus we need to include necessary classes explicitly.
 */
logger("org.hibernate.type.EnumType", TRACE)
/**
 * Shows executed SQL statements. This one is better than show_sql because the latter can log only to console. Note,
 * capitalized SQL letters, it's important.
 */
logger("org.hibernate.SQL", TRACE)
root(INFO, ["consoleAppender"])

DB/Driver Logging

Однако и это не все. Иногда Hibernate не в состоянии логировать запрос как есть, потому как он генерируется самим драйвером, так например происходит во время Batch операций. Для того, чтоб увидеть такие запросы по-настоящему (Hibernate их будет выводить как и другие запросы), нужно смотреть на логи БД или JDBC Driver’a. Рассмотрим пример MySQL. Увидеть запросы можно либо заглянув в /var/log/mysql/mysql.log (это может зависить от версии и настроек MySQL Server’a), либо указав в строке соединения параметры логирования:

jdbc.url=jdbc:mysql://localhost/hib_training?characterEncoding=UTF-8\
  &rewriteBatchedStatements=true\
  &logger=com.mysql.jdbc.log.StandardLogger\
  &profileSQL=true

Стандарный логер (он будет использоваться по умолчанию) может вас не удовлетворить потому как он будет выводить сообщения не синхронно с самим Hibernate. По этим или другим причинам вы можете настроить com.mysql.jdbc.log.Slf4JLogger либо com.mysql.jdbc.log.Jdk14Logger.

Read more