/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
package org.jooby.jdbi;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Provider;
import org.jooby.Env;
import org.jooby.Env.ServiceKey;
import org.jooby.jdbc.Jdbc;
import org.skife.jdbi.v2.DBI;
import org.skife.jdbi.v2.ExpandedStmtRewriter;
import org.skife.jdbi.v2.Handle;
import org.skife.jdbi.v2.IterableArgumentFactory;
import org.skife.jdbi.v2.OptionalArgumentFactory;
import org.skife.jdbi.v2.OptionalContainerFactory;
import org.skife.jdbi.v2.logging.SLF4JLog;
import org.skife.jdbi.v2.tweak.ArgumentFactory;
import org.skife.jdbi.v2.tweak.ConnectionFactory;
import com.google.common.collect.Lists;
import com.google.inject.Binder;
import com.typesafe.config.Config;
/**
* Exposes {@link DBI}, {@link Handle} and SQL Objects (a.k.a DAO).
* This module extends the {@link Jdbc} module so all the services provided by the {@link Jdbc}
* module are inherited.
*
* <p>
* Before start, make sure you already setup a database connection as described in the {@link Jdbc}
* module.
* </p>
*
* <h1>usage</h1>
*
* <p>
* It is pretty straightforward:
* </p>
*
* <pre>
* {
* use(new Jdbi());
*
* get("/", req {@literal ->} {
* DBI dbi = req.require(DBI.class);
* // ... work with dbi
* });
*
* get("/handle", req {@literal ->} {
* try (Handle handle = req.require(Handle.class)) {
* // ... work with dbi handle
* }
* });
* }
* </pre>
* <p>
* A call to <code>req.require(Handle.class)</code> is the same as:
* <code>req.require(DBI.class).open(Handle.class)</code>.
* </p>
*
* <h1>sql objects</h1>
*
* <p>
* It is pretty straightforward (too):
* </p>
*
* <pre>
*
* public interface MyRepository extends Closeable {
* @SqlUpdate("create table something (id int primary key, name varchar(100))")
* void createSomethingTable();
*
* @SqlUpdate("insert into something (id, name) values (:id, :name)")
* void insert(@ind("id") int id, @Bind("name") String name);
*
* @SqlQuery("select name from something where id = :id")
* String findNameById(@Bind("id") int id);
* }
*
* ...
* {
* use(new Jdbi());
*
* get("/handle", req {@literal ->} {
* try (MyRepository h = req.require(MyRepository.class)) {
* h.createSomethingTable();
*
* h.insert(1, "Jooby");
*
* String name = h.findNameById(1);
*
* return name;
* }
* });
* }
* </pre>
*
* <h1>configuration</h1>
* <p>
* If you need to configure and/or customize a {@link DBI} instance, just do:
* </p>
*
* <pre>
* {
* use(new Jdbi().doWith((dbi, config) {@literal ->} {
* // set custom option
* }));
* }
* </pre>
*
* <p>
* That's all folks! Enjoy it!!!
* </p>
*
* @author edgar
* @since 0.5.0
*/
public class Jdbi extends Jdbc {
public static final String ARG_FACTORIES = "__argumentFactories_";
static class DBI2 extends DBI {
private List<ArgumentFactory<?>> factories = new ArrayList<ArgumentFactory<?>>();
public DBI2(final ConnectionFactory connectionFactory) {
super(connectionFactory);
define(ARG_FACTORIES, factories);
}
@Override
public void registerArgumentFactory(final ArgumentFactory<?> argumentFactory) {
factories.add(argumentFactory);
super.registerArgumentFactory(argumentFactory);
}
}
private List<Class<?>> sqlObjects;
public Jdbi(final Class<?>... sqlObjects) {
this("db", sqlObjects);
}
public Jdbi(final String db, final Class<?>... sqlObjects) {
super(db);
this.sqlObjects = Lists.newArrayList(sqlObjects);
}
@SuppressWarnings({"unchecked", "rawtypes" })
@Override
public void configure(final Env env, final Config config, final Binder binder) {
configure(env, config, binder, (name, ds) -> {
DBI dbi = new DBI2(() -> ds.getConnection());
dbi.setSQLLog(new SLF4JLog());
dbi.registerArgumentFactory(new OptionalArgumentFactory());
dbi.registerArgumentFactory(new IterableArgumentFactory());
dbi.registerContainerFactory(new OptionalContainerFactory());
dbi.setStatementRewriter(new ExpandedStmtRewriter());
ServiceKey serviceKey = env.serviceKey();
serviceKey.generate(DBI.class, name, k -> binder.bind(k).toInstance(dbi));
serviceKey.generate(Handle.class, name, k -> binder.bind(k).toProvider(() -> dbi.open()));
sqlObjects.forEach(sqlObject -> binder.bind(sqlObject)
.toProvider((Provider) () -> dbi.open(sqlObject)));
callback(dbi, config);
});
}
}