spring+hibernate應用層讀寫分離

spring+hibernate應用層讀寫分離方案,是基於AbstractRoutingDataSource和AOP實現的。其中AbstractRoutingDataSource用於管理數據源並且根據key返回相應的數據源,AOP決定了什麼時候使用什麼數據源的key。

1、相關類的代碼實現:
1)DataSource 註解類,用來標註某個方法使用的數據源,不標註則使用默認的

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface DataSource {
    /**
     * key="read" 讀庫(從),key="write" 寫庫(主)
     * @return
     */
    public String key();
}

2)DynamicDataSource 類重寫determineCurrentLookupKey方法,返回數據源的key

public class DynamicDataSource extends AbstractRoutingDataSource {

    @Override
    protected Object determineCurrentLookupKey() {
        return DynamicDataSourceHolder.getDataSource();
    }
}

3)DynamicDataSourceHolder 類用於線程安全的保存數據源的key

public class DynamicDataSourceHolder {
    private static final ThreadLocal<String> dataSourceHolder = new ThreadLocal<>();

    public static void setDataSource(String dataSourceKey) {
        dataSourceHolder.set(dataSourceKey);
    }

    public static String getDataSource() {
        return dataSourceHolder.get();
    }

    public static void clearDataSource() {
        dataSourceHolder.remove();
    }
}

4)DynamicDataSourceAop ,設置攔截的方法層面,下面是攔截在service層,當該service執行之前,通過判斷方法有沒有DataSource註解,有則根據DataSource中的key設置數據源的key;service方法執行完畢清除數據源key的設置。
service層使用DataSource註解的原則是:當service裏面有寫方法時就不需要設置,默認使用寫庫,此時讀寫方法都在寫庫中完成;當service裏面沒有寫方法時就使用DataSource設置讀庫,讀方法在讀庫中完成。

@Component
@Aspect
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class DynamicDataSourceAop {
    /**
     * service層方法執行前選擇數據源
     * @param point
     */
    @Before("execution(* com.test.service..*.*(..))")
    public void before(JoinPoint point) {
        Object target = point.getTarget();
        String methodName = point.getSignature().getName();
        Class clazz = target.getClass();
        Class<?>[] parameterTypes = ((MethodSignature) point.getSignature()).getMethod().getParameterTypes();
        try {
            Method method = clazz.getMethod(methodName, parameterTypes);
            if (method != null && method.isAnnotationPresent(DataSource.class)) {
                DataSource data = method.getAnnotation(DataSource.class);
                DynamicDataSourceHolder.setDataSource(data.key());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**
     * service層方法執行完後清空數據源選擇,當找不到相應的key的數據源會使用默認的數據源
     * @param point
     */
    @After("execution(* com.test.service..*.*(..))")
    public void after(JoinPoint point) {
        DynamicDataSourceHolder.clearDataSource();
    }
}

2、spring配置文件

    <bean id="masterDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
        <property name="url" value="${masterconnection.url}" />
        <property name="username" value="${masterconnection.username}" />
        <property name="password" value="${masterconnection.password}" />
    </bean>

    <bean id="slaveDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
        <property name="url" value="${slaveconnection.url}" />
        <property name="username" value="${slaveconnection.username}" />
        <property name="password" value="${slaveconnection.password}" />
    </bean>

    <!-- 自定義動態數據源 -->
    <bean id="dataSource" class="com.test.dao.datasource.DynamicDataSource">
        <property name="targetDataSources">
            <map key-type="java.lang.String">
                <!-- 配置讀寫數據源 -->
                <entry value-ref="masterDataSource" key="write"></entry>
                <entry value-ref="slaveDataSource" key="read"></entry>
            </map>
        </property>
        <property name="defaultTargetDataSource" ref="masterDataSource"></property>
    </bean>

    <!-- classpath是指 WEB-INF文件夾下的classes目錄, classpath*:不僅包含class路徑,還包括jar文件中(class路徑)進行查找  -->
    <bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean" >
        <property name="dataSource" ref="dataSource"/>
        <property name="mappingLocations" value="classpath:com/test/dao/hbm/*.hbm.xml"/>
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQLInnoDBDialect</prop>
                <prop key="hibernate.show_sql">false</prop>
                <prop key="hibernate.format_sql">true</prop>
            </props>
        </property>
    </bean>

    <!-- 開啓註解事務 只對當前配置文件有效 -->
    <tx:annotation-driven transaction-manager="txManager"/>

    <bean id="txManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory"/>
    </bean>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章