Base class for Dao classes

370 views
Skip to first unread message

Devin Austin

unread,
May 16, 2016, 2:53:53 PM5/16/16
to jOOQ User Group
Hi all,

Currently, I'm generating my DAOs on build through maven with the jooq code generator.  This saves me a lot of work on getting the methods I need for DAOs created.  However, I have a "service" layer on top of those that I like to use for CRUD operations that calls either the appropriate DAO methods or if I need a little more granularity, dropping down into straight jooq queries.  Most of the CRUD stuff is exactly the same for each of these classes, so it would be nice to have a base service class that implements these methods and leaves service layer specific queries to be defined in their respective classes.

I'm relatively new to Java, but I've been programming for several years professionally.  I'm struggling to determine how to create such a base class with the appropriate DAO/table class types such that they can be called in a subclass without much, or any, instantiation/modification.  Here is a basic pseudo-example of what I'm talking about: https://gist.github.com/dhoss/539650094085c2ef152acab6d5884a0d

I hope what I'm saying makes sense.  Thanks in advance, and please let me know if I can provide anything else.

-Devin

Lukas Eder

unread,
May 17, 2016, 3:19:43 AM5/17/16
to jooq...@googlegroups.com
Hi Devin,

I think you should introduce parametric polymorphism here, just like org.jooq.DAO. Instead of your version, I'd write:

public abstract class BaseService<P, R extends Record> {

  protected void create(P tableObject) {
    R g = buildRecordObject(tableObject);
    g.store();
  }
  
  protected void update(P tableObject) {
    R g = buildGenericRecord(tableObject);
    g.store();
  }

  protected void delete(P tableObject) {
    R g = buildGenericRecord(tableObject);
    g.delete();
  }
  
  protected P find(String slug) {
    return dao().fetchOneBySlug(slug);
  }
  
  public List<R> list(int pageNumber) {
    pager = new Page(count());
    TABLE table = table();
    RecordMapper genericMapper = mapper();
    return sql.select()
             .from(table)
             .orderBy(table.CREATED_ON.desc())
             .limit(pageSize)
             .offset(pager.offsetFromPage(pageNumber))
             .fetch(genericMapper);
  }
  
  // these would need to be provided by the subclass
  protected abstract R buildRecordObject(P tableObject);
  
  protected abstract TABLE table();
  
  protected abstract BaseDao dao();
  
  protected abstract RecordMapper mapper();
}

But then again, why not just use the org.jooq.DAO from jOOQ, which already does most of this? Quite possibly, though, the existing DAOs are lacking features, so I'm very open to discussing potential new features that we could add to them to make your life easier.

Hope this helps,
Lukas


--
You received this message because you are subscribed to the Google Groups "jOOQ User Group" group.
To unsubscribe from this group and stop receiving emails from it, send an email to jooq-user+...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Devin Austin

unread,
May 17, 2016, 1:00:38 PM5/17/16
to jooq...@googlegroups.com
Hi Lukas,

This is exactly what I was looking for, thank you for responding.

Regarding just using org.jooq.DAO:  I am using it where I can, but perhaps my lack of JOOQ knowledge is causing me to run into some issues.  I think I ran into a few things, one being I wasn't sure how to get the query granularity for sorting, limiting and ordering that I wanted to using the DAO methods.  I haven't looked at this specifically in a while, so I could very well be missing something. 

So, my next questions are:

1. What would you recommend if I were to implement this using just org.jooq.DAO?  Again, I'll go off on my own and do some more research on the generated DAO methods, but I'd like some expert input.
2. What needs to be used in place of the TABLE object in my code example?  I can see situations where I'll want to drop straight into jooq queries and it would be helpful to be able to do that without having to duplicate code.

Thanks again!

--
You received this message because you are subscribed to a topic in the Google Groups "jOOQ User Group" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/jooq-user/KUPyMa2581g/unsubscribe.
To unsubscribe from this group and all its topics, send an email to jooq-user+...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

Lukas Eder

unread,
May 18, 2016, 3:48:15 AM5/18/16
to jooq...@googlegroups.com
Hi Devin,

Thanks for the feedback

