Evaluating JDBI. Why use `Closeable` on objects that are returned to the user that are not supposed to be closed?
See original GitHub issueSo, after just 30 minutes of evaluating JDBI, I ran into this issue that I think will deter me from using it.
I want to do the following:
jdbi.useTransaction(handle -> {
handle.createUpdate("INSERT INTO xyz (data) VALUES (:data)")
.bind("data", data)
.execute();
});
Now, createUpdate
returns a Closeable
. My question is, why would it do this when there is no need to create the actual closeable resource until one of execute
or one
is called? You are basically giving me a simple “binder” type object that allows me to bind things to a simple String
, but it seems this object that is returned is already an “expensive” resource (perhaps there is even an actual prepared statement created already) that will require closing. Why not defer creating the prepared statement until it is actually required?
Now the user is getting a warning on createUpdate
that this resource needs closing, and IMHO these warnings should not be ignored without good reason. Note that in this scenario, the user never needs to close anything, so why return something that is Closeable
in the first place?
I would expect the implementation of createUpdate
to be something like:
class Handle {
UpdateBinder createUpdate(String sql) {
return new Binder(this, sql); // this = Handle
}
}
class UpdateBinder { // not Closeable, why would it be?
UpdateBinder (Handle handle, String sql) {
...
}
... bind methods ...
int execute() {
// - use handle to create prepared statement -- use try with resources on that
// - bind everything
// - execute
// - close
}
}
No Closeable
is ever returned to the user.
Issue Analytics
- State:
- Created a year ago
- Reactions:1
- Comments:13 (8 by maintainers)
I am planning to add documentation that describes this resource management requirement and make sure that JDBI users are more aware.
The good (?) news is that most people use the standard semantics of
handle.create<something>.bind(...).map(...).one()/list()/any method from ResultIterable that is not stream()/iterator()
and that stanza is managing the resources for the user.The only exception are
stream()
anditerator()
because they maintain state within the iterator/stream.I agree that implementing
(Auto)Closeable
on all statements because of this one use case may be overkill. However, we many need to stress that whenever an iterator or a stream is used with JDBI, they must be wrapped in try-with-resources (or manually closed), because otherwise JDBI will leak resources.This may be especially critical for streams where a map or filter function throws an exception.
This is Eclipse, with the warnings “Resource Leak” and “Potential Resource Leak” activated (under Window > Preferences > Java > Compiler > Error/Warnings > Potential Programming Problems).
IntelliJ may have something similar under “Resource Management” (AutoCloseable used without try with resources).
If you return something that is
AutoCloseable
anywhere to user space, and its not closed by the user, then its considered a leak – it’s not possible for the IDE to analyze if it was closed by another follow up call.I think that returning
AutoCloseable
indicates that it should be used in try with resources (if at all possible of course). If this code makes no sense:…then the closeable is returned at the wrong place. I indeed think it makes no sense here, as there is nothing that should be closed in this case.
It’s not strictly a leak though (although not even IntelliJ will be able to figure that out), more that a closeable is returned at a point where there isn’t actually anything that needs closing yet. Eclipse flags it as a “potential” leak, but in general, that’s still a very good warning (it never occurs in “regular” code aside from the odd unit test, and I always have this on). IDE’s will flag it as something that needs closing, but if there was nothing that needed closing then the returned object is incorrect to implement closeable.