복붙노트

[SPRING] 데이터 소스 연결 URL 런타임 변경하기

SPRING

데이터 소스 연결 URL 런타임 변경하기

나는 커넥션 풀링을 위해 스프링 + 하이버 네이트 + mysql과 c3p0을 사용하는 프로젝트에서 일하고있다.

현재 연결 풀의 속성은 src 외부에 정의 된 속성을 통해로드됩니다. (예 : $ {db_uname})

봄 콩을 만들 때 모든 것이 잘 시작됩니다.

우리가 연결 한 데이터베이스가 어떤 이유로 접근 할 수 없으며 호스트를 전환하려고합니다.

새 호스트에 연결하고 풀을 다시 초기화해야하는 콜백을 구현해야합니다.

기존 데이터 소스 / 연결 풀을 정상적으로 덮어 쓰는 방법에 대한 지침은 도움이 될 것입니다.

다음은 스프링 구성 파일의 모습입니다.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="
    http://www.springframework.org/schema/beans 
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/tx 
    http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
    http://www.springframework.org/schema/context 
    http://www.springframework.org/schema/context/spring-context-3.0.xsd
    http://www.springframework.org/schema/util
    http://www.springframework.org/schema/util/spring-util-3.0.xsd">

<!-- Component scans -->
<import resource="component-scans-1.xml" />
<import resource="component-scans-2.xml" />


<util:properties id="serviceManagerProperties" 
    location="classpath:servicemanagers.properties" />

<!-- Properties file -->
<context:property-placeholder location="classpath:database.config.properties,classpath:framework.properties" />

<!-- context:annotation-config / -->
<tx:annotation-driven transaction-manager="transactionManager" />

<bean id="dataSource"
    class="org.springframework.jdbc.datasource.DriverManagerDataSource">
</bean>

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>

<context:mbean-server id="mbeanServer" />
<context:mbean-export server="mbeanServer" default-domain="a.b.c" />

<bean id="cacheManager" factory-method="getInstance"
    class="net.sf.ehcache.CacheManager" />

<bean class="net.sf.ehcache.management.ManagementService" init-method="init">
    <constructor-arg ref="cacheManager" />
    <constructor-arg ref="mbeanServer" />
    <constructor-arg value="false" />
    <constructor-arg value="false" />
    <constructor-arg value="false" />
    <constructor-arg value="true" />
</bean>

<bean id="entityManagerFactory"
    class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="jpaVendorAdapter" ref="jpaAdapter" />
    <property name="persistenceXmlLocation" value="classpath*:META-INF/framework-persistence.xml" />
    <property name="persistenceUnitName" value="PU-NAME" />
    <property name="jpaProperties">
        <props>
            <prop key="hibernate.format_sql">true</prop>
            <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
            <prop key="hibernate.cache.use_query_cache">${hibernate.cache.use_query_cache}</prop>
            <prop key="hibernate.cache.use_second_level_cache">${hibernate.cache.use_second_level_cache}</prop>
            <prop key="hibernate.cache.region.factory_class">
                org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory
            </prop>

            <prop key="hibernate.ejb.cfgfile">hibernate.cfg.xml</prop>
            <prop key="hibernate.generate_statistics">true</prop>


            <!-- CONNECTION SETTINGS -->
            <prop key="hibernate.connection.driver_class">
                com.mysql.jdbc.Driver
            </prop>
            <prop key="hibernate.connection.url">
                jdbc:mysql://${dbhost}/${dbschema}?zeroDateTimeBehavior=convertToNull&amp;useUnicode=true&amp;characterEncoding=UTF-8
            </prop>
            <prop key="hibernate.connection.username">${dbuser}</prop>
            <prop key="hibernate.connection.password">${dbpass}</prop>

            <!-- CONNECTION POOLING -->
            <prop key="hibernate.connection.provider_class">
                org.hibernate.connection.C3P0ConnectionProvider
            </prop>
            <prop key="hibernate.c3p0.maxPoolSize">${hibernate.c3p0.maxSize}</prop>
            <prop key="hibernate.c3p0.minPoolSize">${hibernate.c3p0.minSize}</prop>
            <prop key="hibernate.c3p0.acquireIncrement">${hibernate.c3p0.acquireIncrement}</prop>
            <prop key="hibernate.c3p0.idleConnectionTestPeriod">${hibernate.c3p0.idleTestPeriod}</prop>
            <prop key="hibernate.c3p0.maxStatements">${hibernate.c3p0.maxStatements}</prop>
            <prop key="hibernate.c3p0.timeout">${hibernate.c3p0.timeout}</prop>

        </props>
    </property>
