org.jdbi.v3.sqlobject.package-info Maven / Gradle / Ivy
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* SQL Objects
*
* The SQLObject API allows for declarative definition of interfaces which will handle
* the generation of statements and queries on your behalf when needed. Take the following interface:
*
*
* public interface TheBasics {
* @SqlUpdate("insert into something (id, name) values (:id, :name)")
* int insert(@BindBean Something something);
* @SqlQuery("select id, name from something where id = :id")
* Something findById(@Bind("id") long id);
* }
*
*
* First, install the SQL Object plugin:
*
*
* Jdbi jdbi = Jdbi.create(dataSource);
* jdbi.installPlugin(new SqlObjectPlugin());
*
*
* You can obtain an instance of TheBasics
via one of three means.
*
*
* -
*
* You can pass a lambda to Jdbi. A short-lived instance of the interface will be created, and passed to the
* lambda. The lambda can make any number of calls to that instance before returning. The lifecycle of
* the SQL Object instance ends when the lambda returns.
*
*
* jdbi.useExtension(TheBasics.class, theBasics -> theBasics.insert(new Something(1, "Alice")));
* Something result = jdbi.withExtension(TheBasics.class, theBasics -> theBasics.findById(1));
*
*
* withExtension
returns the value returned by the lambda, whereas useExtension
* has a void return.
*
*
* -
*
* You can get an instance attached to a particular handle. The SQL Object's lifecycle ends when the
* handle is closed.
*
*
* try (Handle handle = jdbi.open()) {
* TheBasics attached = handle.attach(TheBasics.class);
* attached.insert(new Something(2, "Bob");
* Something result = attached.findById(2);
* }
*
*
* -
*
* Finally, you can request an on-demand instance. On-demand instances have an open-ended lifecycle, as they
* obtain and releases connections for each method call. They are thread-safe, and may be reused across an
* application. This is handy when you only need to make single calls at a time.
*
*
* TheBasics onDemand = jdbi.onDemand(TheBasics.class);
* onDemand.insert(new Something(3, "Chris"));
* Something result = onDemand.findById(3);
*
*
* There is a performance penalty every time a connection is allocated and released. If you need to make
* multiple calls in a row to a SQL Object, consider using one of the above options for
* better performance, instead of on-demand.
*
*
*
* Default Methods
*
* You can declare default methods on your interface, which can call other methods of the interface:
*
*
* public interface DefaultMethods {
* @SqlQuery("select * from folders where id = :id")
* Folder getFolderById(int id);
* @SqlQuery("select * from documents where folder_id = :folderId")
* List<Document> getDocuments(int folderId);
* default Node getFolderByIdWithDocuments(int id) {
* Node result = getById(id);
* result.setChildren(listByParendId(id));
* return result;
* }
* }
*
* Mixin Interfaces
*
* All SQL objects implicitly implement the SqlObject
interface (whether you declare it or not), which
* provides a getHandle()
method. This is handy when you need to "drop down" to the core API for
* scenarios not directly supported by SQL Object:
*
*
* public interface UsingMixins extends SqlObject {
* @RegisterBeanMapper(value={Folder.class, Document.class}, prefix={"f", "d"})
* default Folder getFolder(int id) {
* return getHandle().createQuery(
* "select f.id f_id, f.name f_name, " +
* "d.id d_id, d.name d_name, d.contents d_contents " +
* "from folders f left join documents d " +
* "on f.id = d.folder_id " +
* "where f.id = :folderId")
* .bind("folderId", id)
* .reduceRows(Optional.<Folder>empty(), (folder, row) -> {
* Folder f = folder.orElseGet(() -> row.getRow(Folder.class));
* if (row.getColumn("d_id", Integer.class) != null) {
* f.getDocuments().add(row.getRow(Document.class));
* }
* return Optional.of(f);
* });
* }
* }
*
*
* Any interface that extends SqlObject
can be used as a SQL Object mixin, provided all of its methods
* have a SQL method annotation (e.g. @SqlQuery
, or are interface default methods.
*
* Transactions
*
* Any SQL Object method annotation with @Transaction
will be executed within a transaction.
* This is most commonly used on interface default methods to roll up multiple method calls:
*
*
* public interface TransactionAnnotation {
* @SqlUpdate("insert into folders (id, name) values (:id, :name)")
* void insertFolder(@BindBean Folder folder)
* @SqlBatch("insert into documents (id, folder_id, name, content) " +
* "values (:id, :folderId, :name, :content)")
* void insertDocuments(int folderId, @BindBean List<Document> documents);
* @Transaction
* default void insertFolderWithDocuments(Folder folder) {
* insertFolder(folder);
* insertDocuments(folder.getId(), folder.getDocuments());
* }
* }
*
*
* Jdbi also provides a Transactional
mixin interface. When a SQL Object type extends this interface,
* callers may invoke method from that interface to manage transactions:
*
*
* public interface TransactionalWithDefaultMethods extends Transactional {
* @SqlUpdate("insert into folders (id, name) values (:id, :name)")
* void insertFolder(@BindBean Folder folder)
* @SqlBatch("insert into documents (id, folder_id, name, content) " +
* "values (:id, :folderId, :name, :content)")
* void insertDocuments(int folderId, @BindBean List<Document> documents);
* }
*
*
* Thus:
*
*
* TransactionalWithDefaultMethods dao = jdbi.onDemand(TransactionalWithDefaultMethods.class);
* dao.inTransaction(self -> {
* self.insert(folder);
* self.insertDocuments(folder.getId(), folder.getDocuments());
* });
*
*
*
* Note: use caution combining Transactional
with on-demand SQL Objects.
*
* The only methods considered safe to call with on-demand SQL Objects are inTransaction
or
* useTransaction
.
*
*/
package org.jdbi.v3.sqlobject;