Рубрики
Без рубрики

Как перехватывать и изменять SQL-запросы с помощью инспектора инструкций Hibernate

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

Автор оригинала: Vlad Mihalcea.

Вступление

Очень полезной, но менее известной функцией Hibernate является возможность перехватывать и изменять любые автоматически сгенерированные инструкции SQL с помощью утилиты Hibernate StatementInspector .

В этой статье мы рассмотрим, как работает механизм Hibernate Инспектор операторов .

Инспектор по заявлениям

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

Метод inspect принимает инструкцию SQL, которая должна быть выполнена Hibernate, и позволяет вам изменить инструкцию SQL и вернуть ее в Hibernate StatementPreparer .

Чтобы зарегистрировать реализацию интерфейса StatementInspector в режиме гибернации, вы можете использовать свойство hibernate.session_factory.statement_inspector конфигурации, которое может принимать объект StatementInspector Java, Класс или Строку объект, определяющий класс, реализующий интерфейс StatementInspector .

Ведение журнала и изменение инструкций SQL Hibernate

Чтобы лучше идентифицировать инструкции SQL, создаваемые Hibernate, мы можем использовать свойство конфигурации hibernate.use_sql_comments .:


Таким образом, при сохранении Книги сущности:

entityManager.persist(
    new Book()
    .setIsbn("978-9730228236")
    .setTitle("High-Performance Java Persistence")
    .setAuthor("Vlad Mihalcea")
);

Hibernate создает следующую инструкцию SQL:

/* insert com.vladmihalcea.book.hpjp.hibernate.logging.inspector.Book */ 
INSERT INTO book (
    author, 
    isbn, 
    title, 
    id
) 
VALUES (
    'Vlad Mihalcea', 
    '978-9730228236', 
    'High-Performance Java Persistence', 
    1
)

Обратите внимание на комментарий SQL, в котором говорится, что инструкция INSERT связана с операцией Книга сохранение сущности.

При извлечении Книги сущности по ее естественному идентификатору :

Book book = entityManager
    .unwrap(Session.class)
    .bySimpleNaturalId(Book.class)
    .load("978-9730228236");

Hibernate создает следующие инструкции SQL:

/* get current natural-id -> entity-id state 
   com.vladmihalcea.book.hpjp.hibernate.logging.inspector.Book */ 
SELECT 
    book_.id as id1_0_ 
FROM 
    book book_ 
WHERE 
    book_.isbn = '978-9730228236'

SELECT 
    book0_.id as id1_0_0_, 
    book0_.author as author2_0_0_, 
    book0_.isbn as isbn3_0_0_, 
    book0_.title as title4_0_0_ 
FROM 
    book book0_ 
WHERE 
    book0_.id = 1

Первая инструкция SQL SELECT предназначена для разрешения идентификатора сущности на основе предоставленного естественного идентификатора, как объясняется в соответствующем комментарии SQL.

Второй запрос предназначен для извлечения сущности Book на основе разрешенного идентификатора сущности.

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

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

Ведение журнала и изменение операторов SQL с помощью инспектора операторов

Следующая реализация Инспектора операторов позволяет нам регистрировать инструкцию SQL вместе с контекстом, зависящим от режима гибернации, а также удалять комментарий SQL из инструкции до ее возврата.

public class SqlCommentStatementInspector 
        implements StatementInspector {

    private static final Logger LOGGER = LoggerFactory
        .getLogger(
            SqlCommentStatementInspector.class
        );

    private static final Pattern SQL_COMMENT_PATTERN = Pattern
        .compile(
            "\\/\\*.*?\\*\\/\\s*"
        );

    @Override
    public String inspect(String sql) {
        LOGGER.debug(
            "Executing SQL query: {}",
            sql
        );

        return SQL_COMMENT_PATTERN
            .matcher(sql)
            .replaceAll("");
    }
}

Инспектор операторов комментариев Sql может быть предоставлен для перехода в режим гибернации с помощью свойства конфигурации hibernate.session_factory.statement_inspector|/.


Теперь при сохранении сущности Book Hibernate создает следующую запись в журнале и выполняет инструкцию SQL без комментария SQL:

-- Executing SQL query: 
/* insert com.vladmihalcea.book.hpjp.hibernate.logging.inspector.Book */ 
insert into book (author, isbn, title, id) values (?, ?, ?, ?)

Query:["insert into book (author, isbn, title, id) values (?, ?, ?, ?)"], 
Params:[(Vlad Mihalcea, 978-9730228236, High-Performance Java Persistence, 1)]

И то же самое касается извлечения сущности Book по ее естественному идентификатору:

-- Executing SQL query: 
/* get current natural-id->entity-id state com.vladmihalcea.book.hpjp.hibernate.logging.inspector.Book */ 
select book_.id as id1_0_ from book book_ where book_.isbn=?

Query:["select book_.id as id1_0_ from book book_ where book_.isbn=?"], 
Params:[(978-9730228236)]

-- Executing SQL query: 
select book0_.id as id1_0_0_, book0_.author as author2_0_0_, book0_.isbn as isbn3_0_0_, book0_.title as title4_0_0_ from book book0_ where book0_.id=?

Query:["select book0_.id as id1_0_0_, book0_.author as author2_0_0_, book0_.isbn as isbn3_0_0_, book0_.title as title4_0_0_ from book book0_ where book0_.id=?"], 
Params:[(1)]

Круто, правда?

Вывод

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