Как заставить спящий режим распечатывать ошибки с именованным запросом?

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

Caused by: org.hibernate.HibernateException: Errors in named queries: FindAllCompanyFileTypes
    at org.hibernate.impl.SessionFactoryImpl.<init>(SessionFactoryImpl.java:426)
    at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1872)
    at org.hibernate.ejb.Ejb3Configuration.buildEntityManagerFactory(Ejb3Configuration.java:906)
    ... 70 more

Как настроить hibernate для распечатки того, что не так с именованным запросом, а не только именованный запрос имеет ошибку?

UPDATE, например, запрос JPA SELECT f FROM Foo WHERE f.v := true завершится неудачей, когда спящий режим будет жаловаться на недопустимость запроса. Hibernate даже не пытался генерировать SQL из него, запрос неправильный JPQL. Я хочу знать, как заставить спящий режим сказать, что запрос неверен, потому что: = вместо =? не уверен, что это параметр, который можно включить в спящем режиме или нет.

Ответ 1

Материал загрузчика пользовательских запросов Hibernate находится в org.hibernate.loader.custom.sql (для Hibernate 3 и кажется, что Hibernate 4 тоже). Если вы используете log4j, это зависит только от установки этого пакета собственного категорию, чтобы напечатать журналы (я рекомендую вам использовать файл-приложение, потому что последующие журналы ошибок могут перекрывать то, что вас интересует, если вы используете консольный appender).

<category name="org.hibernate.loader.custom.sql" additivity="false">
    <priority value="trace" />
    <appender-ref ref="fileAppender" />
</category>

Предположим, что ваш корневой регистратор отображает каждую ошибку в файле, что вывод, который вы получаете для ошибки при загрузке запроса:

17:27:18,348 TRACE SQLCustomQuery:85 -     starting processing of sql query [SELECT equipment.*, det.*
        FROM tdetectable_equipment equipment JOIN
        tdetectable det
        ON det.id = equipment.id_detectable
        WHERE
        equipment.id_detectable=det.id and det.active=1 and
        equipment.id_warehouse_container = :_Id]
17:27:18,358 TRACE SQLCustomQuery:85 -     starting processing of sql query [select line.*  from tpacking_slip_line line  join tpacking_slip slip  on line.id_packing_slip = slip.id where line.id_detectable = :detectableId and line.id_related_packing_slip_line is null and slip.`type`= :slipType order by slip.date desc;]
17:27:18,359 TRACE SQLQueryReturnProcessor:387 -     mapping alias [line] to entity-suffix [0_]
17:27:18,364 TRACE SQLCustomQuery:85 -     starting processing of sql query [select res.* from tdetectable det  join tpacking_slip_line line on det.id=line.id_detectable  and line.id_related_packing_slip_line is null join tpacking_slip slip on line.id_packing_slip = slip.id  and slip.`type`='OUT' join vreservation res on slip.id_reservation=res.id where det.id in ( :detIds ) group by(res.id) order by count(res.id) desc;]
17:27:18,365 TRACE SQLQueryReturnProcessor:387 -     mapping alias [res] to entity-suffix [0_]
17:27:18,368 ERROR SessionFactoryImpl:424 -     Error in named query: equipmentWarehouseQuery
org.hibernate.MappingException: Unknown collection role: com.mycompany.model.container.Container.detectables
    at org.hibernate.impl.SessionFactoryImpl.getCollectionPersister(SessionFactoryImpl.java:701)
    at org.hibernate.loader.custom.sql.SQLQueryReturnProcessor.addCollection(SQLQueryReturnProcessor.java:393)
    at org.hibernate.loader.custom.sql.SQLQueryReturnProcessor.processCollectionReturn(SQLQueryReturnProcessor.java:428)
    at org.hibernate.loader.custom.sql.SQLQueryReturnProcessor.processReturn(SQLQueryReturnProcessor.java:358)
    at org.hibernate.loader.custom.sql.SQLQueryReturnProcessor.process(SQLQueryReturnProcessor.java:171)
    at org.hibernate.loader.custom.sql.SQLCustomQuery.<init>(SQLCustomQuery.java:87)
    at org.hibernate.engine.query.NativeSQLQueryPlan.<init>(NativeSQLQueryPlan.java:67)
    at org.hibernate.engine.query.QueryPlanCache.getNativeSQLQueryPlan(QueryPlanCache.java:166)
    at org.hibernate.impl.SessionFactoryImpl.checkNamedQueries(SessionFactoryImpl.java:589)
    at org.hibernate.impl.SessionFactoryImpl.<init>(SessionFactoryImpl.java:413)
    at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1872)
    at org.springframework.orm.hibernate3.LocalSessionFactoryBean.newSessionFactory(LocalSessionFactoryBean.java:863)
    at org.springframework.orm.hibernate3.LocalSessionFactoryBean.buildSessionFactory(LocalSessionFactoryBean.java:782)
    at org.springframework.orm.hibernate3.AbstractSessionFactoryBean.afterPropertiesSet(AbstractSessionFactoryBean.java:188)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1541)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1479)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:521)

Это исключение возникает во время загрузки, но при выполнении кода вы также можете иметь ошибки запроса. В этом случае вызывается HibernateException или аналогичный, который вы можете проверить позже. Для случая отсутствия двоеточия это фактически IllegalArgumentException, брошенный AbstractQueryImpl класс:

java.lang.IllegalArgumentException: No positional parameters in query: SELECT equipment.*, det.*
        FROM tdetectable_equipment equipment JOIN
        tdetectable det
        ON det.id = equipment.id_detectable
        WHERE
        equipment.id_detectable=det.id and det.active=1 and
        equipment.id_container = _Id

Ответ 2

Я работал над аналогичной проблемой, и я обнаружил, что если вы используете Spring Data JPA, вы можете использовать @Query в интерфейсе DAO вместо @NamedQuery в объекте сущности, и вы получите гораздо более подробный сообщение об ошибке. Итак, вместо:

@Entity
@Table
@NamedQueries({@NamedQuery(name="MyEntity.myQuery" query="select blah blah blah")})
public class MyEntity
{
...
}

вы бы сказали

@Entity
@Table
public class MyEntity
{
...
}

public interface MyEntityDao
    extends JpaRepository...
{
    @Query("select blah blah blah")
    MyEntity findByMyQuery();
}

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

Ответ 4

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

Итак, вам нужна база данных, чтобы сообщить вам, что не так с запросом. Для этого вы включаете вывод sql из спящего режима, как описано выше. Затем вы подключаетесь к БД через свой любимый инструмент: squirrelSQL, toad, eclipse-plugin и т.д. Теперь, если у вас нет параметров в вашем sql, вы просто вырезаете и вставляете туда, выполняете sql, и это должно помочь вам отладки.

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