Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JPA one-to-many filtering

We are nesting several entities. However upon retrieving we only want to get those entities which are active.

@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;
}

When requesting the Systemproperties I only want to get the properties that are active (active = 1).

Searching around I found some hibernate annotations and the possibility to use subqueries. However both don't really work for me. Even though we are currently using hibernate I'm considering to replace it with Eclipselink, because we currently have to use eager loading and we are likely to run into performance problems with that. The subqueries don't really work well, because we are nesting several levels.

Eclipselink seems to have a @Customizer annotation that could work, however it seems to follow a different concept then the hibernate @FilterDef annotation and would cause additional overhead when switching.

The @JoinColumn doesn't seem to allow further filtering. Is there a standard JPA way to solve this problem?

like image 573
Udo Held Avatar asked Nov 09 '12 10:11

Udo Held


3 Answers

Another hibernate way of doing it with @Where:

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

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

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

  @Id
  @Column(name = "ACTIVE")
  private Integer active;
}
like image 173
danial Avatar answered Nov 18 '22 18:11

danial


AFAIK there is no portable JPA-based way to do this. A clean, however a little bit inefficient, solution would be to do everything on Java-side and create a getter getActiveSystemproperties() that manually iterates over mapped systempropertys and returns an immutable set of active properties.

like image 7
Adam Dyga Avatar answered Nov 18 '22 16:11

Adam Dyga


I needed to solve similar problem in EclipseLink. I used special SessionCustomizer and changed mapping condition for OneToMany annotation. Maybee Hibernate has something similar.

Add customizer to persistence unit:

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

Fragment of customizer:

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);
          }
        }
      }
    }
    }
}
like image 2
David Kacetl Avatar answered Nov 18 '22 16:11

David Kacetl