Создание onetoone-отношения ленивый
в этом приложении мы разрабатываем, мы заметили, что вид был особенно медленным. Я профилировал представление и заметил, что был один запрос, выполненный hibernate, который занял 10 секунд, даже если в базе данных было только два объекта для выборки. Все OneToMany и ManyToMany отношения были ленивыми, так что это не было проблемой. При проверке фактического выполнения SQL я заметил, что в запросе было более 80 соединений.
дальнейшее изучение вопроса, я заметил, что проблема была вызвана глубокой иерархией OneToOne и ManyToOne отношения между классами сущностей. Так что, подумал я, я просто сделаю их ленивыми, что должно решить проблему. Но аннотирование либо @OneToOne(fetch=FetchType.LAZY) или @ManyToOne(fetch=FetchType.LAZY) не работает. Либо я получаю исключение, либо они фактически не заменяются прокси-объектом и, следовательно, ленивы.
есть идеи, как я заставлю это работать? Обратите внимание, что я не использую persistence.xml для определения отношений или сведения о конфигурации , все делается в java-коде.
6 ответов:
во-первых, некоторые пояснения к KLE'ы ответ:
неограничена (обнуляемого) один-к-одному ассоциации является единственной, которая не может быть индикатора без байт-код инструментария. Причина этого заключается в том, что объект-владелец должен знать, должно ли свойство ассоциации содержать прокси-объект или NULL, и он не может определить это, глядя на столбцы своей базовой таблицы из-за того, что один к одному обычно сопоставляется через общий PK, поэтому он должен быть нетерпеливым в любом случае, это делает прокси бессмысленным. Вот это более подробно объяснение.
многие-к-одному (и один-ко-многим, очевидно) не страдают от этой проблемы. Объект-владелец может легко проверить свой собственный FK (и в случае one-to-many пустой прокси-сервер коллекции создается изначально и заполняется по требованию), поэтому ассоциация может быть ленивой.
замена один-к-одному один-ко-многим почти никогда не является хорошей идеей. Вы можете замените его уникальным много-к-одному, но есть и другие (возможно, лучшие) варианты.
Роб Х. имеет действительную точку, однако вы не можете быть в состоянии осуществлять его в зависимости от вашей модели (например, если ваш один-к-одному ассоциации и nullable).
теперь, что касается оригинального вопроса:
A)
@ManyToOne(fetch=FetchType.LAZY)должно работать нормально. Вы уверены, что он не перезаписывается в самом запросе? Можно указатьjoin fetchв HQL и / или явно установите режим выборки через API критериев, который будет иметь приоритет над аннотацией класса. Если это не так, и у вас все еще возникают проблемы, отправьте свои классы, запрос и результирующий SQL для более подробного разговора.Б)
@OneToOneсложнее. Если это определенно не обнуляется, перейдите к предложению Роба Х. и укажите его как таковое:@OneToOne(optional = false, fetch = FetchType.LAZY)в противном случае, если вы можете изменить свою базу данных (добавить столбец внешнего ключа в таблицу владельца), сделайте это и сопоставить его как "присоединился":
@OneToOne(fetch = FetchType.LAZY) @JoinColumn(name="other_entity_fk") public OtherEntity getOther()а в другом месте:
@OneToOne(mappedBy = "other") public OwnerEntity getOwner()если вы не можете этого сделать (и не можете жить с нетерпеливой выборкой) инструментирование байт-кода-ваш единственный вариант. Я должен согласиться с CPerkins, однако - если у вас есть 80!!! присоединяется из-за нетерпеливых ассоциаций OneToOne, у вас есть большие проблемы, чем это : -)
отложенная загрузка работает на обнуляемого типа один-к-одному сопоставления, вы должны позволить впадают в спячку...скомпилировать приборов времени и добавить
@LazyToOne(value = LazyToOneOption.NO_PROXY)один-к-одному отношения.Примере Сопоставления:
@OneToOne(fetch = FetchType.LAZY) @JoinColumn(name="other_entity_fk") @LazyToOne(value = LazyToOneOption.NO_PROXY) public OtherEntity getOther()пример расширения файла сборки Ant (для выполнения инструментирования времени компиляции Hibernate):
<property name="src" value="/your/src/directory"/><!-- path of the source files --> <property name="libs" value="/your/libs/directory"/><!-- path of your libraries --> <property name="destination" value="/your/build/directory"/><!-- path of your build directory --> <fileset id="applibs" dir="${libs}"> <include name="hibernate3.jar" /> <!-- include any other libraries you'll need here --> </fileset> <target name="compile"> <javac srcdir="${src}" destdir="${destination}" debug="yes"> <classpath> <fileset refid="applibs"/> </classpath> </javac> </target> <target name="instrument" depends="compile"> <taskdef name="instrument" classname="org.hibernate.tool.instrument.javassist.InstrumentTask"> <classpath> <fileset refid="applibs"/> </classpath> </taskdef> <instrument verbose="true"> <fileset dir="${destination}"> <!-- substitute the package where you keep your domain objs --> <include name="/com/mycompany/domainobjects/*.class"/> </fileset> </instrument> </target>
основная идея behing XToOnes в спящем режиме заключается в том, что они не ленивы в большинстве случаев.
одна из причин заключается в том, что, когда Hibernate должен решить поставить прокси (с идентификатором) или null,
он должен смотреть в другую таблицу в любом случае присоединиться. Стоимость доступа к другой таблице в базе данных значительна, поэтому она может также извлекать данные для этой таблицы в этот момент (не ленивое поведение), вместо того, чтобы извлекать это в более позднем запросе, который потребует второй доступ к той же таблице.отредактировано: для получения дополнительной информации см. ответ ChssPly76. Этот менее точен и детализирован, ему нечего предложить. Спасибо ChssPly76.
вот то, что работает для меня (без инструментов):
вместо
@OneToOneС обеих сторон, я использую@OneToManyв обратной части отношения (тот, сmappedBy). Это делает свойство коллекцией (Listв примере ниже), но я перевожу его в элемент в геттере, что делает его прозрачным для клиентов.Эта настройка работает лениво, то есть выбор производится только тогда, когда
getPrevious()илиgetNext()называются - и только один выберите для каждого вызова.структура таблицы:
CREATE TABLE `TB_ISSUE` ( `ID` INT(9) NOT NULL AUTO_INCREMENT, `NAME` VARCHAR(255) NULL, `PREVIOUS` DECIMAL(9,2) NULL CONSTRAINT `PK_ISSUE` PRIMARY KEY (`ID`) ); ALTER TABLE `TB_ISSUE` ADD CONSTRAINT `FK_ISSUE_ISSUE_PREVIOUS` FOREIGN KEY (`PREVIOUS`) REFERENCES `TB_ISSUE` (`ID`);класс:
@Entity @Table(name = "TB_ISSUE") public class Issue { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) protected Integer id; @Column private String name; @OneToOne(fetch=FetchType.LAZY) // one to one, as expected @JoinColumn(name="previous") private Issue previous; // use @OneToMany instead of @OneToOne to "fake" the lazy loading @OneToMany(mappedBy="previous", fetch=FetchType.LAZY) // notice the type isnt Issue, but a collection (that will have 0 or 1 items) private List<Issue> next; public Integer getId() { return id; } public String getName() { return name; } public Issue getPrevious() { return previous; } // in the getter, transform the collection into an Issue for the clients public Issue getNext() { return next.isEmpty() ? null : next.get(0); } }
в собственных XML-сопоставлениях Hibernate вы можете выполнить это, объявив один на один сопоставление с ограниченного атрибут имеет значение true. Я не уверен, что такое эквивалент аннотации Hibernate/JPA, и быстрый поиск документа не дал ответа, но, надеюсь, это даст вам возможность продолжить.
Как уже прекрасно объяснил ChssPly76, прокси Hibernate не помогают с неограниченными (нулевыми) ассоциациями один к одному, но есть трюк, объясненный здесь чтобы избежать настройки приборов. Идея в том, чтобы обмануть гибернации, что класс сущностей, который мы хотим использовать уже инструментированных: вы прибор вручную в исходном коде. Это просто! Я реализовал его с CGLib в качестве поставщика байт-кода, и он работает (убедитесь, что вы настроили lazy= "no-proxy"и fetch=" select", а не" join", в вашем HBM).
Я думаю, что это хорошая альтернатива реальные (Я имею в виду автоматическое) инструментирование, когда у вас есть только одно отношение один-к-одному, которое вы хотите сделать ленивым. Основным недостатком является то, что решение зависит от поставщика байт-кода, который вы используете, поэтому точно прокомментируйте свой класс, потому что в будущем вам может потребоваться изменить поставщика байт-кода; конечно, вы также изменяете свой модельный компонент для техническая причина и это не нормально.
Comments