Фильтр гибернации не применяется для объекта объединения

#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 Аннотацию можно использовать, только если у вас есть таблица ссылок между родительским объектом и дочерней таблицей.