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?
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;
}
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.
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);
}
}
}
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With