</bean>

<bean id="jpaAdapter"
    class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />

데이터베이스 스키마가 올바르다 고 가정하면 이벤트에서 데이터베이스 자격 증명과 호스트 정보를 얻습니다. 연결 풀을 '재설정'해야합니다.

해결법

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

    1.AbstractRoutingDataSource는 좋은 선택입니다.

    AbstractRoutingDataSource는 좋은 선택입니다.

    이런 식으로 사용 된 XML 또는 주석 :

    <bean id="ds1" class="..c3p0.DataSource">
        ...
    </bean>
    
    <bean id="ds2" class="..c3p0.DataSource">
        ...
    </bean>
    
    <bean id="dataSource" class="..xxx.RoutingDataSource">
       <property name="targetDataSources">
          <map key-type="java.lang.String">
             <entry key="ds1" value-ref="ds1"/>
             <entry key="ds2" value-ref="ds2"/>             
          </map>
       </property>
       <property name="defaultTargetDataSource" ref="ds1"/>
    </bean>
    
    
    
    <bean id="sessionFactory"
        class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
        <property name="dataSource" ref="dataSource" /> 
    ...
    </bean>
    

    그런 다음 현재 데이터 소스를 결정하는 클래스를 작성하십시오.

    public class RoutingDataSource extends AbstractRoutingDataSource {
        private static final ThreadLocal<String> holder = new ThreadLocal<String>();
    
        protected Object determineCurrentLookupKey()
        {
            return holder.get();
        }
    
        public static void clear(){
            holder.remove();
        }
    
        public static void setDataSourceKey(String key){
            holder.set(key);
        }
    
    }
    

    그건 그렇고, 'try-finally'진술은 지루합니다!

    try{
        RoutingDataSource.setDataSourceKey("ds1");
        myDao.doXXX();
    }finally{
        RoutingDataSource.clear();
    }
    
  2. ==============================

    2.

    <beans:bean id="dataSource"
        class="org.springframework.aop.framework.ProxyFactoryBean">
        <beans:property name="targetSource" ref="swappableDataSource" />
    </beans:bean>
    
    <beans:bean id="dummyDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
        destroy-method="close" />
    
    <beans:bean name="swappableDataSource"
        class="org.springframework.aop.target.HotSwappableTargetSource">
        <beans:constructor-arg ref="dummyDataSource" />
    </beans:bean>
    

    일부에서는 코드에서이 작업을 수행 할 수 있습니다.

    @Autowired
    HotSwappableTargetSource swapable;
    
    public void changeDatasource() throws Exception
    {
                swapable.swap(createNewSource();
    
    }
    
    
    ComboPooledDataSource createNewSource() throws Exception {
        ComboPooledDataSource ds2 = new ComboPooledDataSource();
        ds2.setJdbcUrl(url);
        ds2.setDriverClass(driver);
        ds2.setUser(username);
        ds2.setPassword(password);
    
    
        return ds2;
    }
    
  3. ==============================

    3.만약 당신이 여러 데이터베이스 연결을 요구한다면 .. 그것은 여러가지 hibernate.cfg.file을 생성함으로써 최대 절전 모드로 가능하다.

    만약 당신이 여러 데이터베이스 연결을 요구한다면 .. 그것은 여러가지 hibernate.cfg.file을 생성함으로써 최대 절전 모드로 가능하다.

    다음 행을 통해이를 달성 할 수 있습니다.

    SessionFactory sf = new Configuration().configure("somename.cfg.xml").buildSessionFactory();
    

    기본 연결이 설정되지 않았 으면 다른 최대 절전 모드 구성을로드해야합니다 ... 그렇지 않으면 가능하지 않습니다.

  4. from https://stackoverflow.com/questions/14555611/changing-datasource-connection-url-runtime by cc-by-sa and MIT license