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>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章