javahibernateplayframeworkjooq

How to map to an existing Hibernate model using jOOQ fetchInto()?


I'm trying to use the jOOQ fetchInto() method to map to an existing Hibernate model Organization (class and its inheritances are below).

Organization organization = jooq().select().from(ORGANIZATION).fetchOne().into(Organization.class);

The problem I have is that I can't really understand what happens in DefaultRecordMapper as I feel I'm not entirely familiar with all the terms that are used. I'm trying to figure out how it applies to the Hibernate classes that are in my codebase.

So far what I've tried:

What works:

What doesn't work:

My question is: Is there something I am overlooking with the mapping and what are the things I should look at concerning the classes, fields, constructors and annotations with Hibernate models? I want to eventually map all the Hibernate models in the codebase and use fetchInto to do that.

Thanks! :)

@Entity
public class Organization extends BaseModel {
  @Required public String name;

  //... a lot of other code
}
@MappedSuperclass
public class BaseModel extends Model {
  /** The datetime this entity was first saved. Automatically set by a JPA prePersist */
  @NoBinding
  @Column
  @Type(type = "org.jadira.usertype.dateandtime.joda.PersistentDateTime")
  public DateTime createdAt;

  /** The datetime this entity was last modified. Automatically set by a JPA preUpdate */
  @NoBinding
  @Column
  @Type(type = "org.jadira.usertype.dateandtime.joda.PersistentDateTime")
  public DateTime modifiedAt;

  //...
}
@MappedSuperclass
public class Model extends GenericModel { // Both Model and GenericModel are from the Play Framework
    @Id
    @GeneratedValue
    public Long id;

    public Model() {
    }

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

    public Object _key() {
        return this.getId();
    }
}

Solution

  • jOOQ doesn't support all the many JPA and Hibernate specific annotations. Historically, it supported a few JPA annotations (because why not), but full interop would be excessive and investing product development time in the wrong places. jOOQ is by no means a JPA implementation.

    As of jOOQ 3.20, the JPA annotation mapping functionality has been extracted to a separate module, in parts to stress the fact that future jOOQ versions will not deepen their integration with these annotations:

    So, you shouldn't have too high expectations about annotations to "work"

    Step 0: Why didn't (some) of the mappings work?

    As mentioned before, not all JPA specification is implemented. For example, a known difference is that @Column annotations are mandatory in jOOQ: https://github.com/jOOQ/jOOQ/issues/4586. This will not change!

    Even more so, things like @MappedSuperclass or @Type are never going to be supported by jOOQ.

    Step 1: Do you really need it?

    You've decided to create and run your query with jOOQ. I imagine your actual query is much more complex than what you're showing, because for that particular query, you don't need jOOQ.

    Do you really need to map to Hibernate entities? Because even when you use Hibernate, the recommended approach is to use entities only when you're going to modify them and store the delta back to the database. If that's the case, see step 2 below. If it's not the case, why not use jOOQ's own mapping functionality to work with any style of jOOQ supported POJO?

    In particular, starting from jOOQ 3.15, the MULTISET and ROW operators along with ad-hoc conversion have changed quite a bit how people interact with jOOQ and SQL as shown in this blog post. These approaches seem much more safe and convenient than the automatic mapping using reflection (with or without JPA annotations).

    Step 2: Use Hibernate to execute the jOOQ query

    If you're using jOOQ only to build a rather complex SQL query and you need Hibernate entities as a result, then use Hibernate to execute the jOOQ query as documented here. A small utility should be enough:

    public static <E> List<E> nativeQuery(EntityManager em, org.jooq.Query query, Class<E> type) {
        Query result = em.createNativeQuery(query.getSQL(), type);
    
        List<Object> values = query.getBindValues();
        for (int i = 0; i < values.size(); i++)
            result.setParameter(i + 1, values.get(i));
    
        return result.getResultList();
    }
    

    Though, be aware that you'll be missing out on a ton of jOOQ functionality this way.