2016-05-17 19:00 GMT+02:00 Devin Austin <devin....@gmail.com>:
Hi Lukas,

This is exactly what I was looking for, thank you for responding.

Regarding just using org.jooq.DAO:  I am using it where I can, but perhaps my lack of JOOQ knowledge is causing me to run into some issues.  I think I ran into a few things, one being I wasn't sure how to get the query granularity for sorting, limiting and ordering that I wanted to using the DAO methods.  I haven't looked at this specifically in a while, so I could very well be missing something. 

No, you aren't missing anything. Currently, there aren't a lot of possibilities for these things, out of the box.
 
So, my next questions are:

1. What would you recommend if I were to implement this using just org.jooq.DAO?  Again, I'll go off on my own and do some more research on the generated DAO methods, but I'd like some expert input.

The simplest way forward might be to extend the default DAOImpl and let the code generator use your custom DAOImpl as a base class using generator strategies:
 
One option using generator strategies would be to implement "custom code sections", where you can add your own methods to the generated DAOs:

Another option would be to extend the generated DAOs and add functionality on a per-table basis.

But of course, your suggested option is fine, too.

2. What needs to be used in place of the TABLE object in my code example?  I can see situations where I'll want to drop straight into jooq queries and it would be helpful to be able to do that without having to duplicate code.

I'm sorry, I'm not quite sure what you mean...? Could you provide an example?

Devin Austin

unread,
May 18, 2016, 12:59:23 PM5/18/16
to jooq...@googlegroups.com

The simplest way forward might be to extend the default DAOImpl and let the code generator use your custom DAOImpl as a base class using generator strategies:
 
One option using generator strategies would be to implement "custom code sections", where you can add your own methods to the generated DAOs:

Another option would be to extend the generated DAOs and add functionality on a per-table basis.

But of course, your suggested option is fine, too.


Extending the generated DAOs might not be so bad.  I'll read up on what you've sent me, thanks!

