Wednesday, May 8, 2024
HomeProgrammingThe Many Totally different Methods to Fetch Knowledge in jOOQ – Java,...

The Many Totally different Methods to Fetch Knowledge in jOOQ – Java, SQL and jOOQ.


The jOOQ API is all about comfort, and as such, an essential operation (a very powerful one?) like fetch() should include comfort, too. The default method to fetch knowledge is that this:

End result<Record1<String>> end result =
ctx.choose(BOOK.TITLE)
   .from(BOOK)
   .fetch();

for (Record1<String> file : end result) {
    // ...
}

It fetches all the end result set into reminiscence and closes the underlying JDBC sources eagerly. However what different choices do we’ve got?

Iterable fetching

Within the above instance, the fetch() name wasn’t strictly crucial. jOOQ’s ResultQuery<R> sort conveniently extends Iterable<R>, which signifies that a name to ResultQuery.iterator() may even execute the question. This may be performed primarily in two methods:

Exterior iteration:

for (Record1<String> file : ctx
    .choose(BOOK.TITLE)
    .from(BOOK)
) {
    // ...
}

That is notably good as a result of it feels similar to PL/SQL or PL/pgSQL’s FOR loop for implicit cursors:

FOR rec IN (SELECT guide.title FROM guide) LOOP
  -- ...
END LOOP;

This nonetheless has to fetch all the end result set into reminiscence, although, as a result of there isn’t a for-with-resources syntax in Java that mixes the foreach syntax with a try-with-resources syntax.

Inside iteration:

The JDK 8 added Iterable::forEach, which jOOQ’s ResultQuery inherits, so you are able to do this simply as effectively:

ctx.choose(BOOK.TITLE)
   .from(BOOK)
   .forEach(file -> {
       // ...
   });

The 2 are completely equal.

Single file fetching

For those who’re positive you’re going to fetch solely a single worth, no have to materialise a listing. Simply use one of many following strategies. Given this question:

ResultQuery<Record1<String>> question = ctx
    .choose(BOOK.TITLE)
    .from(BOOK)
    .the place(BOOK.ID.eq(1));

Now you can:

Fetch a nullable file:

This fetches a nullable file, i.e. if the file hasn’t been discovered, null is produced. If there are multiple data, a TooManyRowsException is thrown.

Record1<String> r = question.fetchOne();

Fetch an non-obligatory file:

The null bikeshed is actual, so why hold you from bikeshedding additionally when working with jOOQ? Precisely equal to the above, however utilizing a unique type, is that this:

Non-obligatory<Record1<String>> r = question.fetchOptional();

Fetch a single file:

If you understand your question produces precisely one file, there’s the time period “single” in jOOQ’s API which suggests precisely one:

Record1<String> r = question.fetchSingle();
println(r.toString()); // NPE secure!

The r.toString() name is NullPointerException secure, as a result of if the file didn’t exist a NoDataFoundException would have been thrown.

Resourceful fetching

The default is to eagerly fetch every part into reminiscence, as that’s possible extra helpful to most functions than JDBC’s default of managing sources on a regular basis (together with nested collections, lobs, and so on.). As could possibly be seen within the above Iterator fetching instance, it’s usually the one attainable strategy that doesn’t produce unintended useful resource leaks, on condition that customers can’t even entry the useful resource (by default) through jOOQ.

Nevertheless it isn’t at all times the appropriate selection, so you’ll be able to alternatively hold open underlying JDBC sources whereas fetching knowledge, in case your knowledge set is giant. There are 2 fundamental methods:

Crucial:

By calling ResultQuery.fetchLazy(), you’re making a Cursor<R>, which wraps the underlying JDBC ResultSet, and thus, needs to be contained in a try-with-resources assertion:

strive (Cursor<Record1<String>> cursor = ctx
    .choose(BOOK.TITLE)
    .from(BOOK)
    .fetchLazy()
) {
    for (Record1<String> file : cursor) {
        // ...
    }
}

The Cursor<R> nonetheless extends Iterable<R>, however you’ll be able to fetch data additionally manually from it, e.g.

File file;

whereas ((file = cursor.fetchNext()) != null) {
    // ...
}

Useful:

If the Stream API is extra such as you wish to work with knowledge, simply name ResultQuery.fetchStream() as a substitute, then (however don’t neglect to wrap that in try-with-resources, too!):

strive (Stream<Record1<String>> stream = ctx
    .choose(BOOK.TITLE)
    .from(BOOK)
    .fetchStream()
) {
    stream.forEach(file -> {
        // ...
    });
}

