Инструменты с классами сущностей Javassist, используемыми Hibernate

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

X x = newInstance(X.class) //X is an abstract class

Эта библиотека использует Javassist для создания подкласса во время выполнения, который будет создан методом newInstance.

Насколько я понимаю, Hibernate также использует Javassist для инструментария классов сущностей во время выполнения (пожалуйста, поправьте меня, если это не так).

У меня вопрос, можно ли заставить эти два фреймворка работать вместе? Я имею в виду, могу ли я сказать Hibernate, что каждый раз, когда ему нужен экземпляр класса сущности (абстрактный класс), он должен использовать определенный фабричный метод из моей библиотеки?

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

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


person Sergio    schedule 23.03.2013    source источник


Ответы (1)


Прежде чем перейти к ответу:

  • Да, вы правы, Hibernate в настоящее время использует Javassist (в прошлом он использовал GCLib, но он устарел) для инструментальных классов во время выполнения.
  • Hibernate действительно создает подклассы во время выполнения, у которых есть прокси для постоянных сущностей.

Короткий ответ

К сожалению, я не думаю, что вы сможете настроить Hibernate для использования собственной фабрики. Для подробностей предлагаю прочитать часть с длинным ответом.

Длинный ответ

Насколько мне известно, в настоящее время Hibernate 4.x поддерживает только Javassist в качестве поставщика манипуляций с байт-кодом. Хотя раньше он позволял переключаться между GClib и Javassist в версиях 3.x. Еще в этих версиях вы могли изменить фабрику, которая будет использоваться, настроив глобальный параметр гибернации под названием hibernate.bytecode.provider.

Этот параметр больше не отображается в документации по Hibernate 4.1, но вы все равно можете найти немного об этом в документации для Hibernate 3.2 в разделе дополнительные свойства конфигурации.

Поскольку я сам разработчик, я знаю, что иногда мы оказываемся хитрыми ребятами, и то, что что-то не в документации, не обязательно означает, что это вне кода :-) Итак, я подумал, возможно, если параметр все еще существует, мы можем попытаться использовать его, чтобы делать то, что вы хотите (хотя и не поддерживаемым способом).

Ради любопытства, поскольку на моей машине был код Hibernate 4.0.1 (заметьте, что это не последняя версия), я немного покопался ... И, к удивлению, это свойство все еще существует! После отслеживания использованных ссылок (спасибо Eclipse) я оказался в классе org.hibernate.cfg.Environment (код для версии 4.2.0.CR2), где я нашел следующий код ( код был одинаков как в моей версии, так и в 4.2.0CR2):

 public static BytecodeProvider buildBytecodeProvider(Properties properties) {
    String provider = ConfigurationHelper.getString( BYTECODE_PROVIDER, properties, "javassist" );
    LOG.bytecodeProvider( provider );
    return buildBytecodeProvider( provider );
}

private static BytecodeProvider buildBytecodeProvider(String providerName) {
    if ( "javassist".equals( providerName ) ) {
        return new org.hibernate.bytecode.internal.javassist.BytecodeProviderImpl();
    }

    LOG.unknownBytecodeProvider( providerName );
    return new org.hibernate.bytecode.internal.javassist.BytecodeProviderImpl();
}

Насколько я могу судить, это реализация Javassist для вашей прокси-фабрики, и стандартного способа ее изменить не будет.


Замечание о сумасшедшем

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

‹Взлом молотка›

  • Вы можете попробовать расширить свою собственную структуру для инструментария классов, чтобы не только добавить байт-код для ваших нужд, но также и байт-код, который нужен спящему режиму - что, я бы сказал, было бы чем-то вроде слияния ваших манипуляций с манипуляциями org.hibernate.bytecode.internal.javassist.BytecodeProviderImpl.
  • Затем вы можете переименовать свой класс расширения в BytecodeProviderImpl, поместить его в тот же пакет org.hibernate.bytecode.internal.javassist и, наконец, поместить его где-нибудь в пути к классам, где загрузчик классов найдет его перед тем, что находится в банке (или, возможно, с помощью пользовательского загрузчика классов)

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

‹/ Взлом молотка›

В любом случае, если у вас есть время и желание попробовать, дайте мне знать, сработало ли это.


ОБНОВЛЕНИЕ

Немного поговорив в разделе комментариев, у меня возникла идея использовать пользовательский EntityPersister. Поскольку я не был очень уверен в этом, я немного погуглил, чтобы посмотреть, смогу ли я найти что-то, что могло бы сказать мне, что то, о чем я думал, сработает или нет.

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

Но первый ответ на этот вопрос дает ссылку на нечто похожее на то, о чем я думал. Цитата Паскаля Тивента:

настраиваемая реализация EntityPersister (которую можно зарегистрировать для конкретного сущность во время инициализации Hibernate с использованием настраиваемой конфигурации)

Верно, что это пример для Hibernate в Grails, но в простой Java он почти такой же:

 public void registerCustomEntityPersister(Configuration configuration) {
     final Iterator<PersistentClass> classesIterator = configuration.getClassMappings();
 while (classesIterator.hasNext()) {
    final PersistentClass persistentClass = classesIterator.next();
        if (checkIfIsOneTheClassesThatMatters(persistentClass)) {
          persistentClass.etEntityPersisterClass(CustomEntityPersister.class); 
        }
 }

Несмотря на то, что это кажется выполнимым, похоже, слишком много работы, потому что реализация EntityPersister не выглядит таким тривиальным ... слишком много вещей. Вы можете попытаться расширить стандарт, который используется Hibernate (я действительно не знаю, какой именно), и попытаться переопределить метод getProxy (), чтобы вернуть один из ваших инструментальных классов.

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

Я надеюсь, что по крайней мере я дал вам информацию, которая может вам помочь.

person pabrantes    schedule 26.03.2013
comment
lol, понравилась часть о том, как я слушал Hibernate, мой фреймворк и вся JVM, кричащая в панике :). Я так понял, что фабричным методом вроде не вариант. Но в Hibernate вообще нет поддержки абстрактных классов сущностей (или интерфейсов сущностей). Я имею в виду, что это должен быть способ, которым классы сущностей объявляются абстрактными, и каким-то образом мы можем сообщить Hibernate, какие именно классы он должен использовать, когда требуется создать экземпляры этих абстрактных классов сущностей. - person Sergio; 26.03.2013
comment
Хорошо читая ваш комментарий, я начинаю думать, что возможно можно было бы создать такую ​​инъекцию зависимостей путем сопоставления абстрактного класса / интерфейса с аннотацией @MappedSuperclass, а затем создания настраиваемого EntityPersister (docs.jboss.org/hibernate/orm/3.6/javadocs/org/ hibernate /) для этого интерфейса, где вы должны решить, какой класс будет загружен. Но не уверен ... - person pabrantes; 26.03.2013
comment
@sergio: Извините, я забыл упомянуть вас в последнем комментарии, и я не думаю, что вы получите уведомление в противном случае. - person pabrantes; 26.03.2013
comment
привет @pabrantes, спасибо за отзыв !. Мне нужно время, чтобы обработать и понять ваши ссылки (несколько веков назад работал с Hibernate, и я многое забыл). В любом случае это выглядит немного сложновато (но это не было неожиданностью). Я также обновил свой вопрос в соответствии с нашим обсуждением. - person Sergio; 26.03.2013
comment
@Sergio: Мне очень жаль, но я не могу прямо сейчас обновить свой вопрос с этой идеей. Но дайте мне несколько часов (4 или 5), и я обновлю свой вопрос, добавив более точную информацию о том, что я вам только что сказал. - person pabrantes; 26.03.2013