All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.helidon.dbclient.DbClient Maven / Gradle / Ivy

/*
 * Copyright (c) 2019, 2021 Oracle and/or its affiliates.
 *
 * 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.
 */
package io.helidon.dbclient;

import java.util.Arrays;
import java.util.List;
import java.util.ServiceLoader;
import java.util.function.Function;
import java.util.function.Supplier;

import io.helidon.common.mapper.MapperManager;
import io.helidon.common.reactive.Single;
import io.helidon.common.reactive.Subscribable;
import io.helidon.common.serviceloader.HelidonServiceLoader;
import io.helidon.config.Config;
import io.helidon.dbclient.spi.DbClientProvider;
import io.helidon.dbclient.spi.DbClientProviderBuilder;
import io.helidon.dbclient.spi.DbClientServiceProvider;
import io.helidon.dbclient.spi.DbMapperProvider;

/**
 * Helidon database client.
 */
public interface DbClient {
    /**
     * Execute database statements in transaction.
     *
     * @param       statement execution result type, MUST be either a {@link io.helidon.common.reactive.Multi}
     *                 or a {@link io.helidon.common.reactive.Single}, as returned by all APIs of DbClient.
     * @param       the type provided by the result type
     * @param executor database statement executor, see {@link DbExecute}
     * @return statement execution result
     */
    > T inTransaction(Function executor);

    /**
     * Execute database statement.
     *
     * @param       statement execution result type, MUST be either a {@link io.helidon.common.reactive.Multi}
     *                 or a {@link io.helidon.common.reactive.Single}, as returned by all APIs of DbClient
     * @param       the type provided by the result type
     * @param executor database statement executor, see {@link DbExecute}
     * @return statement execution result
     */
    > T execute(Function executor);

    /**
     * Type of this database provider (such as jdbc:mysql, mongoDB etc.).
     *
     * @return name of the database provider
     */
    String dbType();

    /**
     * Unwrap database client internals.
     * Only database connection is supported. Any operations based on this connection are blocking.
     * Reactive support must be implemented in user code.
     *
     * @param  target class to be unwrapped
     * @param cls target class to be unwrapped
     * @return database client internals future matching provided class.
     * @throws UnsupportedOperationException when provided class is not supported
     */
     Single unwrap(Class cls);

    /**
     * Create Helidon database handler builder.
     *
     * @param config name of the configuration node with driver configuration
     * @return database handler builder
     */
    static DbClient create(Config config) {
        return builder(config).build();
    }

    /**
     * Create Helidon database handler builder.
     * 

Database driver is loaded as SPI provider which implements {@link io.helidon.dbclient.spi.DbClientProvider} interface. * First provider on the class path is selected.

* * @return database handler builder */ static Builder builder() { DbClientProvider theSource = DbClientProviderLoader.first(); if (null == theSource) { throw new DbClientException( "No DbSource defined on classpath/module path. An implementation of io.helidon.dbclient.spi.DbSource is " + "required " + "to access a DB"); } return builder(theSource); } /** * Create Helidon database handler builder. * * @param source database driver * @return database handler builder */ static Builder builder(DbClientProvider source) { return new Builder(source); } /** * Create Helidon database handler builder. *

Database driver is loaded as SPI provider which implements {@link io.helidon.dbclient.spi.DbClientProvider} interface. * Provider on the class path with matching name is selected.

* * @param dbSource SPI provider name * @return database handler builder */ static Builder builder(String dbSource) { return DbClientProviderLoader.get(dbSource) .map(DbClient::builder) .orElseThrow(() -> new DbClientException( "No DbSource defined on classpath/module path for name: " + dbSource + ", available names: " + Arrays.toString(DbClientProviderLoader.names()))); } /** * Create a Helidon database handler builder from configuration. * * @param dbConfig configuration that should contain the key {@code source} that defines the type of this database * and is used to load appropriate {@link io.helidon.dbclient.spi.DbClientProvider} from Java Service loader * @return a builder pre-configured from the provided config */ static Builder builder(Config dbConfig) { return dbConfig.get("source") .asString() // use builder for correct DbSource .map(DbClient::builder) // or use the default one .orElseGet(DbClient::builder) .config(dbConfig); } /** * Helidon database handler builder. */ final class Builder implements io.helidon.common.Builder { private final HelidonServiceLoader.Builder clientServiceProviders = HelidonServiceLoader.builder( ServiceLoader.load(DbClientServiceProvider.class)); /** * Provider specific database handler builder instance. */ private final DbClientProviderBuilder theBuilder; private Config config = Config.empty(); /** * Create an instance of Helidon database handler builder. * * @param dbClientProvider provider specific {@link io.helidon.dbclient.spi.DbClientProvider} instance */ private Builder(DbClientProvider dbClientProvider) { this.theBuilder = dbClientProvider.builder(); } /** * Build provider specific database handler. * * @return new database handler instance */ @Override public DbClient build() { // add client services from service loader Config servicesConfig = config.get("services"); List providers = clientServiceProviders.build().asList(); for (DbClientServiceProvider provider : providers) { Config providerConfig = servicesConfig.get(provider.configKey()); if (!providerConfig.exists()) { // this client service is on classpath, yet there is no configuration for it, so it is ignored continue; } provider.create(providerConfig) .forEach(this::addService); } return theBuilder.build(); } /** * Add an interceptor provider. * The provider is only used when configuration is used ({@link #config(io.helidon.config.Config)}. * * @param provider provider to add to the list of loaded providers * @return updated builder instance */ public Builder addServiceProvider(DbClientServiceProvider provider) { this.clientServiceProviders.addService(provider); return this; } /** * Add a client service. * * @param clientService clientService to apply * @return updated builder instance */ public Builder addService(DbClientService clientService) { theBuilder.addService(clientService); return this; } /** * Add a client service. * * @param clientServiceSupplier supplier of client service * @return updated builder instance */ public Builder addService(Supplier clientServiceSupplier) { theBuilder.addService(clientServiceSupplier.get()); return this; } /** * Use database connection configuration from configuration file. * * @param config {@link io.helidon.config.Config} instance with database connection attributes * @return database provider builder */ public Builder config(Config config) { theBuilder.config(config); this.config = config; return this; } /** * Statements to use either from configuration * or manually configured. * * @param statements Statements to use * @return updated builder instance */ public Builder statements(DbStatements statements) { theBuilder.statements(statements); return this; } /** * Database schema mappers provider. * Mappers associated with types in this provider will override existing types associations loaded * as {@link io.helidon.dbclient.spi.DbMapperProvider} Java services. * * @param provider database schema mappers provider to use * @return updated builder instance */ public Builder mapperProvider(DbMapperProvider provider) { theBuilder.addMapperProvider(provider); return this; } /** * Mapper manager for generic mapping, such as mapping of parameters to expected types. * * @param manager mapper manager * @return updated builder instance */ public Builder mapperManager(MapperManager manager) { theBuilder.mapperManager(manager); return this; } } }