Фильтрация JPA "один ко многим"

Мы размещаем несколько объектов. Однако при извлечении мы хотим получить только те объекты, которые активны.

@Entity
public class System {
  @Id
  @Column(name = "ID")
  private Integer id;

  @OneToMany(mappedBy = "system")
  private Set<Systemproperty> systempropertys;
}

@Entity
public class Systemproperty {
  @Id
  @Column(name = "ID")
  private Integer id;

  @Id
  @Column(name = "ACTIVE")
  private Integer active;
}

При запросе Systemproperties я хочу получить только те свойства active (active = 1).

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

У Eclipselink есть @Customizer аннотация, которая могла бы работать, однако, похоже, она соответствует другой концепции, тогда аннотация hibernate @FilterDef и при переключении вызовет дополнительные накладные расходы.

@JoinColumn, похоже, не позволяет продолжить фильтрацию. Существует ли стандартный способ JPA для решения этой проблемы?

Ответ 1

AFAIK не существует переносного метода на основе JPA. Чистым, хотя и немного неэффективным решением было бы сделать все на стороне Java и создать getter getActiveSystemproperties(), который вручную выполняет итерацию над отображаемым systempropertys и возвращает неизменяемый набор активных свойств.

Ответ 2

Другой спящий способ сделать это с помощью @Where:

@Entity
public class System {
  @Id
  @Column(name = "ID")
  private Integer id;

  @OneToMany(mappedBy = "system")
  @Where("active = true")
  private Set<Systemproperty> systempropertys;
}

@Entity
public class Systemproperty {
  @Id
  @Column(name = "ID")
  private Integer id;

  @Id
  @Column(name = "ACTIVE")
  private Integer active;
}

Ответ 4

Мне нужно было решить подобную проблему в EclipseLink. Я использовал специальный SessionCustomizer и изменил условия отображения для аннотации OneToMany. У Maybee Hibernate есть что-то подобное.

Добавить элемент настройки для сохранения:

props.put(PersistenceUnitProperties.SESSION_CUSTOMIZER,MySessionCustomizer.class.getName());
EntityManagerFactory factory = new PersistenceProvider().createEntityManagerFactory(pu.getPersistenceUnitName(), props);

Фрагмент настройки:

public class MySessionCustomizer implements SessionCustomizer {
    public void customize(Session session) throws Exception {
        final Map<?, ?> descs = session.getDescriptors();
    if (descs != null)
    {
      // This code assumes single table per descriptor!
      for (final Object descObj : descs.values())
      {
        final ClassDescriptor desc = (ClassDescriptor) descObj;
        final Class<?> sourceClass = desc.getJavaClass();

        for (DatabaseMapping mapping : desc.getMappings())
        {
          if (mapping instanceof OneToManyMapping)
          {
            final OneToManyMapping collectionMapping = ((OneToManyMapping) mapping);

            // create default foreign key condition (nescessary):
            final DatabaseField sourceField = mapping.getSourceKeyFields().get(0);
            final DatabaseField targetField = mapping.getTargetForeignKeyFields().get(0);
            final Expression defaultFkExpression =  new ExpressionBuilder(mapping.getReferenceClass()).getParameter(sourceField).equal(eb.getField(targetField));

            // add filter condition "additionalExpression"
            final Expression finalExpression = defaultFkExpression.and(additionalExpression);

             // SET default foreign key condition and filter condition to mapping
             mapping.setSelectionCriteria(finalExpression);
          }
        }
      }
    }
    }
}