복붙노트

[SPRING] Hibernate 4.2와 Spring 3.1.1을 사용하여 MultiTenantConnectionProvider 설정하기

SPRING

Hibernate 4.2와 Spring 3.1.1을 사용하여 MultiTenantConnectionProvider 설정하기

저는 현재 별도의 스키마 aproach를 사용하여 다중 임차를 위해 최대 절전 모드를 설정하려고합니다. 약 2 일 동안 작업 한 후 Google을 통해 찾을 수있는 거의 모든 소스를 탐색 한 후 매우 좌절하기 시작했습니다.

기본적으로 나는 Hibernate devguide http://docs.jboss.org/hibernate/orm/4.1/devguide/en-US/html_single/#d5e4691에 제공된 가이드를 따르려고 노력하고있다. 하지만 불행히도 ConnectionProvider를 빌드하기 위해 ConnectionProviderUtils를 찾을 수 없습니다. 현재 나는 2 점을 계산하려고하고있다.

이 문제를 해결할 수있는 방법에 대한 지침 (방법, 클래스, 자습서)

그래서 여기 내 구현의 관련 부분이 있습니다 : 데이터 소스와 Hibernate.cfg.xml :

    <bean id="dataSource"   class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.microsoft.sqlserver.jdbc.SQLServerDriver" />
        <property name="url" value="jdbc:sqlserver://<host>:<port>;databaseName=<DbName>;" />
        <property name="username" value=<username> />
        <property name="password" value=<password> />
   </bean>
   <bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
        <!-- property name="dataSource" ref="dataSource" /-->
        <property name="annotatedClasses">
            <list>
                <value>c.h.utils.hibernate.User</value>
                <value>c.h.utils.hibernate.Role</value>
                <value>c.h.utils.hibernate.Tenant</value>
            </list>
        </property>
        <property name="hibernateProperties">
            <value>
                hibernate.dialect=org.hibernate.dialect.SQLServerDialect
                hibernate.show_sql=true
                hibernate.multiTenancy=SCHEMA
                hibernate.tenant_identifier_resolver=c.h.utils.hibernate.CurrentTenantIdentifierResolver
                hibernate.multi_tenant_connection_provider=c.h.utils.hibernate.MSSQLMultiTenantConnectionProviderImpl 
            </value>
        </property>
    </bean>

MSSQLMultiTenantConnectionProviderImpl :

package c.hoell.utils.hibernate;

import java.sql.Connection;
import java.sql.SQLException;

import javax.sql.DataSource;

