В настоящее время у нас есть класс, который выглядит примерно так, что (обезличенных и нерелевантные части удалены):Hibernate аннотации для одного выбора объекта (с которой), а не на-ко-многим коллекции
@Entity
@Table(name = "MAIN_TABLE")
public class MainTable extends AbstractTable {
@OneToMany(fetch = FetchType.LAZY, mappedBy = "mainTable")
@OrderBy("CREATED_ON DESC")
private Set<MainTableState> states;
...
public MainTableState getActiveState(){
if(this.states == null || this.states.isEmpty()){
return null;
}
MainTableState latest = states.iterator().next();
// The reason we use this for-loop, even though we have the @OrderBy annotation,
// Is because we can later add states to this list, which aren't automatically ordered
for(MainTableState state : states){
if(state.getCreatedOn() != null && latest.getCreatedOn() != null &&
state.getCreatedOn().after(latest.getCreatedOn()){
latest = state;
}
}
return latest;
}
...
}
Так себе он по умолчанию извлекает все MainTableStates из БД, и если нам нужна ActiveState, мы используем метод for-loop. Очевидно, это плохо для производительности. В настоящее время мы вообще не используем этот список (цель состояла в том, чтобы иметь историю состояний, но это было перенесено в будущее), но мы довольно часто используем метод getActiveState()
, в основном для отображения строки внутри MainTableState
-класс в пользовательском интерфейсе.
Кроме того, даже если мы всегда будем использовать TreeSet
и сохраним его, чтобы мы не нуждались в цикле, но вместо этого нужно только states.iterator().next()
, он все равно инициализирует список состояний. С некоторыми тяжелыми испытаниями производительности у нас было более 1 миллиона MainTableState
-в случае, когда он разбился с java.lang.OutOfMemoryError: GC overhead limit exceeded
.
Итак, мы хотим, чтобы изменить его на следующий вместо:
@Entity
@Table(name = "MAIN_TABLE")
public class MainTable extends AbstractEntity {
@???
private MainTableState activeState;
...
public MainTableStates getActiveState(){
return activeState;
}
...
}
Итак, мой вопрос, что я должен положить на @???
для достижения этой цели? Я предполагаю, что мне нужен @Formula
или что-то подобное, но как я могу сказать, чтобы спящий режим должен вернуть объект MainTableState
? Я видел, что @Formula
используется с MAX
для даты, но это должно было получить эту дату-свойство, а не получить весь объект на основе этой максимальной даты.
После @ user2447161 «s предложение я использовал @Where
-annotation, который действительно помогает уменьшить размер сбора до 1 (иногда), но у меня есть еще два взаимосвязанных вопроса:
Как использовать
@OnToMany
и@Where
, но получить один объект, а не список объектов одного размера? Возможно ли это? Here in a answer from December 2010 it is stated it isn't. Было ли это исправлено где-то за последние шесть лет?Как бороться со случайным псевдонимом в предложении where? Я мог бы сделать что-то вроде этого:
@OneToMany (скачивает = FetchType.LAZY, mappedBy = "mainTable") @Where (п = «CREATED_ON = (SELECT MAX (mts.CREATED_ON) FROM MAIN_TABLE_STATES мтс WHERE mts.FK_MAIN_ID = ???. MAIN_ID) ") private Set states; // TODO Получить один объект вместо коллекции с размером 1
Проблема с в том, что ???
случайный псевдоним генерируется спящий режим (иногда это this_
, иногда это что-то вдоль линий mainTable_1_
и т.д.). Как установить этот псевдоним для всего запроса в БД, чтобы использовать его здесь? Я также попробовал MAIN_TABLE.MAIN_ID
вместо этого, который не работает, и без псевдонима он также не работает, потому что он использует MainTableState
-alias вместо MainTable
-alias (как показано ниже).
from
MAIN_TABLE this_
left outer join
MAIN_TABLE_STATUSES mainstat2_
on this_.main_id=mainstat2_.fk_main_id
and (
mainstat2_.created_on = (
SELECT
MAX(mts.created_on)
FROM
MAIN_TABLE_STATUSES mts
WHERE
-- mainstat2_.main_id should be this_.main_id instead here:
mts.fk_main_id = mainstat2_.main_id
)
)
Если у вас нет безумного количества строк, индексов и/или очень строгих требований к производительности, я не вижу, чтобы это было проблемой производительности. С современными компьютерами цикл for, как правило, занимает nano секунд, а загрузка набора, а не одной строки, должна быть незначительной. Также убедитесь, что список нетерпеливы, когда он используется, есть значительные накладные расходы с ленивыми -loading (вероятно, на несколько больше, чем фактический цикл). – Tobb
@Tobb Я добавил еще одну строку к вопросу. Цикл for-loop не является основной проблемой, это превышение верхнего предела «java.lang.OutOfMemoryError: GC overhead» с более чем 1 миллионом экземпляров MainTableState. Это основная причина, по которой мы хотим просто сохранить одно активное состояние, а не целые коллекции (которые в настоящее время не используются). –
Возможно, проверьте @where и фильтры ... http://stackoverflow.com/questions/12365285/hibernate-limit-query-with-one-to-many. Или сделайте свой собственный DTO вне пользовательского запроса ... – user2447161