0

I would like to have a generic method performing a SELECT * FROM entity<T> WHERE entity<T>.primaryKey = method_parameter that I can use on every entity of my domain code.

So far I have this nongeneric method. It returns a list which will always only have one element because the primary keys are unqiue:

public static List<App> selectOneApp(int appid) {
    Session session = HibernateSessionFactory.getFactory().openSession();
    List<App> list = null;
    Transaction tx = null;

    try {
        tx = session.beginTransaction();

        CriteriaBuilder builder = session.getCriteriaBuilder();
        CriteriaQuery<App> criteria = builder.createQuery(App.class);
        Root<App> root = criteria.from(App.class);
        criteria.select(root);

        criteria.where(builder.equal(root.get(App_.appId), appid));
        //equivalent to WHERE App.appid = appid

        list = session.createQuery(criteria).getResultList();

        tx.commit();
    } catch (HibernateException e) {
        if (tx != null)
            tx.rollback();
        log.error(e);
    } finally {
        session.close();
    }
    return list;
}

I would like it to be somehow like this:

public static <T> List<T> selectOneRow(int key, Class<T> clazz) {
    Session session = HibernateSessionFactory.getFactory().openSession();
    List<T> list = null;
    Transaction tx = null;

    try {
        tx = session.beginTransaction();

        CriteriaBuilder builder = session.getCriteriaBuilder();
        CriteriaQuery<T> criteria = builder.createQuery(clazz);
        Root<T> root = criteria.from(clazz);
        criteria.select(root);

        criteria.where(builder.equal(root.get(clazz_.Id), key)); 
        // I got stuck here. How can I pass a generic key?

        list = session.createQuery(criteria).getResultList();

        tx.commit();
        ...
    return list;
}

I use hibernate's code generator to generate the domain code automatically. From that I genererate the JPA metamodel and everything is nice and typesafe.

I was thinking I would have to generate some kind of inheritence structure where the parent class only has a get() method and a primary key. All the entity classes that want to use this generic select method then inherit from that parent class. But I am not sure how to do that or if it is even possible.

This question was my reference but it is missing the criteria.where part: Java class - how to pass Generic Object to a function

Snippet from my data model:

CREATE TABLE app_type(
    appTypeID TINYINT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
    app_type_name VARCHAR(32)
);

CREATE TABLE app(
    appID INT UNSIGNED NOT NULL PRIMARY KEY,
    app_name VARCHAR(191),
    appTypeID TINYINT UNSIGNED,
    pubID INT UNSIGNED,
    CONSTRAINT fk_app_appType_appTypeID
        FOREIGN KEY (appTypeID) 
        REFERENCES app_type(appTypeID) 
        ON UPDATE CASCADE);

CREATE TABLE publisher(
    pubID INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
    publisher_name VARCHAR(191));

CREATE TABLE ref_app_publisher(
    appID INT UNSIGNED NOT NULL,
    pubID INT UNSIGNED NOT NULL,
    PRIMARY KEY(appID, pubID),
    UNIQUE INDEX ux_refAppPublisher_pubID_appID (pubID, appID),
    CONSTRAINT fk_refAppPublisher_app_appID
        FOREIGN KEY (appID) 
        REFERENCES app(appID) 
        ON UPDATE CASCADE,
    CONSTRAINT fk_refAppPublisher_publisher_pubID 
        FOREIGN KEY (pubID) 
        REFERENCES publisher(pubID)
        ON UPDATE CASCADE);

Thanks!

Community
  • 1
  • 1
EliteRaceElephant
  • 7,744
  • 4
  • 47
  • 62
  • 1
    hibernate Session : T get(Class entityType, Serializable id) method do not work for you ? – Thierry Jan 11 '17 at 07:26
  • @Thierry Thanks, `Session : T get(Class entityType, Serializable id)` did not help me in the end but through your comment I digged through the API a little more and found my solution. =) – EliteRaceElephant Jan 11 '17 at 14:23

1 Answers1

1

I succeeded!

The caller will always know what class or entity the operation is supposed to work on. So the trick was to give the caller more responsibility by adding ther parameters ormClass and singularAttribute.

/**
 * Select a single row from any ORM type. Returns a single result in a list
 * or an empty list. Avoids NoResultException() from JPA's getSingleResult()
 * method
 * 
 * @param key
 *            The unique primary key to select.
 * @param ormClass
 *            The ORM class to select.
 * @param singularAttribute
 *            A JPA SingularAttribute<<ormType>, <primaryKey> data
 *            type> containing the mapping to the primary key.
 * @return list The selected ORM type in a list. If no result the list is
 *         empty.
 */
public static <T, X> List<T> selectOneRow(int key, Class<T> ormClass,
        SingularAttribute<T, X> singularAttribute) {
    Session session = HibernateSessionFactory.getFactory().openSession();
    List<T> list = null;
    Transaction tx = null;

    try {
        tx = session.beginTransaction();

        CriteriaBuilder builder = session.getCriteriaBuilder();
        CriteriaQuery<T> criteria = builder.createQuery(ormClass);
        Root<T> root = criteria.from(ormClass);
        criteria.select(root);
        criteria.where(builder.equal(root.get(singularAttribute), key));

        list = session.createQuery(criteria).getResultList();

        tx.commit();
    } catch (HibernateException e) {
        if (tx != null)
            tx.rollback();
        log.error(e);
    } finally {
        session.close();
    }
    return list;
}

Note: I encountered an unexplainable NullPointerException when passing SingularAttribute as a parameter that took me a little to figure out. Here is the solution: Hibernate/JPA - NullPointerException when accessing SingularAttribute parameter

Community
  • 1
  • 1
EliteRaceElephant
  • 7,744
  • 4
  • 47
  • 62