Or, use Stream::map, Stream::scale back, or no matter. Regrettably, the Stream API isn’t auto-closing. Whereas it might have been attainable to implement the API this manner, its “escape hatches,” like Stream.iterator() would nonetheless stop auto-closing behaviour (at the least except many extra options had been launched, comparable to e.g. an AutoCloseableIterator, or no matter).

So, you’ll have to interrupt your fluent pipeline with the try-with-resources assertion.

Useful, however not resourceful

After all, you’ll be able to at all times name fetch() first, then stream later, with the intention to stream the information out of your reminiscence instantly. If resourcefulness isn’t essential (i.e. the efficiency influence is negligible as a result of the end result set isn’t massive), you’ll be able to write this:

ctx.choose(BOOK.TITLE)
   .from(BOOK)
   .fetch()
   .stream()
   .forEach(file -> {
       // ...
   });

Or use Stream::map, Stream::scale back, or no matter

Collector fetching

Beginning with jOOQ 3.11, each ResultQuery::gather and Cursor::gather had been added. The JDK Collector API is extraordinarily poweful. It doesn’t get the eye it deserves (outdoors of the Stream API). In my view, there needs to be an Iterable::gather methodology, as it might make sense to re-use Collector sorts on any assortment, e.g.

Set<String> s = Set.of(1, 2, 3);
Listing<String> l = s.gather(Collectors.toList());

Why not? Collector is sort of a twin to the Stream API itself. The operations aren’t composed in a pipelined syntax, however in a nested syntax. Aside from that, to me at the least, it feels fairly comparable.

In case of jOOQ, they’re very highly effective. jOOQ provides just a few helpful out-of-the-box collectors in Data. Let me showcase Data.intoMap(), which has this overload, for instance:

<Okay,V,R extends Record2<Okay,V>> Collector<R,?,Map<Okay,V>> intoMap()

The fascinating bit right here is that it captures the varieties of a Record2 sort as the important thing and worth sort of the ensuing map. A easy generic trick to verify it really works provided that you venture precisely 2 columns, for instance:

Map<Integer, String> books =
ctx.choose(BOOK.ID, BOOK.TITLE)
   .from(BOOK)
   .gather(Data.intoMap());

That is utterly sort secure. You possibly can’t venture 3 columns, or the mistaken column sorts due to all these generics. That is extra handy than the equal that’s out there on the ResultQuery API instantly, the place it’s important to repeat the projected column expressions:

Map<Integer, String> books =
ctx.choose(BOOK.ID, BOOK.TITLE)
   .from(BOOK)
   .fetchMap(BOOK.ID, BOOK.TITLE);

With the ResultQuery::gather and Cursor::gather APIs, you need to use any arbitrary collector, together with your personal, which is actually very highly effective! Additionally, it removes the necessity for the middleman End result knowledge construction, so it doesn’t need to fetch every part into reminiscence (except your Collector does it anyway, after all).

Collectors are notably helpful when gathering MULTISET nested collections. An instance has been given right here, the place a nested assortment was additionally mapped into such a Map<Okay, V>.

Reactive fetching

Ranging from jOOQ 3.15, R2DBC has been supported. Because of this ResultQuery<R> is now additionally a reactive streams Writer<R> (each the reactive-streams API and the JDK 9 Circulation API are supported for higher interoperability).

So, simply choose your favorite reactive streams API of selection, e.g. reactor, and stream jOOQ end result units reactively like this:

Flux<Record1<String>> flux = Flux.from(ctx
    .choose(BOOK.TITLE)
    .from(BOOK)
);

Many fetching

Final however not least, there are uncommon circumstances when your question produces multiple end result set. This was once fairly en vogue in SQL Server and associated RDBMS, the place saved procedures may produce cursors. MySQL and Oracle even have the characteristic. For instance:

Outcomes outcomes = ctx.fetch("sp_help");

for (End result<?> end result : outcomes) {
    for (File file : end result) {
        // ...
    }
}

The usual foreach loop will solely iterate outcomes, however you can too entry the interleaved row counts utilizing Outcomes.resultsOrRows() if that’s of curiosity to you as effectively.

Conclusion

Comfort and developer person expertise is on the core of jOOQ’s API design. Like all good assortment API, jOOQ provides a wide range of composable primitives that permit for extra successfully integrating SQL into your utility.

SQL is only a description of an information construction. jOOQ helps describe that knowledge construction in a sort secure means on the JVM. It’s pure for additional processing to be attainable in an equally sort secure means, as we’re used to from the JDK’s personal assortment APIs, or third events like jOOλ, vavr, streamex, and so on.

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Most Popular

Recent Comments