'Hibernate: How to generically search for a DB record that matches a given POJO in all fields except ID? (NOTE: Search fields are dynamic at runtime!)

Does hibernate have a simple straight-forward way to search for a record that matches a given POJO in all fields except the ID?

I need an approach that does not require me to hard-code field names, because this method will be used by multiple tables/entities and POJO types.

In essence I have a generic Dao for a Pojo abstract class:

    // each Entity/POJO class is a subtype of this, with additional fields
@MappedSuperclass
public abstract class Pojo {
    @Id
    @GeneratedValue(strategy= GenerationType.AUTO, generator="native")
    @GenericGenerator(name = "native",strategy = "native")
    @JsonIgnore
    private int id;

    public int getId() {
        return this.id;
    }

    public void setId(int id) {
        this.id = id;
    }
}

public class Dao<POJO extends Pojo> {
    private final Class<POJO> pojoClass;

    public Dao(final Class<POJO> pojoClass) {
        this.pojoClass = pojoClass;
    }

    private Session getSession() {
        return sessionFactory.openSession();
    }

    // ... standard CRUD methods go here ...

    /*  IF I have a map of all of the property names and values, I can dynamically generate a search...
     *  ...but how would I get the field names and getters from Hibernate?
     *  And more importantly... shouldn't there be a way to just pass the POJO to hibernate
     *  and tell it to find a matching a record in the appropriate entity class?
     */
    public List<POJO> findByPropertyEqual(Map<String, Object> propertyMap) {
        Session session = getSession();
        CriteriaBuilder builder = session.getCriteriaBuilder();
        CriteriaQuery<POJO> query = builder.createQuery(pojoClass);
        Root<POJO> root = query.from(pojoClass);
        List<Predicate> predicates = new ArrayList<Predicate>();
        for (Map.Entry entry: propertyMap.entrySet()) {
            predicates.add(builder.equal(root.get((String) entry.getKey()), entry.getValue()));
        }
        query.select(root).where(builder.and(predicates.toArray(new Predicate[predicates.size()])));

        List<POJO> list = session.createQuery(query).getResultList();
        session.close();
        return list;
    }
}

Reason I need this: As part of deserialization for nested JSON objects, I need to find if there is already a record which matches the given record, and use it instead of creating another identical record. In some cases these objects have an id actually assigned by the API -- in those cases I override the @JsonIgnore, and can fetch from Hibernate by Id... easy -- BUT in other cases it is just a generated ID only used in my local back-end, and I have to search for the record by its actual data contents.

For example:

{
  "name": "Outer JSON object",
  "someData": 12345,
  "innerObjects": [
    {
      "field1": "value1",
      "field2": "value2",
      "etc": "find me, I'm already in the DB!"
    },
    {
      "field1": "a different value1",
      "field2": "value2"
      "etc": "find me, I'm already in the DB!"
    }
  ]
}

There are many different types of "innerObjects", each of which will be represented by a concrete implementation of the Pojo abstract class. These inner objects are consistent in terms of which data structure is returned with which field of the outer object. For some fields they are presented as an array of objects, sometimes as single objects, but always in a consistent manner per-field. However, there are so many of these different data types that it would be incredibly tedious to hard-code a separate Dao for each of these inner classes, in addition to the Pojo class I've already had to create and annotate, and SQL 'CREATE TABLE' statement that has to be written to match it.

I'm honestly a little surprised Hibernate does not have a straight-forward solution to this. I've been trying to read through the Hibernate Search documentation, but it is daunting and seems very little of it actually applies to my situation. I'm thinking I may have to resort to some kind of reflection to create a search map, but I'd need to fetch the names of the applicable properties out of Hibernate to begin with, and was hoping it would also provide the getter method in that case.

What's the right way to handle this situation?



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source