#java #hibernate #java-ee-8 #hibernate-filters
#java #гибернация #java-ee-8 #hibernate-фильтры
Вопрос:
Я пытаюсь понять фильтры гибернации, я думал, что фильтр применяется, даже если запрос не запускается из отфильтрованного объекта и может быть применен, если я просто присоединюсь к нему.
Мои объекты:
@Table(name = "SPM_SECTION", schema = "TEST")
@GenericGenerator(name = "MODSEC_ID.GEN", strategy = "uuid2")
public class ModuleSection implements Serializable {
private AcademicClass academicClass;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "CLASS_ID")
public AcademicClass getAcademicClass() {
return academicClass;
}
public void setAcademicClass(AcademicClass academicClass) {
this.academicClass = academicClass;
}
}
@Entity
@GenericGenerator(name = "AC_CLASS_ID.GEN", strategy = "uuid2")
@Where(clause="1=1")
@FilterDef(name= Resources.SECURITY_FILTER_NAME, parameters={@ParamDef(name=Resources.SECURITY_FILTER_PARAMETER, type="string")})
@Filter(name=Resources.SECURITY_FILTER_NAME, condition = "DISCRIMINATOR_ID in (:" Resources.SECURITY_FILTER_PARAMETER ")")
@Table(name = "ACADEMIC_CLASS", schema = "TEST", uniqueConstraints = @UniqueConstraint(columnNames = {"OUS_ID", "YEAR_ID",
"PERIOD_ID", "SHIFT_ID", "SEMESTER", "CODE" }))
public class AcademicClass implements java.io.Serializable {
//I tried by having the association here, i also tried without it.
private Set<ModuleSection> sections = new HashSet<>(0);
@OneToMany(fetch = FetchType.LAZY, mappedBy = "academicClass")
public Set<ModuleSection> getSections() {
return this.sections;
}
public void setSections(Set<ModuleSection> sections) {
this.sections = sections;
}
}
Фильтр включается с помощью перехватчика, а список параметров извлекается из базы данных для обеспечения безопасности.
Когда я выполняю запрос, подобный этому:
em.createQuery("select acc from AcademicClass acc ...........", AcademicClass.class)
.getResultList();
фильтр применяется. Но я также хочу, чтобы фильтр применялся, когда мой запрос начинается с ModuleSection:
em.createQuery("select ms from ModuleSection ms join ms.academicClass acc", AcademicClass.class)
.getResultList();
В приведенном выше запросе фильтр не применяется.
academicClass в объекте ModuleSection имеет значение null, но у меня также есть другие объекты, отличные от null, где приведенный выше случай не работает.
Я также безуспешно пытался применить @Filter или @FilterJoinTable в свойстве раздела модуля:
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "CLASS_ID")
@Filter(name=Resources.SECURITY_FILTER_NAME, condition = "DISCRIMINATOR_ID in (:" Resources.SECURITY_FILTER_PARAMETER ")")
@FilterJoinTable(name=Resources.SECURITY_FILTER_NAME, condition = "DISCRIMINATOR_ID in (:" Resources.SECURITY_FILTER_PARAMETER ")")
public AcademicClass getAcademicClass() {
return academicClass;
}
Мои вопросы:
Предназначены ли фильтры для фильтрации только объекта в предложении from ? применяется ли фильтр к объединяемым объектам?
Если я хочу реализовать вышеуказанное, должен ли я также добавить DISCRIMINATOR_ID в ModuleSection и добавить фильтр к этому объекту, начиная запрос оттуда?
Комментарии:
1. Почему вы присоединяетесь вручную
academicClass
?2. Это большой запрос, в котором мне нужен фильтр, если я присоединяюсь к отфильтрованному объекту. Выше приведен только пример.
3. Я предполагаю, что Hibernate должен использовать свой собственный механизм для обнаружения и применения фильтров, и если вы создаете запрос вручную, вы не используете механизм гибернации для создания объединений, чтобы их нельзя было фильтровать.
4. Таким образом, фильтр применяется только тогда, когда у меня есть объект для предложения from, которого нет в joins? Потому что, когда объект находится в предложении from, фильтр применяется правильно.
Ответ №1:
В документации hibernate об этом ничего не говорится, но похоже, что это правда, которая @Filter
применяется только к from
предложению.
Предполагая, что у нас есть следующее сопоставление:
@Entity
@Table(name = "ACADEMIC_CLASS")
@FilterDef(
name="isAccessible",
parameters = @ParamDef(
name="sec",
type="string"
)
)
@Filter(
name="isAccessible",
condition="{acClass}.discriminator_id in (:sec)",
aliases = {
@SqlFragmentAlias(alias = "acClass", table= "TEST_SCHEMA.ACADEMIC_CLASS")
}
)
public class AcademicClass
{
@Id
@Column(name = "class_id")
private Long id;
@OneToMany(fetch = FetchType.LAZY, mappedBy = "academicClass")
private Set<ModuleSection> sections;
// getters/setters
}
@Entity
@Table(name = "SPM_SECTION")
public class ModuleSection
{
@Id
@Column(name = "sec_id")
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "sec_class_id")
private AcademicClass academicClass;
// getters/setters
}
Когда мы выполняем следующий запрос:
session
.enableFilter("isAccessible")
.setParameter("sec", "A1");
List<AcademicClass> classes = session.createQuery(
"select ac from ModuleSection ms join ms.academicClass ac",
AcademicClass.class
).getResultList();
Фильтр не применяется. Это должно произойти в JoinSequence.toJoinFragment. В filterCondition
этом случае значение пусто.
Но для переписанного следующим образом запроса:
List<AcademicClass> classes = session.createQuery(
"select ac from ModuleSection ms, AcademicClass ac where ms.academicClass = ac",
AcademicClass.class
).getResultList();
У нас будет:
и в результате будет сгенерирован следующий запрос:
Hibernate:
/* select
ac
from
ModuleSection ms,
AcademicClass ac
where
ms.academicClass = ac
*/
select
academiccl1_.class_id as class_id1_0_
from TEST_SCHEMA.SPM_SECTION modulesect0_ cross
join TEST_SCHEMA.ACADEMIC_CLASS academiccl1_
where academiccl1_.discriminator_id in (?)
and modulesect0_.sec_class_id=academiccl1_.class_id
Итак, в качестве обходного пути вы можете переписать свой запрос таким образом.
@FilterJoinTable
Аннотацию можно использовать, только если у вас есть таблица ссылок между родительским объектом и дочерней таблицей.