I have posted this comment last Sunday but it looks like it went astray - 
here is the repost.

Hi Lucas,

I have been watching jOOQ for a while and have to say that you are doing a 
fantastic job! 
(...have you got a day time job as well?)

Couple of months back I decided to use jOOQ (for all the obvious reasons) 
in my project. I am now at the 
point when I have got a decent piece of functionality implemented and 
started to write some high level tests 
for my use case. Each tests runs with a simple DYI (do it yourself) 
profiler that gives me time breakdown 
for the relevant methods within the execution stack.

During these tests I have first noticed that it takes disproportionally 
long time to construct the Factory class. 
To give you an example I have a test case that inserts 12 records for 7 
different entity types (in other words 
12 records into 7 tables). The whole test runs 266 ms and out of this 140 
ms is spent to construct the Factory
(with a JDBC connection and H2 Dialect in my case). After checking the 
source code I did not find anything there 
that should take so long so I added a couple of traces there and run it 
through the debugger. I have  noticed 
that after the initial Factory constructor call there are additional 15 
calls as well. These calls come from 
the default static initialiser that is run for each database dialect. 

    static {
        for (SQLDialect dialect : SQLDialect.values()) {
            Factory.DEFAULT_INSTANCES[dialect.ordinal()] = new 
Factory(dialect);
        }
    }

All in all one could say that this is not a big deal as you construct the 
factory once (and incur this cost only once) but 
since the Factory is constructed with the requested dialect it seems a bit 
pointless to have this done for all available dialects.


The side effect/product of the added traces in the Factory constructor 
brought up another issue. The Factory constructor 
is called multiple times after the initial instance of the Factory is 
created. In this specific test case there are additional 110 
calls. The story starts with the construction of the insert statement - in 
my case one of them looks like this:

        return ftry
            .insertInto(CATEGORY, 
                        CATEGORY.ID, 
                        CATEGORY.USR, 
                        CATEGORY.VER, 
                        CATEGORY.TYPE, 
                        CATEGORY.NAME)
            .values(ent.getId(), 
                      ent.getUser(), 
                    newVer,
                    ent.getEnum(Property.CategoryType),
                    ent.getString(Property.Name));

Which eventually leads to adding key/value pairs to a linked hash map that 
calls hashCode() on TableFiedlImp class
that is implemented in the AbstractQueryPart class:
    
    @Override
    public int hashCode() {
        // This is a working default implementation. It should be 
overridden by
        // concrete subclasses, to improve performance
        return create().renderInlined(this).hashCode();
    }
 
Seems like something got forgotten here as this default implementation 
calls: 

    protected final Factory create() {
        return create(getConfiguration());
    }

On the Factory class which then calls:

    protected final Factory create(Configuration configuration) {
        return Factory.getNewFactory(configuration);
    }

And then the constructor:
    
    @SuppressWarnings("deprecation")
    final static Factory getNewFactory(Configuration configuration) {
        if (configuration == null) {
            return 
getNewFactory(DefaultConfiguration.DEFAULT_CONFIGURATION);
        }
        else {
            return new Factory(
                configuration.getDataSource(),
                configuration.getConnection(),
                configuration.getDialect(),
                configuration.getSettings(),
                configuration.getSchemaMapping(),
                configuration.getData());
        }
    }

 
 The bottom line is that the constructor is called for each column of each 
record that is constructed. 
 (see the attached snapshot for the whole stack trace). I have not profiled 
any updates as yet but it 
 seems likely that the behaviour will be the same as they most likely reuse 
the same functionality. 
 
 Regards,
 Adam
 
 PS
 It is a great shame that there is not a simple JUnit profiler add-on 
available which would show these 
 kinds of issue straight away - any takers?.

 


Reply via email to