In terms of my last question, regarding the TABLE object (I'm afraid I don't know a better term for it), if you look here: https://gist.github.com/dhoss/539650094085c2ef152acab6d5884a0d#file-baseservice-java-L24, you'll see I've got pseudo code attempting to try and fulfill a generic table object for the query.  Normally, instead of TABLE, it would be a specific table object name generated by jooq, like GALLERY or IMAGE, in my case.  I'm trying to determine what the appropriate generic would be in lieu of my pseudo code.  I've dug into the generated table classes, and I've found this (apologies for the naming inconsistencies, I have some refactoring to do):

       public class Galleries extends TableImpl<GalleriesRecord> {

        private static final long serialVersionUID = 1035239683;

        /**
         * The reference instance of <code>public.galleries</code>
         */
        public static final Galleries GALLERIES = new Galleries();

        /**
         * The class holding records for this type
         */
        @Override
        public Class<GalleriesRecord> getRecordType() {
                return GalleriesRecord.class;
        } 
        // ....

However, I attempted to make the table object generic several different types shown here, but none worked.  I think I'll try your polymorphic parameter suggestion here as well, as it seems to make more sense the more I type, but I'd like your input if you have a better idea.

Again, thanks for looking at this.

-Devin

--
You received this message because you are subscribed to a topic in the Google Groups "jOOQ User Group" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/jooq-user/KUPyMa2581g/unsubscribe.
To unsubscribe from this group and all its topics, send an email to jooq-user+...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Lukas Eder

unread,
May 19, 2016, 2:27:34 AM5/19/16
to jooq...@googlegroups.com
2016-05-18 18:59 GMT+02:00 Devin Austin <devin....@gmail.com>:

The simplest way forward might be to extend the default DAOImpl and let the code generator use your custom DAOImpl as a base class using generator strategies:
 
One option using generator strategies would be to implement "custom code sections", where you can add your own methods to the generated DAOs:

Another option would be to extend the generated DAOs and add functionality on a per-table basis.

But of course, your suggested option is fine, too.


Extending the generated DAOs might not be so bad.  I'll read up on what you've sent me, thanks!

Alright. Let me know how it goes, and if you encounter any issues.

In terms of my last question, regarding the TABLE object (I'm afraid I don't know a better term for it), if you look here: https://gist.github.com/dhoss/539650094085c2ef152acab6d5884a0d#file-baseservice-java-L24, you'll see I've got pseudo code attempting to try and fulfill a generic table object for the query.  Normally, instead of TABLE, it would be a specific table object name generated by jooq, like GALLERY or IMAGE, in my case.  I'm trying to determine what the appropriate generic would be in lieu of my pseudo code.  I've dug into the generated table classes, and I've found this (apologies for the naming inconsistencies, I have some refactoring to do):

       public class Galleries extends TableImpl<GalleriesRecord> {

        private static final long serialVersionUID = 1035239683;

        /**
         * The reference instance of <code>public.galleries</code>
         */
        public static final Galleries GALLERIES = new Galleries();

        /**
         * The class holding records for this type
         */
        @Override
        public Class<GalleriesRecord> getRecordType() {
                return GalleriesRecord.class;
        } 
        // ....

However, I attempted to make the table object generic several different types shown here, but none worked.  I think I'll try your polymorphic parameter suggestion here as well, as it seems to make more sense the more I type, but I'd like your input if you have a better idea.

Aha, I see. You'd have to declare the generic type along the lines of this (simplified):

class BaseService<R extends Record, T extends Table<R>> {
    T table();
}

Then, a GalleriesService could be defined as such:

class GalleriesService extends BaseService<GalleriesRecord, Galleries> {
    
}

Hope this helps getting you in the right direction
Lukas

Devin Austin

unread,
May 19, 2016, 12:45:28 PM5/19/16
to jooq...@googlegroups.com
Okay.  Perfect.  Thanks so much.  I'll report back once I've played with this for a bit.

--
You received this message because you are subscribed to a topic in the Google Groups "jOOQ User Group" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/jooq-user/KUPyMa2581g/unsubscribe.
To unsubscribe from this group and all its topics, send an email to jooq-user+...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Devin Austin

unread,
May 25, 2016, 2:28:16 PM5/25/16
to jOOQ User Group
Hi again Lukas,

So, I've hit a bit of a wall.  I'm not able to get the POJO type correct for my mapper method, which may not be a huge deal, since there's only one column/attribute that I need to mess with to make sure it contains a valid value, but making this "just work" would be a lot easier than refactoring that logic out somewhere else.

This is the error I'm seeing:
[ERROR] /home/devin/projects/lumos-gallery/gallery/src/main/java/com/lumos/service/ImageService.java:[32,8] com.lumos.service.ImageService is not abstract and does not override abstract method mapper() in com.lumos.service.BaseService
[ERROR] /home/devin/projects/lumos-gallery/gallery/src/main/java/com/lumos/service/ImageService.java:[102,48] mapper() in com.lumos.service.ImageService cannot override mapper() in com.lumos.service.BaseService
[ERROR] return type org.jooq.RecordMapper<com.lumos.db.tables.records.ImagesRecord,com.lumos.db.tables.pojos.Images> is not compatible with org.jooq.RecordMapper<org.jooq.Record,com.lumos.db.tables.pojos.Images>
[ERROR] /home/devin/projects/lumos-gallery/gallery/src/main/java/com/lumos/service/ImageService.java:[101,3] method does not override or implement a method from a supertype
[ERROR] /home/devin/projects/lumos-gallery/gallery/src/main/java/com/lumos/service/GalleryService.java:[29,8] com.lumos.service.GalleryService is not abstract and does not override abstract method mapper() in com.lumos.service.BaseService
[ERROR] /home/devin/projects/lumos-gallery/gallery/src/main/java/com/lumos/service/GalleryService.java:[150,54] mapper() in com.lumos.service.GalleryService cannot override mapper() in com.lumos.service.BaseService
[ERROR] return type org.jooq.RecordMapper<com.lumos.db.tables.records.GalleriesRecord,com.lumos.db.tables.pojos.Galleries> is not compatible with org.jooq.RecordMapper<org.jooq.Record,com.lumos.db.tables.pojos.Galleries>
[ERROR] /home/devin/projects/lumos-gallery/gallery/src/main/java/com/lumos/service/GalleryService.java:[149,3] method does not override or implement a method from a supertype


A few things:

1. The suggestions you gave me concerning the generic types seem to be okay, or at least they're covered up by this error.
2. I've only tested this on the GalleryService class so I don't have to do a lot of back and forth on the code for multiple classes, and I've only made the bare minimum code adjustments for both the ImageService and GalleryService classes to properly subclass the BaseService class, so my apologies if there is a lot of comment clutter and redundant code at this point.
3. I thought it might be easier to just extend the DAOImpl class like you suggested, but I'm still left with figuring out how to shoe-horn in sorting and such, so I don't think that's a viable route at this point.  I would, however, be more than happy to help contribute sorting and limiting conditions if that's on the future TODO list.
4.  A slight tangent, but looking at my code I think it would be nicer to have an interface the lays out the CRUD contract, gets implemented in a CRUD base class, and then subclassed as needed.  My hope would be to make my database CRUD a good deal more encapsulated and lighter weight, and allow me to have things like a cache service that's still a service but completely unrelated to the database CRUD operations.  I'm probably just thinking out loud, but if that makes any of this simpler, please let me know your thoughts.

I am certainly not adverse to doing something else for my mapping code. I really just need to have the GALLERIES.COVER_PHOTO get set to either the value in the database or the default cover photo, which I suppose I could just set by default when inserting the record, so there's a value for that column in the database.

Either way, I would appreciate your thoughts.

Thanks,
-Devin

Lukas Eder

unread,
May 26, 2016, 2:33:20 AM5/26/16
to jooq...@googlegroups.com
Hi Devin,

2016-05-25 20:28 GMT+02:00 Devin Austin <devin....@gmail.com>:
Hi again Lukas,

So, I've hit a bit of a wall.  I'm not able to get the POJO type correct for my mapper method, which may not be a huge deal, since there's only one column/attribute that I need to mess with to make sure it contains a valid value, but making this "just work" would be a lot easier than refactoring that logic out somewhere else.

This is the error I'm seeing:
[ERROR] /home/devin/projects/lumos-gallery/gallery/src/main/java/com/lumos/service/ImageService.java:[32,8] com.lumos.service.ImageService is not abstract and does not override abstract method mapper() in com.lumos.service.BaseService
[ERROR] /home/devin/projects/lumos-gallery/gallery/src/main/java/com/lumos/service/ImageService.java:[102,48] mapper() in com.lumos.service.ImageService cannot override mapper() in com.lumos.service.BaseService
[ERROR] return type org.jooq.RecordMapper<com.lumos.db.tables.records.ImagesRecord,com.lumos.db.tables.pojos.Images> is not compatible with org.jooq.RecordMapper<org.jooq.Record,com.lumos.db.tables.pojos.Images>
[ERROR] /home/devin/projects/lumos-gallery/gallery/src/main/java/com/lumos/service/ImageService.java:[101,3] method does not override or implement a method from a supertype
[ERROR] /home/devin/projects/lumos-gallery/gallery/src/main/java/com/lumos/service/GalleryService.java:[29,8] com.lumos.service.GalleryService is not abstract and does not override abstract method mapper() in com.lumos.service.BaseService
[ERROR] /home/devin/projects/lumos-gallery/gallery/src/main/java/com/lumos/service/GalleryService.java:[150,54] mapper() in com.lumos.service.GalleryService cannot override mapper() in com.lumos.service.BaseService
[ERROR] return type org.jooq.RecordMapper<com.lumos.db.tables.records.GalleriesRecord,com.lumos.db.tables.pojos.Galleries> is not compatible with org.jooq.RecordMapper<org.jooq.Record,com.lumos.db.tables.pojos.Galleries>
[ERROR] /home/devin/projects/lumos-gallery/gallery/src/main/java/com/lumos/service/GalleryService.java:[149,3] method does not override or implement a method from a supertype


Aah, the joys of generics and covariant overriding... The fix is actually very simple:

You declared

protected abstract RecordMapper<Record, P> mapper();
 
You implemented:

protected RecordMapper<GalleriesRecord, Galleries> mapper() { ...

Those signatures are not compatible, just like List<Integer> is not a subtype of List<Number> (if you're interested about the details, you'll find lots of explanations e.g. on Stack Overflow: http://stackoverflow.com/q/30498190/521799)

Long story short, your declaration should read:

protected abstract RecordMapper<R, P> mapper();

Hope this helps. I'll reply to your remaining points later on.
Best Regards,
Lukas

Lukas Eder

unread,
May 26, 2016, 1:02:19 PM5/26/16
to jooq...@googlegroups.com
Hi Devin,

Here's the promised follow-up

2016-05-25 20:28 GMT+02:00 Devin Austin <devin....@gmail.com>:
3. I thought it might be easier to just extend the DAOImpl class like you suggested, but I'm still left with figuring out how to shoe-horn in sorting and such, so I don't think that's a viable route at this point.

You have to decide whether you want to expose the jOOQ API to consumers of your services (e.g. Field, SortField, Condition, etc.) or if you want to implement your own criteria API. From then on it's just yak shaving.
 
 I would, however, be more than happy to help contribute sorting and limiting conditions if that's on the future TODO list.

Yes it is. I cannot seem to find the issue on GitHub right now, but it might be a nice feature. The only question is the same as the one above. Should we really expose the jOOQ API?
 
4.  A slight tangent, but looking at my code I think it would be nicer to have an interface the lays out the CRUD contract, gets implemented in a CRUD base class, and then subclassed as needed.  My hope would be to make my database CRUD a good deal more encapsulated and lighter weight, and allow me to have things like a cache service that's still a service but completely unrelated to the database CRUD operations.  I'm probably just thinking out loud, but if that makes any of this simpler, please let me know your thoughts.

Thus far, caching has been out of scope for jOOQ, except perhaps for result set caching as displayed in this blog post:

I'm always open for discussion though. What specific use-cases did you have in mind?
 
I am certainly not adverse to doing something else for my mapping code. I really just need to have the GALLERIES.COVER_PHOTO get set to either the value in the database or the default cover photo, which I suppose I could just set by default when inserting the record, so there's a value for that column in the database.

How about implementing that using either a trigger, or the DDL "DEFAULT" clause?

If you want to implement that on the jOOQ layer, the RecordListener SPI might be interesting to you, given that you intend to implement a CRUD layer anyway.

Cheers,
Lukas

Devin Austin

unread,
May 26, 2016, 2:15:33 PM5/26/16
to jooq...@googlegroups.com
Hi Lukas,

I am really grateful for your attentiveness to this.  It's really helping me pull together some concepts I've struggled with for some time.

You have to decide whether you want to expose the jOOQ API to consumers of your services (e.g. Field, SortField, Condition, etc.) or if you want to implement your own criteria API. From then on it's just yak shaving.

I think at this point it's going to be easiest to sort of lump common queries into service classes and use whatever available DAO methods get generated.  I've had a lot of success in the past just dropping down closer to bare metal when features I want aren't immediately present in the current layer.

 
Yes it is. I cannot seem to find the issue on GitHub right now, but it might be a nice feature. The only question is the same as the one above. Should we really expose the jOOQ API?
 
Please do let me know when/if you find it, and I'll be happy to fork and submit PRs.  I think it would at least be nice to have the ability to either call something like .sort(COLUMN.asc()), or pass in a "configuration" object that potentially contains limit, order by and group by information, or pass in a parameter somehow.  My background is mostly ruby/perl, so I don't know how this even translates to Java but something like this would be neat: .fetchAllBySlug(:order_by => 'created_on DESC').  I realize there's not a clean way to implement anonymous maps in Java, but it conveys my point, I think.

Thus far, caching has been out of scope for jOOQ, except perhaps for result set caching as displayed in this blog post:

I'm always open for discussion though. What specific use-cases did you have in mind?

I went off on a tangent there a bit, so I don't have anything jOOQ specific in terms of caching. 

How about implementing that using either a trigger, or the DDL "DEFAULT" clause?

If you want to implement that on the jOOQ layer, the RecordListener SPI might be interesting to you, given that you intend to implement a CRUD layer anyway.

I dug through my code and I actually have the default getting set in my add() method.  The problem I'm running into is still my mapper.  Right after I submitted my question regarding the mapping error, I had actually implemented what you suggested for the generics, but it still doesn't like my method signature (up to date code: https://gist.github.com/dhoss/d40edcbfc111ad7604bc8ce0d3c9bdec): 
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.2:compile (default-compile) on project gallery: Compilation failure
[ERROR] /home/devin/projects/lumos-gallery/gallery/src/main/java/com/lumos/service/BaseService.java:[73,14] method map in interface org.jooq.Result<R> cannot be applied to given types;
[ERROR] required: org.jooq.RecordMapper<? super org.jooq.Record,E>
[ERROR] found: org.jooq.RecordMapper<R,P>
[ERROR] reason: cannot infer type-variable(s) E
[ERROR] (argument mismatch; org.jooq.RecordMapper<R,P> cannot be converted to org.jooq.RecordMapper<? super org.jooq.Record,E>)

At this point, I just need something that will turn the objects I get back from jOOQ into my POJOs.  It would be really neat if I could get the "implement your own damn mapper" per table/service working.  Previously, I had each table service passing a lambda to fetch() (pulled from here: https://github.com/lukaseder/minitwit/blob/master/src/main/java/com/minitwit/dao/MessageDao.java#L69), and that worked fine, but it doesn't lend itself very well to a base service that individual table service classes can inherit default CRUD methods from.  I've been staring at this code for too long and am making things convoluted in my head, so forgive me if I'm making this whole thing too complicated, but making the record mapper piece generic enough, and implemented in the correct spot is what's confusing me the most.  Looking through the docs, you can add one in your DSL context configuration (http://www.jooq.org/doc/3.7/manual/sql-execution/fetching/pojos-with-recordmapper-provider/), but that looks like it's still expecting a distinct class name to fetchInto().  You can also call .map() after .fetch() and pass it a record mapper (http://www.jooq.org/doc/3.7/manual/sql-execution/fetching/recordmapper/), which is what I'm trying to do, but running into the issue above.  I'm certainly open to more intelligent approaches.

By the way, I'm going to write up a tutorial on all this once it gets sorted out and I'm more than happy to contribute documentation if it fits in anywhere.

Thanks,
-Devin


--
You received this message because you are subscribed to a topic in the Google Groups "jOOQ User Group" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/jooq-user/KUPyMa2581g/unsubscribe.
To unsubscribe from this group and all its topics, send an email to jooq-user+...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Devin Austin

unread,
May 26, 2016, 3:59:19 PM5/26/16
to jooq...@googlegroups.com
Hi Lukas,

I don't know how dirty this is, but I may have found a solution.

If you look here: https://gist.github.com/dhoss/eb94f0e33138e51c478a033d9e70cc4e#file-baseservice-java-L22-L28 and here: https://gist.github.com/dhoss/eb94f0e33138e51c478a033d9e70cc4e#file-baseservice-java-L74, this mitigates the need for a mapper() method.  Some preliminary testing shows that it seems to work, so far.

I'm aware of the type safety issues, but barring any issues that come up, this seems to do the trick.  What are your thoughts?

Thanks!

Devin Austin

unread,
May 26, 2016, 4:44:16 PM5/26/16
to jooq...@googlegroups.com
Looks like I jumped the gun on this a bit, it doesn't actually work.  I'll report back if I find anything else.

Devin Austin

unread,
May 26, 2016, 5:07:16 PM5/26/16
to jOOQ User Group
Okay, sorry for the spam, but I think I found a solution:

public abstract class BaseService <R extends Record, T extends Table<R>, E>{

 // ...
  private Class<E> recordClass;


  public BaseService(DSLContext s) {
    // ...
    this.recordClass =(Class<E>) ((ParameterizedType) getClass()
                                                        .getGenericSuperclass()).getActualTypeArguments()[0];
  }

I'll continue testing this, but so far *knock on wood* things seem to be working.  I would definitely like any feedback you have though.

Thanks again,
-Devin
To unsubscribe from this group and all its topics, send an email to jooq-user+unsubscribe@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

Lukas Eder

unread,
May 27, 2016, 8:00:49 AM5/27/16
to jooq...@googlegroups.com
2016-05-26 20:15 GMT+02:00 Devin Austin <devin....@gmail.com>:
I think at this point it's going to be easiest to sort of lump common queries into service classes and use whatever available DAO methods get generated.  I've had a lot of success in the past just dropping down closer to bare metal when features I want aren't immediately present in the current layer.

That certainly makes sense
 
Yes it is. I cannot seem to find the issue on GitHub right now, but it might be a nice feature. The only question is the same as the one above. Should we really expose the jOOQ API?
 
Please do let me know when/if you find it, and I'll be happy to fork and submit PRs.  I think it would at least be nice to have the ability to either call something like .sort(COLUMN.asc()), or pass in a "configuration" object that potentially contains limit, order by and group by information, or pass in a parameter somehow.  My background is mostly ruby/perl, so I don't know how this even translates to Java but something like this would be neat: .fetchAllBySlug(:order_by => 'created_on DESC').  I realize there's not a clean way to implement anonymous maps in Java, but it conveys my point, I think.

See, at some point, I stop understanding why we still need these DAOs if we end up writing our own query language that is *not* the ordinary jOOQ API :)

It would be possible to pass lambdas to such a method, but I'm starting to feel uneasy about this - at least as long as we're talking about core code. What clients do in client code is entirely up to them, of course...
 

How about implementing that using either a trigger, or the DDL "DEFAULT" clause?

If you want to implement that on the jOOQ layer, the RecordListener SPI might be interesting to you, given that you intend to implement a CRUD layer anyway.

I dug through my code and I actually have the default getting set in my add() method.  The problem I'm running into is still my mapper.  Right after I submitted my question regarding the mapping error, I had actually implemented what you suggested for the generics, but it still doesn't like my method signature (up to date code: https://gist.github.com/dhoss/d40edcbfc111ad7604bc8ce0d3c9bdec): 
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.2:compile (default-compile) on project gallery: Compilation failure
[ERROR] /home/devin/projects/lumos-gallery/gallery/src/main/java/com/lumos/service/BaseService.java:[73,14] method map in interface org.jooq.Result<R> cannot be applied to given types;
[ERROR] required: org.jooq.RecordMapper<? super org.jooq.Record,E>
[ERROR] found: org.jooq.RecordMapper<R,P>
[ERROR] reason: cannot infer type-variable(s) E
[ERROR] (argument mismatch; org.jooq.RecordMapper<R,P> cannot be converted to org.jooq.RecordMapper<? super org.jooq.Record,E>)

That's a different error. Note the subtle difference between:

sql.select().from(table())...fetch() // Type is Result<Record>

and

sql.selectFrom(table())...fetch() // Type is Result<R>

You obviously want the latter.

At this point, I just need something that will turn the objects I get back from jOOQ into my POJOs.  It would be really neat if I could get the "implement your own damn mapper" per table/service working.

Well, jOOQ uses generics. Writing API based on jOOQ requires studying generics. Inevitably :) But I know it can be hard at times. The compiler's error messages aren't always obvious when it comes to generic type errors.
 
Previously, I had each table service passing a lambda to fetch() (pulled from here: https://github.com/lukaseder/minitwit/blob/master/src/main/java/com/minitwit/dao/MessageDao.java#L69), and that worked fine, but it doesn't lend itself very well to a base service that individual table service classes can inherit default CRUD methods from.  I've been staring at this code for too long and am making things convoluted in my head, so forgive me if I'm making this whole thing too complicated, but making the record mapper piece generic enough, and implemented in the correct spot is what's confusing me the most.  Looking through the docs, you can add one in your DSL context configuration (http://www.jooq.org/doc/3.7/manual/sql-execution/fetching/pojos-with-recordmapper-provider/), but that looks like it's still expecting a distinct class name to fetchInto().  You can also call .map() after .fetch() and pass it a record mapper (http://www.jooq.org/doc/3.7/manual/sql-execution/fetching/recordmapper/), which is what I'm trying to do, but running into the issue above.  I'm certainly open to more intelligent approaches.

By the way, I'm going to write up a tutorial on all this once it gets sorted out and I'm more than happy to contribute documentation if it fits in anywhere.

That would be wonderful! I'd love to link to it from here: http://www.jooq.org/community

Lukas Eder

unread,
May 27, 2016, 8:03:01 AM5/27/16
to jooq...@googlegroups.com
That's a nice idea. With your current design, this will work. But beware, you're entering a minefield here. The Java compiler erases (most) generic type information. This works for very specific reasons (i.e. your subclass terminally binding the generic types R, T, and E to the respective concrete types). There are other situations, where you cannot discover the "actual type arguments" through reflection.

I usually prefer to pass around explicit class literals, such as GalleryRecord.class.

Hope this helps,
Lukas

To unsubscribe from this group and all its topics, send an email to jooq-user+...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to the Google Groups "jOOQ User Group" group.
To unsubscribe from this group and stop receiving emails from it, send an email to jooq-user+...@googlegroups.com.

Devin Austin

unread,
May 28, 2016, 1:02:21 AM5/28/16
to jooq...@googlegroups.com

I agree, explicit class literals would be much nicer, but I'm not able to picture how that gets passed around. Do you have an example to share?

Devin Austin

unread,
May 28, 2016, 1:09:36 AM5/28/16
to jooq...@googlegroups.com

Now that you put it that way re: "why use daos when it gets redefined substantially anyway," then yea it doesn't make much sense. I actually am only using the Dao methods in one or two spots and that's out of pure laziness. If one wants a DSL for Dao querying, then it makes sense for them to build it themselves.  I think it takes things in too far off in a different direction than what jOOQ aims to initially provide trying to support it.

I've responded to your other email, and after I meditate on your response for a bit, I'll draft up an article and post it with your blessing, and you can obviously link to it.

Thanks again!

--

Lukas Eder

unread,
May 28, 2016, 3:12:15 AM5/28/16
to jooq...@googlegroups.com
2016-05-28 7:02 GMT+02:00 Devin Austin <devin....@gmail.com>:

I agree, explicit class literals would be much nicer, but I'm not able to picture how that gets passed around. Do you have an example to share?

Instead of the base type relying on the sub type's assumed usage of generics via reflection, you could add a constructor like:

BaseService(Class<R> recordType, Class<P> pojoType) {...}

By removing the default (no args) constructor, you'll force all subtypes to make explicit decisions about what class literal to pass when calling the super constructor. Essentially, that's also what DAOImpl does, at least for the pojoType, the recordType is also available from Table.getRecordType()

Hope this helps,
Lukas

Lukas Eder

unread,
May 28, 2016, 3:17:01 AM5/28/16
to jooq...@googlegroups.com
2016-05-28 7:09 GMT+02:00 Devin Austin <devin....@gmail.com>:

Now that you put it that way re: "why use daos when it gets redefined substantially anyway," then yea it doesn't make much sense. I actually am only using the Dao methods in one or two spots and that's out of pure laziness. If one wants a DSL for Dao querying, then it makes sense for them to build it themselves.  I think it takes things in too far off in a different direction than what jOOQ aims to initially provide trying to support it.

To be fair, the fact that we actually have DAOs might be the source of confusion here. The main reason why we once introduced them was to compete with a (meanwhile in maintenance mode) competitor product and with Spring's repository "pattern". They were so easy to implement, seemed like a low hanging fruit.

The more users were using DAOs, the less it became obvious where DAOs should really go. The problem for jOOQ here is that other than DAOs, jOOQ is extremely open to and unopinionated about all sorts of application designs (transaction models, caching, etc.) But DAOs make strong assumptions about client application design, and possibly the wrong ones, so it's hard to get them "right".

I've responded to your other email, and after I meditate on your response for a bit, I'll draft up an article and post it with your blessing, and you can obviously link to it.

You don't need my blessing, we're not censoring anything here :) It would be very interesting to read it in the very way you experienced your jOOQ journey.

Best Regards,
Lukas
Reply all
Reply to author
Forward
0 new messages