Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Selecting generic primary key with CriteriaQuery

When migrating from Hibernate Criteria api to CriteriaQuery I ran into a generic DAO for a abstract class that has a where on a common field but does a select on their id, even if the ids are totally different per class.

The old projection looks like this

criteria.setProjection(Projections.id());

Is there any way to do this in a similar way with CriteriaQuery?

Edit: Full criteria code

DetachedCriteria detachedCriteria = DetachedCriteria.forClass(MyEntity.class);
detachedCriteria.add(Restrictions.in("accountID", accounts));
detachedCriteria.setProjection(Projections.id());

EntityManager em = ...;
Criteria criteria = detachedCriteria.getExecutableCriteria((Session) em.getDelegate());
List<Integer> list = criteria.list();
like image 690
Jeppz Avatar asked May 25 '18 08:05

Jeppz


2 Answers

I just managed to find it on my own.

criteriaQuery.select(
    root.get(entityRoot.getModel().getDeclaredId(int.class))
);
like image 187
Jeppz Avatar answered Nov 04 '22 17:11

Jeppz


Combining answers:

https://stackoverflow.com/a/16911313

https://stackoverflow.com/a/47793003

I created this method:

public String getIdAttribute(EntityManager em, String fullClassName) {
    Class<? extends Object> clazz = null;
    try {
        clazz = Class.forName(fullClassName);
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
    Metamodel m = em.getMetamodel();
    IdentifiableType<T> of = (IdentifiableType<T>) m.managedType(clazz);
    return of.getId(of.getIdType().getJavaType()).getName();
}

then I have the entity manager injected

@PersistenceContext
private EntityManager em;

I get the root entity primary key like that:

String rootFullClassName = root.getModel().getJavaType().getName();
String primaryKeyName = getIdAttribute(em, rootFullClassName);

and I get the primary keys referenced on attributes like that:

return (Specification<T>) (root, query, builder) -> {
    Set<Attribute<? super T, ?>> attributes = root.getModel().getAttributes();
    for (Attribute a: attributes) {
        if(a.isAssociation()) { 
            Path rootJoinGetName = root.join(a.getName());
            String referencedClassName = rootJoinGetName.getJavaType().getName();
            String referencedPrimaryKey = getIdAttribute(em, referencedClassName);
            //then I can use it to see if it is equal to a value (e.g 
            //filtering actors by movies with id = 1 - in 
            //this case referencedPrimaryKey is "id")
            Predicate p = rootJoinGetName.get(referencedPrimaryKey).in(1);
        }
    }
}

In this way I don't need to know the type of the primary key/referenced key in advance as it can be derived through the Entity Manager Meta Model. The above code can be used with CriteriaQuery as well as Specifications.

like image 2
Michail Michailidis Avatar answered Nov 04 '22 17:11

Michail Michailidis