import org.hibernate.service.UnknownUnwrapTypeException;
import org.hibernate.service.jdbc.connections.spi.ConnectionProvider;
import org.hibernate.service.jdbc.connections.spi.MultiTenantConnectionProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class MSSQLMultiTenantConnectionProviderImpl implements MultiTenantConnectionProvider  {




    private static final long serialVersionUID = 8074002161278796379L;

    @Autowired
    private DataSource dataSource;


    public void configure(Properties props) throws HibernateException {

    }


    @Override
    public Connection getAnyConnection() throws SQLException {
        Properties properties = getConnectionProperties(); //method which sets the hibernate properties

        DriverManagerConnectionProviderImpl defaultProvider = new   DriverManagerConnectionProviderImpl();
        defaultProvider.configure(properties);
        Connection con = defaultProvider.getConnection();
        ResultSet rs = con.createStatement().executeQuery("SELECT * FROM [schema].table");
        rs.close(); //the statement and sql is just to test the connection
        return defaultProvider.getConnection();
    }

    @Override
    public Connection getConnection(String tenantIdentifier) throws SQLException {
        <--not sure how to implement this-->
    }

    @Override
    public void releaseAnyConnection(Connection connection) throws SQLException {
        connection.close();

    }

    @Override
    public void releaseConnection(String tenantIdentifier, Connection connection){
        try {
            this.releaseAnyConnection(connection);
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    @Override
    public boolean supportsAggressiveRelease() {
        return false;
    }

    @Override
    public boolean isUnwrappableAs(Class unwrapType) {
        return ConnectionProvider.class.equals( unwrapType ) || MultiTenantConnectionProvider.class.equals( unwrapType ) || MSSQLMultiTenantConnectionProviderImpl.class.isAssignableFrom( unwrapType );
    }

    @SuppressWarnings("unchecked")
    @Override
    public <T> T unwrap(Class<T> unwrapType) {
        if ( isUnwrappableAs( unwrapType ) ) {
            return (T) this;
        }
        else {
            throw new UnknownUnwrapTypeException( unwrapType );
        }
    }

    public DataSource getDataSource() {
        return dataSource;
    }

    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }

}

지금은 설정 파일에서 필요한 설정을 얻기 위해 볼 수있는 두 가지 접근 방법이 있습니다. configure () 메소드를 실행하거나 데이터 소스의 주입을 가능하게하십시오.   나는 첫번째 것이 더 좋은 길일 것 같아요.

언급해야 할 중요한 점은, Hibernate를 하나의 세입자 (최대 수면 (Hibernate)이 사용하는 표준 ConnectionProvider를 사용하는 MultiTenantConnectionProvider를 사용하지 않고 사용하는 것을 의미)

이미이 게시물을 읽는 사람에게 큰 감사를드립니다. 답변을 기대합니다.

친애하는

나는 이것으로 조금 놀았고 MultiTenantConnectionProvider (위 코드 업데이트)에 연결 상세 정보를 하드 코드했습니다. 이것은 MultiTenantConnectionProvider와 관련하여 잘 작동합니다. 그러나 이것은 여전히 ​​내 문제를 해결하지 못합니다.   이제 트랜잭션 관리자를 초기화 할 때 내 응용 프로그램이 실패합니다.

<tx:annotation-driven transaction-manager="txManager" proxy-target-class="true"/>
    <bean id="txManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>

이것은 스택 추적의 예외입니다.

이 문제를 디버그 모드에서 추적 해 보았는데 문제는 내 SessionFactory가 DataSource를 유지하지 못한다는 것을 알았습니다. (hibernate.cfg.xml에 DataSource를 지정했는지 여부는 아무런 차이가 없다) 그러나 TransactionManager를 초기화 할 때 SessionFactory에서 DataSource를 가져 오려고 시도하고 결과적으로 NullPointerException으로 실패합니다.   누구나 최대 절전 모드의 내부 동작 중 어떤 시점에서 이것이 실패했는지에 대한 힌트를 가지고 있습니까? 필자가 본 모든 문서와 게시물에서 DataSource의 SessionFactory 로의 주입을 처리 할 필요가 없음을 알 수 없습니다.   당분간은 데이터 소스를 필요한 장소에 가져 오는 방법이나 초기화 흐름을 변경하는 방법을 알아 내려고합니다. 누구든지 더 좋은 아이디어가 있다면 나는 정말로 행복 할 것이다.

편집 : 또한 최대 절전 모드 포럼에 게시 됨 :

따라서 TransactionManager의 autodetectDataSource 속성을 false로 설정하여이 문제를 해결할 수있었습니다.

<property name="autodetectDataSource" value="false"/>

다음 게시물 http://forum.springsource.org/showthread.php?123478-SessionFactory-configured-for-ultultancy-but-no-tenant-identifier-specified에서 힌트를 얻었습니다. 불행히도 나는 지금 정확히 그 문제에 갇혀있다. ^^ "그러나 이것은 다른 주제에 대한 문제입니다. (편집 : 이것은 이전 테스트와 잘못된 구성 일뿐입니다.

이 주제에 관해서는, 어떻게 든 Spring Security를 ​​사용하기위한 설정에서 이미 가지고있는 DataSource를 재사용 할 수 있기를 원하는 Hibernate가 두 곳에서 DataSource를 설정하지 않아도된다는 문제가 남아 있습니다.   그래서 문제는 여전히 MultiTenantConnectionProvider에서 DataSource의 사용을 통합하는 방법을 의미합니다. 누구든지 그 어떤 힌트를 찾을 수있는 아이디어를 가지고 있습니까?

해결법

  1. ==============================

    1.이 질문의 주석 자 (HHH-8752) 중 한 명이 언급 한 JIRA 문제에 대한 Steve Ebersole의 의견에 따르면 :

    이 질문의 주석 자 (HHH-8752) 중 한 명이 언급 한 JIRA 문제에 대한 Steve Ebersole의 의견에 따르면 :

    나는 방금 그의 제안을 따랐고 그것을 가능하게 만들었다.

    이것은 Spring Bean으로 정의 된 CurrentTenantIdentifierResolver입니다.

    @Component
    @Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
    public class RequestURITenantIdentifierResolver implements CurrentTenantIdentifierResolver {
    
        @Autowired
        private HttpServletRequest request;
    
        @Override
        public String resolveCurrentTenantIdentifier() {
            String[] pathElements = request.getRequestURI().split("/");
            String tenant = pathElements[1];
            return tenant;
        }
    
        @Override
        public boolean validateExistingCurrentSessions() {
            return true;
        }
    }
    

    이것은 Spring Bean으로 정의 된 MultiTenantConnectionProvider입니다.

    @Component
    public class SchemaPerTenantConnectionProviderImpl implements MultiTenantConnectionProvider {
    
        @Autowired
        private DataSource dataSource;
    
        @Override
        public Connection getAnyConnection() throws SQLException {
            return dataSource.getConnection();
        }
    
        @Override
        public void releaseAnyConnection(final Connection connection) throws SQLException {
            connection.close();
        }
    
        @Override
        public Connection getConnection(final String tenantIdentifier) throws SQLException {
            final Connection connection = getAnyConnection();
            try {
                connection.createStatement().execute("USE " + tenantIdentifier);
            } catch (SQLException e) {
                throw new HibernateException("Could not alter JDBC connection to specified schema [" + tenantIdentifier + "]",
                                             e);
            }
            return connection;
        }
    
        @Override
        public void releaseConnection(final String tenantIdentifier, final Connection connection) throws SQLException {
            try {
                connection.createStatement().execute("USE dummy");
            } catch (SQLException e) {
                // on error, throw an exception to make sure the connection is not returned to the pool.
                // your requirements may differ
                throw new HibernateException(
                        "Could not alter JDBC connection to specified schema [" +
                                tenantIdentifier + "]",
                        e
                );
            } finally {
                connection.close();
            }
        }
    
        @Override
        public boolean supportsAggressiveRelease() {
            return true;
        }
    
        @Override
        public boolean isUnwrappableAs(Class aClass) {
            return false;
        }
    
        @Override
        public <T> T unwrap(Class<T> aClass) {
            return null;
        }
    }
    

    마지막으로 위의 두 가지 구성 요소를 사용하도록 유선 된 LocalContainerEntityManagerFactoryBean입니다.

    @Configuration
    public class HibernateConfig {
    
        @Bean
        public JpaVendorAdapter jpaVendorAdapter() {
            return new HibernateJpaVendorAdapter();
        }
    
    
        @Bean
        public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource,
                                                                           MultiTenantConnectionProvider multiTenantConnectionProvider,
                                                                           CurrentTenantIdentifierResolver tenantIdentifierResolver) {
            LocalContainerEntityManagerFactoryBean emfBean = new LocalContainerEntityManagerFactoryBean();
            emfBean.setDataSource(dataSource);
            emfBean.setPackagesToScan(VistoJobsApplication.class.getPackage().getName());
            emfBean.setJpaVendorAdapter(jpaVendorAdapter());
    
            Map<String, Object> jpaProperties = new HashMap<>();
            jpaProperties.put(org.hibernate.cfg.Environment.MULTI_TENANT,
                              MultiTenancyStrategy.SCHEMA);
            jpaProperties.put(org.hibernate.cfg.Environment.MULTI_TENANT_CONNECTION_PROVIDER,
                              multiTenantConnectionProvider);
            jpaProperties.put(org.hibernate.cfg.Environment.MULTI_TENANT_IDENTIFIER_RESOLVER,
                              tenantIdentifierResolver);
            emfBean.setJpaPropertyMap(jpaProperties);
            return emfBean;
        }
    }
    

    사용하고있는 데이터 소스는 스프링 부트에 의해 자동으로 사용 가능하게됩니다.

    이게 도움이 되길 바란다.

  2. ==============================

    2.좋아, 이걸 마무리 지으면 다음과 같이 끝났어. 나는 간단한 CurrentTenantIdentifierResolver를 사용한다. 그리고 다른 곳에서 DataSource를 내 MultiTenantConnectionProviderImpl에 주입하려고 시도하는 대신 ConnectionProvider에서 DataSource (c3p0 ComboPooledDatasource)를 만들고 내 ConnectionProvider에서 제공하는 연결 만 사용하여 시작했습니다. 그래서 나는 여분의 DataSource를 제거했다. DataSource의 속성을 쉽게 구성 할 수 있도록 속성 파일에서 구성 데이터를 가져 오는 것을 선택했습니다.

    좋아, 이걸 마무리 지으면 다음과 같이 끝났어. 나는 간단한 CurrentTenantIdentifierResolver를 사용한다. 그리고 다른 곳에서 DataSource를 내 MultiTenantConnectionProviderImpl에 주입하려고 시도하는 대신 ConnectionProvider에서 DataSource (c3p0 ComboPooledDatasource)를 만들고 내 ConnectionProvider에서 제공하는 연결 만 사용하여 시작했습니다. 그래서 나는 여분의 DataSource를 제거했다. DataSource의 속성을 쉽게 구성 할 수 있도록 속성 파일에서 구성 데이터를 가져 오는 것을 선택했습니다.

    CurrentTenantIdentifierResolver Impl :

    public class CurrentTenantIdentifierResolverImpl implements CurrentTenantIdentifierResolver {
    
    
        /**
         * The method returns the RequestServerName as tenantidentifier.
         * If no FacesContext is available null is returned.
         * 
         * @return String tenantIdentifier
         */
        @Override
        public String resolveCurrentTenantIdentifier() {
            if (FacesContext.getCurrentInstance() != null){
                return FacesContext.getCurrentInstance().getExternalContext().getRequestServerName();
            } else {
                return null;
            }
        }
    
        @Override
        public boolean validateExistingCurrentSessions() {
            return true;
        }
    
    }
    

    MultiTenantConnectionProviderImpl :

    PropertyUtil은 내 속성을 가져 오는 간단한 로컬 도우미 클래스에 불과합니다. 그것이 특별한 것이 아니기 때문에 나는 대답을 어지럽히 지 않기 위해 그것을 포함시키지 않을 것입니다.

    public class MultiTenantConnectionProviderImpl implements MultiTenantConnectionProvider  {
    
    
        private static final long serialVersionUID = 8074002161278796379L;
    
    
        private static Logger log = LoggerFactory.getLogger(MultiTenantConnectionProviderImpl.class );
    
        private ComboPooledDataSource cpds;
    
        private Properties properties;
    
        /**
         * 
         * Constructor. Initializes the ComboPooledDataSource based on the config.properties.
         * 
         * @throws PropertyVetoException
         */
        public MultiTenantConnectionProviderImpl() throws PropertyVetoException {
            log.info("Initializing Connection Pool!");
            properties = new Properties();
            try {
                properties.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("config.properties"));
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            cpds = new ComboPooledDataSource("Example");
            cpds.setDriverClass(properties.getProperty("jdbc.driver"));
            cpds.setJdbcUrl(properties.getProperty("jdbc.url"));
            cpds.setUser(properties.getProperty("jdbc.user"));
            cpds.setPassword(PropertyUtil.getCredential("jdbc.password"));
            log.info("Connection Pool initialised!");
        }
    
    
        @Override
        public Connection getAnyConnection() throws SQLException {
            log.debug("Get Default Connection:::Number of connections (max: busy - idle): {} : {} - {}",new int[]{cpds.getMaxPoolSize(),cpds.getNumBusyConnectionsAllUsers(),cpds.getNumIdleConnectionsAllUsers()});
            if (cpds.getNumConnectionsAllUsers() == cpds.getMaxPoolSize()){
                log.warn("Maximum number of connections opened");
            }
            if (cpds.getNumConnectionsAllUsers() == cpds.getMaxPoolSize() && cpds.getNumIdleConnectionsAllUsers()==0){
                log.error("Connection pool empty!");
            }
            return cpds.getConnection();
        }
    
        @Override
        public Connection getConnection(String tenantIdentifier) throws SQLException {
            log.debug("Get {} Connection:::Number of connections (max: busy - idle): {} : {} - {}",new Object[]{tenantIdentifier, cpds.getMaxPoolSize(),cpds.getNumBusyConnectionsAllUsers(),cpds.getNumIdleConnectionsAllUsers()});
            if (cpds.getNumConnectionsAllUsers() == cpds.getMaxPoolSize()){
                log.warn("Maximum number of connections opened");
            }
            if (cpds.getNumConnectionsAllUsers() == cpds.getMaxPoolSize() && cpds.getNumIdleConnectionsAllUsers()==0){
                log.error("Connection pool empty!");
            }
            return cpds.getConnection(tenantIdentifier, PropertyUtil.getCredential(tenantIdentifier));
        }
    
        @Override
        public void releaseAnyConnection(Connection connection) throws SQLException {
            connection.close();
        }
    
        @Override
        public void releaseConnection(String tenantIdentifier, Connection connection){
            try {
                this.releaseAnyConnection(connection);
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
    
        @Override
        public boolean supportsAggressiveRelease() {
            return false;
        }
    
        @SuppressWarnings("rawtypes")
        @Override
        public boolean isUnwrappableAs(Class unwrapType) {
            return ConnectionProvider.class.equals( unwrapType ) || MultiTenantConnectionProvider.class.equals( unwrapType ) || MultiTenantConnectionProviderImpl.class.isAssignableFrom( unwrapType );
        }
    
        @SuppressWarnings("unchecked")
        @Override
        public <T> T unwrap(Class<T> unwrapType) {
            if ( isUnwrappableAs( unwrapType ) ) {
                return (T) this;
            }
            else {
                throw new UnknownUnwrapTypeException( unwrapType );
            }
        }
    }
    

    c3p0 특정 구성은 c3p0-config.xml에서 가져옵니다.

    <c3p0-config>
        <named-config name="Example">
            <property name="acquireIncrement">3</property>
            <property name="preferredTestQuery">SELECT 1</property>
            <property name="checkoutTimeout">2000</property>
            <property name="idleConnectionTestPeriod">30</property>
            <property name="initialPoolSize">1</property>
            <property name="maxIdleTime">18000</property>
            <property name="maxPoolSize">30</property>
            <property name="minPoolSize">1</property>
            <property name="maxStatements">50</property>
            <property name="testConnectionOnCheckin">true</property>
        </named-config>
    </c3p0-config>
    

    그리고 db 특성은 config.properties 파일에 의해 제공됩니다.

    jdbc.url=<serverUrl>
    jdbc.driver=<driverClass>
    jdbc.dbName=<dBname>
    jdbc.dbowner=<dbo>
    jdbc.username=<user>
    jdbc.password=<password>
    
    hibernate.dialect=<hibernateDialect>
    hibernate.debug=false
    

    자격 증명은 다른 파일과 비슷한 방식으로 가져옵니다.

    개선 사항을 제공하는 모든 의견을 보내 주시면 감사하겠습니다.

  3. ==============================

    3. 대신 을 사용하면 나에게 도움이되는 것처럼 보입니다. https://jira.springsource.org/browse/SPR-10823#comment-94855

    대신 을 사용하면 나에게 도움이되는 것처럼 보입니다. https://jira.springsource.org/browse/SPR-10823#comment-94855

     <bean id="multiTenantConnectionProvider"
           class="test.MultiTenantConnectionProviderImpl"/>
    
    
     <bean id="sessionFactory"
          class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
        <property name="packagesToScan" value="test.models" />
        <property name="hibernateProperties">
            <map>
                <entry key="hibernate.dialect" value="org.hibernate.dialect.PostgreSQL82Dialect"/>
                <entry key="hibernate.multiTenancy" value="SCHEMA"/>
                <entry key="hibernate.tenant_identifier_resolver" value="test.CurrentTenantIdentifierResolverImpl"/>
                <entry key="hibernate.multi_tenant_connection_provider" value-ref="multiTenantConnectionProvider"/>
            </map>
        </property>
      </bean>
    
  4. ==============================

    4.Spring Framework 버전 3.2.4부터는 Spring 컨테이너가 MultiTenantConnectionProvider 및 CurrentTenantIdentifierResolver를 관리 할 방법이 없습니다. 이로 인해 이미 구성된 DataSource, WebContext 및 다른 Spring 관리 Bean 및 기능을 사용하는 것과 같은 많은 장애가 발생합니다. 나는보다 깨끗한 솔루션을 찾으려고 노력했지만 오직 하나만 나와 있습니다.

    Spring Framework 버전 3.2.4부터는 Spring 컨테이너가 MultiTenantConnectionProvider 및 CurrentTenantIdentifierResolver를 관리 할 방법이 없습니다. 이로 인해 이미 구성된 DataSource, WebContext 및 다른 Spring 관리 Bean 및 기능을 사용하는 것과 같은 많은 장애가 발생합니다. 나는보다 깨끗한 솔루션을 찾으려고 노력했지만 오직 하나만 나와 있습니다.

    org.springframework.orm.hibernate4.LocalSessionFactoryBuilder를 확장하고 커스텀 LocalSessionFactoryBean을 작성합니다 (서브 클래스화할 수없고 LocalSessionFactoryBuilder를 제공 할 수 있습니다.

    여기에 간다 :

    package com.levitech.hibernate;
    
    import javax.sql.DataSource;
    
    import org.hibernate.cfg.Environment;
    import org.hibernate.context.spi.CurrentTenantIdentifierResolver;
    import org.hibernate.service.jdbc.connections.spi.MultiTenantConnectionProvider;
    import org.springframework.core.io.ResourceLoader;
    
    public class CustomLocalSessionFactoryBuilder extends org.springframework.orm.hibernate4.LocalSessionFactoryBuilder {
    
    
        public CustomLocalSessionFactoryBuilder(DataSource dataSource,ResourceLoader resourceLoader, MultiTenantConnectionProvider connectionProvider, 
                CurrentTenantIdentifierResolver tenantIdResolver) {
            super(dataSource, resourceLoader);
            getProperties().put(Environment.MULTI_TENANT_CONNECTION_PROVIDER, connectionProvider);
            getProperties().put(Environment.MULTI_TENANT_IDENTIFIER_RESOLVER, tenantIdResolver);
    
        }
    
    
    
    }
    

    LocalSessionFactoryBean을 대체합니다 (유일한 변경은 맞춤 LocalSessionFactoryBuilder를 사용하는 afterPropertiesSet () 메소드에 있음).

    package com.levitech.hibernate;
    
    /*
     * Copyright 2002-2013 the original author or authors.
     *
     * 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.
     */
    
    
    
    import java.io.File;
    import java.io.IOException;
    import java.util.Properties;
    
    import javax.sql.DataSource;
    
    import org.hibernate.Interceptor;
    import org.hibernate.SessionFactory;
    import org.hibernate.cfg.Configuration;
    import org.hibernate.cfg.NamingStrategy;
    import org.hibernate.context.spi.CurrentTenantIdentifierResolver;
    import org.hibernate.service.jdbc.connections.spi.MultiTenantConnectionProvider;
    import org.springframework.beans.factory.DisposableBean;
    import org.springframework.beans.factory.FactoryBean;
    import org.springframework.beans.factory.InitializingBean;
    import org.springframework.context.ResourceLoaderAware;
    import org.springframework.core.io.ClassPathResource;
    import org.springframework.core.io.Resource;
    import org.springframework.core.io.ResourceLoader;
    import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
    import org.springframework.core.io.support.ResourcePatternResolver;
    import org.springframework.core.io.support.ResourcePatternUtils;
    import org.springframework.orm.hibernate4.HibernateExceptionTranslator;
    import org.springframework.orm.hibernate4.LocalSessionFactoryBuilder;
    
    /**
     * {@link org.springframework.beans.factory.FactoryBean} that creates a Hibernate
     * {@link org.hibernate.SessionFactory}. This is the usual way to set up a shared
     * Hibernate SessionFactory in a Spring application context; the SessionFactory can
     * then be passed to Hibernate-based data access objects via dependency injection.
     *
     * <p><b>NOTE:</b> This variant of LocalSessionFactoryBean requires Hibernate 4.0 or higher.
     * It is similar in role to the same-named class in the {@code orm.hibernate3} package.
     * However, in practice, it is closer to {@code AnnotationSessionFactoryBean} since
     * its core purpose is to bootstrap a {@code SessionFactory} from annotation scanning.
     *
     * <p><b>NOTE:</b> To set up Hibernate 4 for Spring-driven JTA transactions, make
     * sure to either specify the {@link #setJtaTransactionManager "jtaTransactionManager"}
     * bean property or to set the "hibernate.transaction.factory_class" property to
     * {@link org.hibernate.engine.transaction.internal.jta.CMTTransactionFactory}.
     * Otherwise, Hibernate's smart flushing mechanism won't work properly.
     *
     * @author Juergen Hoeller
     * @since 3.1
     * @see #setDataSource
     * @see #setPackagesToScan
     * @see LocalSessionFactoryBuilder
     */
    public class CustomLocalSessionFactoryBean extends HibernateExceptionTranslator
            implements FactoryBean<SessionFactory>, ResourceLoaderAware, InitializingBean, DisposableBean {
    
    
        private MultiTenantConnectionProvider multiTenantConnectionProvider;
    
        private CurrentTenantIdentifierResolver tenantIdResolver;
    
        private DataSource dataSource;
    
        private Resource[] configLocations;
    
        private String[] mappingResources;
    
        private Resource[] mappingLocations;
    
        private Resource[] cacheableMappingLocations;
    
        private Resource[] mappingJarLocations;
    
        private Resource[] mappingDirectoryLocations;
    
        private Interceptor entityInterceptor;
    
        private NamingStrategy namingStrategy;
    
        private Properties hibernateProperties;
    
        private Class<?>[] annotatedClasses;
    
        private String[] annotatedPackages;
    
        private String[] packagesToScan;
    
        private Object jtaTransactionManager;
    
        private ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
    
        private Configuration configuration;
    
        private SessionFactory sessionFactory;
    
    
    
    
    
        public MultiTenantConnectionProvider getMultiTenantConnectionProvider() {
            return multiTenantConnectionProvider;
        }
    
        public void setMultiTenantConnectionProvider(
                MultiTenantConnectionProvider multiTenantConnectionProvider) {
            this.multiTenantConnectionProvider = multiTenantConnectionProvider;
        }
    
        public CurrentTenantIdentifierResolver getTenantIdResolver() {
            return tenantIdResolver;
        }
    
        public void setTenantIdResolver(CurrentTenantIdentifierResolver tenantIdResolver) {
            this.tenantIdResolver = tenantIdResolver;
        }
    
        /**
         * Set the DataSource to be used by the SessionFactory.
         * If set, this will override corresponding settings in Hibernate properties.
         * <p>If this is set, the Hibernate settings should not define
         * a connection provider to avoid meaningless double configuration.
         */
        public void setDataSource(DataSource dataSource) {
            this.dataSource = dataSource;
        }
    
        /**
         * Set the location of a single Hibernate XML config file, for example as
         * classpath resource "classpath:hibernate.cfg.xml".
         * <p>Note: Can be omitted when all necessary properties and mapping
         * resources are specified locally via this bean.
         * @see org.hibernate.cfg.Configuration#configure(java.net.URL)
         */
        public void setConfigLocation(Resource configLocation) {
            this.configLocations = new Resource[] {configLocation};
        }
    
        /**
         * Set the locations of multiple Hibernate XML config files, for example as
         * classpath resources "classpath:hibernate.cfg.xml,classpath:extension.cfg.xml".
         * <p>Note: Can be omitted when all necessary properties and mapping
         * resources are specified locally via this bean.
         * @see org.hibernate.cfg.Configuration#configure(java.net.URL)
         */
        public void setConfigLocations(Resource[] configLocations) {
            this.configLocations = configLocations;
        }
    
        /**
         * Set Hibernate mapping resources to be found in the class path,
         * like "example.hbm.xml" or "mypackage/example.hbm.xml".
         * Analogous to mapping entries in a Hibernate XML config file.
         * Alternative to the more generic setMappingLocations method.
         * <p>Can be used to add to mappings from a Hibernate XML config file,
         * or to specify all mappings locally.
         * @see #setMappingLocations
         * @see org.hibernate.cfg.Configuration#addResource
         */
        public void setMappingResources(String[] mappingResources) {
            this.mappingResources = mappingResources;
        }
    
        /**
         * Set locations of Hibernate mapping files, for example as classpath
         * resource "classpath:example.hbm.xml". Supports any resource location
         * via Spring's resource abstraction, for example relative paths like
         * "WEB-INF/mappings/example.hbm.xml" when running in an application context.
         * <p>Can be used to add to mappings from a Hibernate XML config file,
         * or to specify all mappings locally.
         * @see org.hibernate.cfg.Configuration#addInputStream
         */
        public void setMappingLocations(Resource[] mappingLocations) {
            this.mappingLocations = mappingLocations;
        }
    
        /**
         * Set locations of cacheable Hibernate mapping files, for example as web app
         * resource "/WEB-INF/mapping/example.hbm.xml". Supports any resource location
         * via Spring's resource abstraction, as long as the resource can be resolved
         * in the file system.
         * <p>Can be used to add to mappings from a Hibernate XML config file,
         * or to specify all mappings locally.
         * @see org.hibernate.cfg.Configuration#addCacheableFile(java.io.File)
         */
        public void setCacheableMappingLocations(Resource[] cacheableMappingLocations) {
            this.cacheableMappingLocations = cacheableMappingLocations;
        }
    
        /**
         * Set locations of jar files that contain Hibernate mapping resources,
         * like "WEB-INF/lib/example.hbm.jar".
         * <p>Can be used to add to mappings from a Hibernate XML config file,
         * or to specify all mappings locally.
         * @see org.hibernate.cfg.Configuration#addJar(java.io.File)
         */
        public void setMappingJarLocations(Resource[] mappingJarLocations) {
            this.mappingJarLocations = mappingJarLocations;
        }
    
        /**
         * Set locations of directories that contain Hibernate mapping resources,
         * like "WEB-INF/mappings".
         * <p>Can be used to add to mappings from a Hibernate XML config file,
         * or to specify all mappings locally.
         * @see org.hibernate.cfg.Configuration#addDirectory(java.io.File)
         */
        public void setMappingDirectoryLocations(Resource[] mappingDirectoryLocations) {
            this.mappingDirectoryLocations = mappingDirectoryLocations;
        }
    
        /**
         * Set a Hibernate entity interceptor that allows to inspect and change
         * property values before writing to and reading from the database.
         * Will get applied to any new Session created by this factory.
         * @see org.hibernate.cfg.Configuration#setInterceptor
         */
        public void setEntityInterceptor(Interceptor entityInterceptor) {
            this.entityInterceptor = entityInterceptor;
        }
    
        /**
         * Set a Hibernate NamingStrategy for the SessionFactory, determining the
         * physical column and table names given the info in the mapping document.
         * @see org.hibernate.cfg.Configuration#setNamingStrategy
         */
        public void setNamingStrategy(NamingStrategy namingStrategy) {
            this.namingStrategy = namingStrategy;
        }
    
        /**
         * Set Hibernate properties, such as "hibernate.dialect".
         * <p>Note: Do not specify a transaction provider here when using
         * Spring-driven transactions. It is also advisable to omit connection
         * provider settings and use a Spring-set DataSource instead.
         * @see #setDataSource
         */
        public void setHibernateProperties(Properties hibernateProperties) {
            this.hibernateProperties = hibernateProperties;
        }
    
        /**
         * Return the Hibernate properties, if any. Mainly available for
         * configuration through property paths that specify individual keys.
         */
        public Properties getHibernateProperties() {
            if (this.hibernateProperties == null) {
                this.hibernateProperties = new Properties();
            }
            return this.hibernateProperties;
        }
    
        /**
         * Specify annotated entity classes to register with this Hibernate SessionFactory.
         * @see org.hibernate.cfg.Configuration#addAnnotatedClass(Class)
         */
        public void setAnnotatedClasses(Class<?>[] annotatedClasses) {
            this.annotatedClasses = annotatedClasses;
        }
    
        /**
         * Specify the names of annotated packages, for which package-level
         * annotation metadata will be read.
         * @see org.hibernate.cfg.Configuration#addPackage(String)
         */
        public void setAnnotatedPackages(String[] annotatedPackages) {
            this.annotatedPackages = annotatedPackages;
        }
    
        /**
         * Specify packages to search for autodetection of your entity classes in the
         * classpath. This is analogous to Spring's component-scan feature
         * ({@link org.springframework.context.annotation.ClassPathBeanDefinitionScanner}).
         */
        public void setPackagesToScan(String... packagesToScan) {
            this.packagesToScan = packagesToScan;
        }
    
        /**
         * Set the Spring {@link org.springframework.transaction.jta.JtaTransactionManager}
         * or the JTA {@link javax.transaction.TransactionManager} to be used with Hibernate,
         * if any.
         * @see LocalSessionFactoryBuilder#setJtaTransactionManager
         */
        public void setJtaTransactionManager(Object jtaTransactionManager) {
            this.jtaTransactionManager = jtaTransactionManager;
        }
    
        public void setResourceLoader(ResourceLoader resourceLoader) {
            this.resourcePatternResolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader);
        }
    
    
        public void afterPropertiesSet() throws IOException {
            LocalSessionFactoryBuilder sfb = new CustomLocalSessionFactoryBuilder(this.dataSource, this.resourcePatternResolver, multiTenantConnectionProvider, tenantIdResolver);
    
            if (this.configLocations != null) {
                for (Resource resource : this.configLocations) {
                    // Load Hibernate configuration from given location.
                    sfb.configure(resource.getURL());
                }
            }
    
            if (this.mappingResources != null) {
                // Register given Hibernate mapping definitions, contained in resource files.
                for (String mapping : this.mappingResources) {
                    Resource mr = new ClassPathResource(mapping.trim(), this.resourcePatternResolver.getClassLoader());
                    sfb.addInputStream(mr.getInputStream());
                }
            }
    
            if (this.mappingLocations != null) {
                // Register given Hibernate mapping definitions, contained in resource files.
                for (Resource resource : this.mappingLocations) {
                    sfb.addInputStream(resource.getInputStream());
                }
            }
    
            if (this.cacheableMappingLocations != null) {
                // Register given cacheable Hibernate mapping definitions, read from the file system.
                for (Resource resource : this.cacheableMappingLocations) {
                    sfb.addCacheableFile(resource.getFile());
                }
            }
    
            if (this.mappingJarLocations != null) {
                // Register given Hibernate mapping definitions, contained in jar files.
                for (Resource resource : this.mappingJarLocations) {
                    sfb.addJar(resource.getFile());
                }
            }
    
            if (this.mappingDirectoryLocations != null) {
                // Register all Hibernate mapping definitions in the given directories.
                for (Resource resource : this.mappingDirectoryLocations) {
                    File file = resource.getFile();
                    if (!file.isDirectory()) {
                        throw new IllegalArgumentException(
                                "Mapping directory location [" + resource + "] does not denote a directory");
                    }
                    sfb.addDirectory(file);
                }
            }
    
            if (this.entityInterceptor != null) {
                sfb.setInterceptor(this.entityInterceptor);
            }
    
            if (this.namingStrategy != null) {
                sfb.setNamingStrategy(this.namingStrategy);
            }
    
            if (this.hibernateProperties != null) {
                sfb.addProperties(this.hibernateProperties);
            }
    
            if (this.annotatedClasses != null) {
                sfb.addAnnotatedClasses(this.annotatedClasses);
            }
    
            if (this.annotatedPackages != null) {
                sfb.addPackages(this.annotatedPackages);
            }
    
            if (this.packagesToScan != null) {
                sfb.scanPackages(this.packagesToScan);
            }
    
            if (this.jtaTransactionManager != null) {
                sfb.setJtaTransactionManager(this.jtaTransactionManager);
            }
    
            // Build SessionFactory instance.
            this.configuration = sfb;
            this.sessionFactory = buildSessionFactory(sfb);
        }
    
        /**
         * Subclasses can override this method to perform custom initialization
         * of the SessionFactory instance, creating it via the given Configuration
         * object that got prepared by this LocalSessionFactoryBean.
         * <p>The default implementation invokes LocalSessionFactoryBuilder's buildSessionFactory.
         * A custom implementation could prepare the instance in a specific way (e.g. applying
         * a custom ServiceRegistry) or use a custom SessionFactoryImpl subclass.
         * @param sfb LocalSessionFactoryBuilder prepared by this LocalSessionFactoryBean
         * @return the SessionFactory instance
         * @see LocalSessionFactoryBuilder#buildSessionFactory
         */
        protected SessionFactory buildSessionFactory(LocalSessionFactoryBuilder sfb) {
            return sfb.buildSessionFactory();
        }
    
        /**
         * Return the Hibernate Configuration object used to build the SessionFactory.
         * Allows for access to configuration metadata stored there (rarely needed).
         * @throws IllegalStateException if the Configuration object has not been initialized yet
         */
        public final Configuration getConfiguration() {
            if (this.configuration == null) {
                throw new IllegalStateException("Configuration not initialized yet");
            }
            return this.configuration;
        }
    
    
        public SessionFactory getObject() {
            return this.sessionFactory;
        }
    
        public Class<?> getObjectType() {
            return (this.sessionFactory != null ? this.sessionFactory.getClass() : SessionFactory.class);
        }
    
        public boolean isSingleton() {
            return true;
        }
    
    
        public void destroy() {
            this.sessionFactory.close();
        }
    
    }
    

    응용 프로그램 컨텍스트에서 bean을 정의하십시오.

    <bean id="multiTenantProvider" class="com.levitech.hibernate.MultiTenantConnectionProviderImpl" depends-on="myDataSource" lazy-init="false"></bean>
    <bean id="tenantIdResolver" class="com.levitech.hibernate.TenantIdResolver"></bean>
    
    <bean id="sessionFactory" class="com.levitech.hibernate.CustomLocalSessionFactoryBean" depends-on="liquibase, myDataSource, multiTenantProvider">
            <property name="dataSource" ref="myDataSource"></property>
            <property name="multiTenantConnectionProvider" ref="multiTenantProvider"></property>
            <property name="tenantIdResolver" ref="tenantIdResolver"></property>
    
             <property name="mappingLocations" value="classpath*:hibernate/**/*.hbm.xml" />
    
        <property name="hibernateProperties">
          <value>
            hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
            hibernate.show_sql=true
            hibernate.cache.region.factory_class=org.hibernate.cache.ehcache.EhCacheRegionFactory
            hibernate.cache.use_query_cache=true
            hibernate.cache.use_second_level_cache=true
            hibernate.multiTenancy=SCHEMA
              </value>
        </property>
          </bean>
    

    최대 절전 모드 속성에서 다음 값을 제공하지 마십시오. hibernate.tenant_identifier_resolver와 hibernate.multi_tenant_connection_provider

    당신은 모두 준비가되어 있고 당신의 콩은 모두 봄 관리를하고 있습니다. 다시 DI를 자유롭게 사용할 수 있습니다! 희망이 사람을 도움이됩니다. 나는 Jira의 기능 요청서를 작성했다.

  5. ==============================

    5.이 사람들의 반응과이 링크를 사용하여, 나는 스프링이나 C3P0 이외의 다른 것을 함께 사용하지 않았습니다.

    이 사람들의 반응과이 링크를 사용하여, 나는 스프링이나 C3P0 이외의 다른 것을 함께 사용하지 않았습니다.

    내 최대 절전 모드 설정에이 2 가지 속성을 추가해야했습니다.

    properties.setProperty("hibernate.multiTenancy", "SCHEMA");
    properties.setProperty("hibernate.multi_tenant_connection_provider", MultiTenantConnectionProviderImpl.class.getName());
    

    HibernateUtils.java

    import org.hibernate.HibernateException;
    import org.hibernate.Session;
    import org.hibernate.SessionFactory;
    import org.hibernate.cfg.Configuration;
    import org.hibernate.service.ServiceRegistry;
    import org.hibernate.service.ServiceRegistryBuilder;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    /**
     *
     * @author Alex
     */
    public class HibernateUtils {
        private static final Logger logger = LoggerFactory.getLogger(HibernateUtils.class);
        private static SessionFactory sessionFactory;
    
        static{
            init();
        }
    
        public static void init(){
            try {
                Configuration configuration = new Configuration()
                        .setProperties(ConnectionPropertiesUtils.getProperties());
    
                ServiceRegistry serviceRegistry = new ServiceRegistryBuilder().applySettings(configuration.getProperties()).buildServiceRegistry();
                sessionFactory = configuration.buildSessionFactory(serviceRegistry);
            } catch (Exception e) {
                logger.error(e.getMessage());
            }
        }
    
        public static Session getTenantSession(String tenant){
            return getSession(tenant);
        }
    
        public static Session getAuthSession(){
            return getSession("AUTH");
        }
    
        public static Session getLogSession(){
            return getSession("LOG");
        }
    
        public static Session getConfigSession(){
            return getSession("CONFIG");
        }
    
        public static Session getSession(String tenant)
                throws HibernateException {
            if(sessionFactory == null){
                init();
            }
            return sessionFactory.withOptions().tenantIdentifier(tenant).openSession();
        }
    
        @Deprecated
        public static Session getSession()
                throws HibernateException {
            if(sessionFactory == null){
                init();
            }
            return sessionFactory.openSession();
        }
    }
    

    그리고 MultiTenantConnectionProviderImpl.java

    import com.mchange.v2.c3p0.ComboPooledDataSource;
    import java.beans.PropertyVetoException;
    import java.sql.Connection;
    import java.sql.SQLException;
    import org.hibernate.HibernateException;
    import org.hibernate.service.UnknownUnwrapTypeException;
    import org.hibernate.service.jdbc.connections.spi.ConnectionProvider;
    import org.hibernate.service.jdbc.connections.spi.MultiTenantConnectionProvider;
    import org.hibernate.service.spi.Stoppable;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    /**
     * Simplistic implementation for illustration purposes showing a single
     * connection pool used to serve multiple schemas using "connection altering".
     * Here we use the T-SQL specific USE command; Oracle users might use the ALTER
     * SESSION SET SCHEMA command; etc.
     */
    public class MultiTenantConnectionProviderImpl implements MultiTenantConnectionProvider, Stoppable {
    
        private static Logger log = LoggerFactory.getLogger(MultiTenantConnectionProviderImpl.class);
        private ComboPooledDataSource cpds;
    
        public MultiTenantConnectionProviderImpl() throws PropertyVetoException {
            log.info("Initializing Connection Pool!");
    
            cpds = new ComboPooledDataSource("Example");
            cpds.setDriverClass(ConnectionPropertiesUtils.getProperty("hibernate.connection.driver_class"));
            cpds.setJdbcUrl(ConnectionPropertiesUtils.getProperty("hibernate.connection.url"));
            cpds.setUser(ConnectionPropertiesUtils.getProperty("hibernate.connection.username"));
            cpds.setPassword(ConnectionPropertiesUtils.getProperty("hibernate.connection.password"));
    
            log.info("Connection Pool initialised!");
        }
    
        @Override
        public Connection getAnyConnection() throws SQLException {
            log.debug("Get Default Connection:::Number of connections (max: busy - idle): {} : {} - {}", new int[]{cpds.getMaxPoolSize(), cpds.getNumBusyConnectionsAllUsers(), cpds.getNumIdleConnectionsAllUsers()});
            if (cpds.getNumConnectionsAllUsers() == cpds.getMaxPoolSize()) {
                log.warn("Maximum number of connections opened");
            }
            if (cpds.getNumConnectionsAllUsers() == cpds.getMaxPoolSize() && cpds.getNumIdleConnectionsAllUsers() == 0) {
                log.error("Connection pool empty!");
            }
            return cpds.getConnection();
        }
    
        @Override
        public Connection getConnection(String tenantIdentifier) throws SQLException {
            final Connection connection = getAnyConnection();
            try {
                //This is DB specific syntax. This work for MSSQL and MySQL
                //Oracle uses the ALTER SESSION SET SCHEMA command
                connection.createStatement().execute("USE " + tenantIdentifier);
            } catch (SQLException e) {
                throw new HibernateException("Could not alter JDBC connection to specified schema [" + tenantIdentifier + "]", e);
            }
            return connection;
        }
    
        @Override
        public void releaseAnyConnection(Connection connection) throws SQLException {
            connection.close();
        }
    
        @Override
        public void releaseConnection(String tenantIdentifier, Connection connection) {
            try {
                this.releaseAnyConnection(connection);
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
    
        @Override
        public boolean supportsAggressiveRelease() {
            return false;
        }
    
        @SuppressWarnings("rawtypes")
        @Override
        public boolean isUnwrappableAs(Class unwrapType) {
            return ConnectionProvider.class.equals(unwrapType) || MultiTenantConnectionProvider.class.equals(unwrapType) || MultiTenantConnectionProviderImpl.class.isAssignableFrom(unwrapType);
        }
    
        @SuppressWarnings("unchecked")
        @Override
        public <T> T unwrap(Class<T> unwrapType) {
            if (isUnwrappableAs(unwrapType)) {
                return (T) this;
            } else {
                throw new UnknownUnwrapTypeException(unwrapType);
            }
        }
    
        public void stop() {
            cpds.close();
        }
    }
    
  6. from https://stackoverflow.com/questions/16213573/setting-up-a-multitenantconnectionprovider-using-hibernate-4-2-and-spring-3-1-1 by cc-by-sa and MIT license