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

io.helidon.integrations.cdi.hibernate.DataSourceBackedDialectFactory Maven / Gradle / Ivy

/*
 * Copyright (c) 2023 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.integrations.cdi.hibernate;

import java.lang.System.Logger;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.util.Map;

import javax.sql.DataSource;

import org.hibernate.boot.registry.StandardServiceInitiator;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.jdbc.dialect.internal.DialectFactoryImpl;
import org.hibernate.engine.jdbc.dialect.spi.BasicSQLExceptionConverter;
import org.hibernate.engine.jdbc.dialect.spi.DatabaseMetaDataDialectResolutionInfoAdapter;
import org.hibernate.engine.jdbc.dialect.spi.DialectFactory;
import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo;
import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfoSource;
import org.hibernate.service.spi.ServiceContributor;
import org.hibernate.service.spi.ServiceRegistryImplementor;

import static java.lang.System.Logger.Level.WARNING;

/**
 * A {@link DialectFactory} implementation (and a {@link ServiceContributor}, and a {@link StandardServiceInitiator
 * StandardServiceInitiator<DialectFactory>}) that introspects {@link DatabaseMetaData} from a configured {@link
 * DataSource}.
 *
 * 

Hibernate is guaranteed to perform each of the following invocations, once, ever, in order:

* *
    * *
  1. (The {@linkplain #DataSourceBackedDialectFactory() zero-argument constructor})
  2. * *
  3. The {@link #contribute(StandardServiceRegistryBuilder)} method
  4. * *
  5. The {@link #initiateService(Map, ServiceRegistryImplementor)} method (if applicable)
  6. * *
* *

Then, at application runtime, after the sole instance of this class has been installed following the protocol * above, Hibernate will call the {@link #buildDialect(Map, DialectResolutionInfoSource)} method as appropriate.

* * @see #contribute(StandardServiceRegistryBuilder) * * @see #initiateService(Map, ServiceRegistryImplementor) * * @see #buildDialect(Map, DialectResolutionInfoSource) * * @see Custom {@code Service} Implementations (overriding) in the Hibernate Integration Guide */ public final class DataSourceBackedDialectFactory implements DialectFactory, ServiceContributor, StandardServiceInitiator { /* * Static fields. */ /** * A {@link Logger} for instances of this class. * * @see Logger */ private static final Logger LOGGER = System.getLogger(DataSourceBackedDialectFactory.class.getName()); /** * A version identifier for {@linkplain java.io.Serializable serialization} purposes. */ private static final long serialVersionUID = 1L; /* * Instance fields. */ /** * An instance of Hibernate's standard {@link DialectFactory} implementation ({@link DialectFactoryImpl}) to which * all "real work" is delegated. * *

This field is set once, ever, to a non-{@code null} value, by the {@link #initiateService(Map, * ServiceRegistryImplementor)} method, which Hibernate calls as part of its bootstrap protocol.

* * @see #initiateService(Map, ServiceRegistryImplementor) * * @see StandardServiceInitiator#initiateService(Map, ServiceRegistryImplementor) * * @see DialectFactoryImpl#buildDialect(Map, DialectResolutionInfoSource) */ private DialectFactoryImpl dfi; /* * Constructors. */ /** * Creates a new {@link DataSourceBackedDialectFactory}. * * @deprecated For use by {@link java.util.ServiceLoader} instances only. */ @Deprecated public DataSourceBackedDialectFactory() { super(); } /* * Instance methods. */ /** * Retrieves the {@link DataSource} stored in the supplied {@link Map} under the {@link * AvailableSettings#JAKARTA_JTA_DATASOURCE} key, and uses it to ultimately acquire a {@link DatabaseMetaData} instance * to adapt into a {@link DialectResolutionInfo} instance, such that a {@link DialectResolutionInfoSource} can be * constructed, in the case where the supplied {@code dialectResolutionInfoSource} is {@code null}, and passes the * resulting {@link DialectResolutionInfoSource} to the {@linkplain DialectFactoryImpl#buildDialect(Map, * DialectResolutionInfoSource) standard DialectFactory that Hibernate uses by default}, such that * Hibernate can determine what {@link Dialect} to use when database connectivity is established via a {@code * META-INF/persistence.xml}'s {@code jta-data-source} element. * * @param settings a {@link Map} containing the settings returned by an invocation of the {@link * StandardServiceRegistryBuilder#getSettings()} method; must not be {@code null} * * @param dialectResolutionInfoSource a {@link DialectResolutionInfoSource} supplied by Hibernate; may be, and often * is, {@code null} * * @return a {@link Dialect}; never {@code null} * * @exception NullPointerException if {@code settings} is {@code null} * * @exception org.hibernate.HibernateException if a {@link Dialect} could not be constructed * * @see DialectFactoryImpl#buildDialect(Map, DialectResolutionInfoSource) * * @see AvailableSettings#JAKARTA_JTA_DATASOURCE * * @see DatabaseMetaDataDialectResolutionInfoAdapter * * @deprecated For Hibernate use only. */ @Deprecated @Override // DialectFactory public Dialect buildDialect(Map settings, DialectResolutionInfoSource dialectResolutionInfoSource) { if (dialectResolutionInfoSource == null) { DataSource ds = getDataSource(settings); if (ds != null) { DatabaseMetaData dmd; try (Connection c = ds.getConnection()) { dmd = c.getMetaData(); } catch (SQLException e) { throw BasicSQLExceptionConverter.INSTANCE.convert(e); } DialectResolutionInfo dri = new DatabaseMetaDataDialectResolutionInfoAdapter(dmd); dialectResolutionInfoSource = () -> dri; } } // No null check needed for this.dfi because it is guaranteed to have been set by the initiateService(Map, // ServiceRegistryImplementor) method (q.v.). return this.dfi.buildDialect(settings, dialectResolutionInfoSource); } /** * {@linkplain StandardServiceRegistryBuilder#addInitiator(StandardServiceInitiator) Contributes} this {@link * DataSourceBackedDialectFactory} as a {@link StandardServiceInitiator * StandardServiceInitiator<DialectFactory>} if and only if certain requirements are met. * *

This method will call {@link StandardServiceRegistryBuilder#addInitiator(StandardServiceInitiator) * standardServiceRegistryBuilder.addInitiator(this)} if and only if all of the following preconditions hold:

* *
    * *
  • {@code settings.get(}{@link AvailableSettings#DIALECT DIALECT}{@code )} returns an object that is either {@code * null} or a {@linkplain String#isBlank() blank} {@link String}
  • * *
  • {@code settings.get(}{@link AvailableSettings#JAKARTA_JTA_DATASOURCE JAKARTA_JTA_DATASOURCE}{@code )} returns a * non-{@code null} {@link DataSource}
  • * *
* *

If any other state of affairs holds, this method takes no action.

* * @param standardServiceRegistryBuilder a {@link StandardServiceRegistryBuilder} whose {@link * StandardServiceRegistryBuilder#addInitiator(StandardServiceInitiator)} may be called with {@code this} as its * sole argument; must not be {@code null} * * @deprecated For Hibernate use only. */ @Deprecated @Override // ServiceContributor public void contribute(StandardServiceRegistryBuilder standardServiceRegistryBuilder) { Map settings = standardServiceRegistryBuilder.getSettings(); Object dialect = settings.get(AvailableSettings.DIALECT); if ((dialect == null || dialect instanceof String dialectString && dialectString.isBlank()) && getDataSource(settings) != null) { standardServiceRegistryBuilder.addInitiator(this); } } /** * Returns {@link Class Class<DialectFactory>} when invoked, thus describing the type of service the {@link * #initiateService(Map, ServiceRegistryImplementor)} will initiate. * * @return {@link Class Class<DialectFactory>} when invoked * * @see StandardServiceInitiator#getServiceInitiated() * * @deprecated For Hibernate use only. */ @Deprecated @Override // StandardServiceInitiator public Class getServiceInitiated() { return DialectFactory.class; } /** * Returns {@code this} when invoked. * * @param settings ignored * * @param serviceRegistry a {@link ServiceRegistryImplementor} supplied by Hibernate * * @return {@code this} when invoked * * @deprecated For Hibernate use only. */ @Deprecated @Override // StandardServiceInitiator public DataSourceBackedDialectFactory initiateService(Map settings, ServiceRegistryImplementor serviceRegistry) { // This method is kind of like a @PostConstruct method. The initiateService(Map, ServiceRegistryImplementor) // method is called only once, ever, by Hibernate, in a guaranteed bootstrap protocol. Consequently this is the // only place where the dfi instance field will ever be set. No null check for this.dfi is therefore needed in // the buildDialect(Map, DialectResolutionInfoSource) method. // // Additionally, ServiceInitiators are not required to be thread-safe, so the dfi instance variable is not, and // need not be, volatile. this.dfi = new DialectFactoryImpl(); this.dfi.injectServices(serviceRegistry); return this; } /* * Static methods. */ /** * Retrieves the value indexed under the {@link AvailableSettings#JAKARTA_JTA_DATASOURCE} key, and, if and only if it is * a {@link DataSource}, returns it. * * @param settings a {@link Map} containing the settings returned by an invocation of the {@link * StandardServiceRegistryBuilder#getSettings()} method; must not be {@code null} * * @return a {@link DataSource}, or {@code null} * * @exception NullPointerException if {@code settings} is {@code null} */ private static DataSource getDataSource(Map settings) { Object ds = settings.get(AvailableSettings.JAKARTA_JTA_DATASOURCE); if (!(ds instanceof DataSource)) { if (LOGGER.isLoggable(WARNING)) { LOGGER.log(WARNING, "No DataSource found under key " + AvailableSettings.JAKARTA_JTA_DATASOURCE); } return null; } return (DataSource) ds; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy