spring+mybatis多数据源配置

(1)单数据源配置

<bean id="dataSource" class="org.logicalcobwebs.proxool.ExtendsProxoolDataSource">
        <property name="alias" value="project"></property>
        <property name="delegateProperties">
            <value>user=${jdbc_health.username},password=${jdbc_health.password}</value>
        </property>
        <property name="user" value="${jdbc_health.username}"/>
        <property name="password" value="${jdbc_health.password}"/>
        <property name="driver" value="${jdbc_health.driverClassName}"/>
        <property name="driverUrl" value="${jdbc_health.url}"/>           
        <property name="maximumConnectionCount" value="${jdbc_health.maximumConnectionCount}"></property>
        <property name="maximumActiveTime" value="${jdbc_health.maximumActiveTime}"></property>
        <property name="maximumConnectionLifetime" value="${jdbc_health.maximumConnectionLifetime}"></property>
        <property name="prototypeCount" value="${jdbc_health.prototypeCount}"></property>
        <property name="houseKeepingSleepTime" value="${jdbc_health.houseKeepingSleepTime}"></property>
        <property name="simultaneousBuildThrottle" value="${jdbc_health.simultaneousBuildThrottle}"></property>
        <property name="houseKeepingTestSql" value="${jdbc_health.houseKeepingTestSql}"></property>
        <property name="verbose" value="${jdbc_health.verbose}"></property>
        <property name="statistics" value="${jdbc_health.statistics}"></property>
        <property name="statisticsLogLevel" value="${jdbc_health.statisticsLogLevel}"></property>
    </bean>

    <!--mybatis与Spring整合  -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="configLocation" value="classpath:SqlMapConfig.xml"/>
        <property name="dataSource" ref="dataSource"/> 
        <property name="plugins">
            <array>
                <ref bean="offsetLimitInterceptor"/>
            </array>
        </property>
    </bean>

(2)多数据源配置

    <!-- 多数据远配置 -->
    <bean id="multipleDataSource" class="com.netease.haitao.common.datasource.MultipleDataSource" primary="true">  
        <property name="targetDataSources">  
            <map key-type="java.lang.String">  
                <entry value-ref="dataSource" key="dataSource"></entry>  
                <entry value-ref="readOnlyDataSource" key="readOnlyDataSource"></entry>  
            </map>  
        </property>  
        <property name="defaultTargetDataSource" ref="dataSource"/>
    </bean>  

    <bean id="dataSource" class="org.logicalcobwebs.proxool.ExtendsProxoolDataSource" primary="false">
        <property name="alias" value="travel"></property>
        <property name="delegateProperties">
            <value>user=${jdbc_health.username},password=${jdbc_health.password}</value>
        </property>
        <property name="user" value="${jdbc_health.username}"/>
        <property name="password" value="${jdbc_health.password}"/>
        <property name="driver" value="${jdbc_health.driverClassName}"/>
        <property name="driverUrl" value="${jdbc_health.url}"/>           
        <property name="maximumConnectionCount" value="${jdbc_health.maximumConnectionCount}"></property>
        <property name="maximumActiveTime" value="${jdbc_health.maximumActiveTime}"></property>
        <property name="maximumConnectionLifetime" value="${jdbc_health.maximumConnectionLifetime}"></property>
        <property name="prototypeCount" value="${jdbc_health.prototypeCount}"></property>
        <property name="houseKeepingSleepTime" value="${jdbc_health.houseKeepingSleepTime}"></property>
        <property name="simultaneousBuildThrottle" value="${jdbc_health.simultaneousBuildThrottle}"></property>
        <property name="houseKeepingTestSql" value="${jdbc_health.houseKeepingTestSql}"></property>
        <property name="verbose" value="${jdbc_health.verbose}"></property>
        <property name="statistics" value="${jdbc_health.statistics}"></property>
        <property name="statisticsLogLevel" value="${jdbc_health.statisticsLogLevel}"></property>
    </bean>

    <!-- 备库数据源 -->
    <bean id="readOnlyDataSource" class="org.logicalcobwebs.proxool.ExtendsProxoolDataSource" primary="false">
        <property name="alias" value="readOnlyTravel"></property>
        <property name="delegateProperties">
            <value>user=${jdbc_health.readonly.username},password=${jdbc_health.readonly.password}</value>
        </property>
        <property name="user" value="${jdbc_health.username}"/>
        <property name="password" value="${jdbc_health.password}"/>
        <property name="driver" value="${jdbc_health.driverClassName}"/>
        <property name="driverUrl" value="${jdbc_health.readonly.url}"/>           
        <property name="maximumConnectionCount" value="${jdbc_health.maximumConnectionCount}"></property>
        <property name="maximumActiveTime" value="${jdbc_health.maximumActiveTime}"></property>
        <property name="maximumConnectionLifetime" value="${jdbc_health.maximumConnectionLifetime}"></property>
        <property name="prototypeCount" value="${jdbc_health.prototypeCount}"></property>
        <property name="houseKeepingSleepTime" value="${jdbc_health.houseKeepingSleepTime}"></property>
        <property name="simultaneousBuildThrottle" value="${jdbc_health.simultaneousBuildThrottle}"></property>
        <property name="houseKeepingTestSql" value="${jdbc_health.houseKeepingTestSql}"></property>
        <property name="verbose" value="${jdbc_health.verbose}"></property>
        <property name="statistics" value="${jdbc_health.statistics}"></property>
        <property name="statisticsLogLevel" value="${jdbc_health.statisticsLogLevel}"></property>
    </bean>

  <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="configLocation" value="classpath:SqlMapConfig.xml"/>
        <property name="dataSource" ref="multipleDataSource"/>
        <property name="plugins">
            <array>
                <ref bean="paginationInterceptor"/>
                <ref bean="rowBoundPaginationInterceptor"/>
            </array>
        </property>
    </bean>

    <!-- 从com.netease.health.dao中查找有Repository注解的接口,实例化为Mybatis的实现类并注册到Spring容器中-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.netease.health.dao" />
        <property name="annotationClass" value="org.springframework.stereotype.Repository"/>
    </bean>

<!-- 自定义aop -->
<aop:aspectj-autoproxy/>

(3)multipleDataSource实现

/**
 * 
* @ClassName: MultipleDataSource 
* @Description: 多数据源 
* @date 2015年8月26日 下午4:28:07 
*
 */
public class MultipleDataSource extends AbstractRoutingDataSource {
    private static final ThreadLocal<String> dataSourceKey = new ThreadLocal<String>();

    /**
     * 
     * @Description: 设置当前线程的数据源
     * @date 2015年8月26日 下午4:29:01 
     * @param dataSource
     */
    public static void setDataSourceKey(String dataSource) {
        dataSourceKey.set(dataSource);
    }

    @Override
    protected Object determineCurrentLookupKey() {
        return dataSourceKey.get();
    }

    public static void removeDataSourceKey() {
        dataSourceKey.remove();
    }
}

(4)dao定义

public interface MySqlMapper {
    @Select("select * from MyTable")
    List<Map<String,Object>> getList();
}

public interface SqlServerMapper {
        @Select("select * from MyTable")
        List<Map<String,Object>> getList();
}

(5)使用SpringAOP方式实现自动切换

/**
 * 
* @ClassName: DataSourceSelect 
* @Description: 数据源选择注解
* @date 2015年8月26日 下午4:45:54 
*
 */
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSourceSelect {
    /**
     * 
     * @Description: 数据源值,默认为主库
     * @date 2015年8月26日 下午4:49:05 
     * @return
     */
    DataSourceEnum value() default DataSourceEnum.READ_WRITE_DATA_SOURCE;
}
/**
 * 
* @ClassName: MultipleDataSourceAspectAdvice 
* @Description: 数据源切面
* @date 2015年8月26日 下午6:47:09 
*
 */
@Aspect
@Component
public class MultipleDataSourceAspectAdvice {

     @Pointcut("@annotation(com.netease.haitao.common.annotation.DataSourceSelect) && @annotation(org.springframework.web.bind.annotation.RequestMapping)") 
     public void dataSourceSelect() {} 

    /**
     * 
     * @Description: 在加了DataSourceSelect注解的方法前设置数据源
     * @date 2015年8月26日 下午6:47:17 
     * @param pjp
     * @throws Throwable
     */
    @Around(value="dataSourceSelect() && @annotation(selectAnnotation)")
    public Object changeDataSource(ProceedingJoinPoint pjp, DataSourceSelect selectAnnotation) throws Throwable {
        MultipleDataSource.setDataSourceKey(selectAnnotation.value().getName());
        try {
            return pjp.proceed();
        } finally {
            MultipleDataSource.removeDataSourceKey();
        }
    }
}

枚举数据库

/**
 * 
* @ClassName: DataSourceEnum 
* @Description: 多数据远枚举
* @date 2015年8月26日 下午4:59:51 
*
 */
public enum DataSourceEnum {
    /**
     * 读写数据源
     */
    READ_WRITE_DATA_SOURCE("dataSource"),

    /**
     * 只读数据源
     */
    READ_ONLY_DATA_SOURCE("readOnlyDataSource");

    private String name;

    private DataSourceEnum(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}

(6)调用方法

@RequestMapping(value="/manage", params="opType=orderlist")
    @ResponseBody
    @DataSourceSelect(DataSourceEnum.READ_ONLY_DATA_SOURCE)
    public ResultInfo orderlist(String opType, HttpServletRequest request) {
        .......
    }

(7)说明
这里就上面的实现做个简单解释,在我们配置单数据源时可以看到数据源类型使用了org.logicalcobwebs.proxool.ExtendsProxoolDataSource,而这个代码实现了javax.sql.DataSource接口
配置sqlSessionFactory时org.mybatis.spring.SqlSessionFactoryBean注入参数dataSource类型就是javax.sql.DataSource

实现多数据源的方法就是我们自定义了一个MultipleDataSource,这个类继承自AbstractRoutingDataSource,而AbstractRoutingDataSource继承自AbstractDataSource ,AbstractDataSource 实现了javax.sql.DataSource接口,所以我们的MultipleDataSource也实现了javax.sql.DataSource接口,可以赋值给sqlSessionFactory的dataSource属性

public abstract class AbstractRoutingDataSource extends AbstractDataSource implements InitializingBean {
}

public abstract class AbstractDataSource implements DataSource {
}

再来说下MultipleDataSource的实现原理,MultipleDataSource实现AbstractRoutingDataSource抽象类,然后实现了determineCurrentLookupKey方法,这个方法用于选择具体使用targetDataSources中的哪一个数据源

<!-- 多数据远配置 -->
    <bean id="multipleDataSource" class="com.netease.haitao.common.datasource.MultipleDataSource" primary="true">  
        <property name="targetDataSources">  
            <map key-type="java.lang.String">  
                <entry value-ref="dataSource" key="dataSource"></entry>  
                <entry value-ref="readOnlyDataSource" key="readOnlyDataSource"></entry>  
            </map>  
        </property>  
        <property name="defaultTargetDataSource" ref="dataSource"/>
    </bean>  

可以看到Spring配置中multipleDataSource设置了两个属性defaultTargetDataSource和targetDataSources,这两个属性定义在AbstractRoutingDataSource,当MyBatis执行查询时会先选择数据源,选择顺序时现根据determineCurrentLookupKey方法返回的值到targetDataSources中去找,若能找到怎返回对应的数据源,若找不到返回默认的数据源defaultTargetDataSource,具体参考AbstractRoutingDataSource的源码

public abstract class AbstractRoutingDataSource extends AbstractDataSource implements InitializingBean {

    private Map<Object, Object> targetDataSources;

    private Object defaultTargetDataSource;


    /**
     * Retrieve the current target DataSource. Determines the
     * {@link #determineCurrentLookupKey() current lookup key}, performs
     * a lookup in the {@link #setTargetDataSources targetDataSources} map,
     * falls back to the specified
     * {@link #setDefaultTargetDataSource default target DataSource} if necessary.
     * @see #determineCurrentLookupKey()
     */
    protected DataSource determineTargetDataSource() {
        Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");
        Object lookupKey = determineCurrentLookupKey();
        DataSource dataSource = this.resolvedDataSources.get(lookupKey);
        if (dataSource == null && (this.lenientFallback || lookupKey == null)) {
            dataSource = this.resolvedDefaultDataSource;
        }
        if (dataSource == null) {
            throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");
        }
        return dataSource;
    }

    /**
     * Determine the current lookup key. This will typically be
     * implemented to check a thread-bound transaction context.
     * <p>Allows for arbitrary keys. The returned key needs
     * to match the stored lookup key type, as resolved by the
     * {@link #resolveSpecifiedLookupKey} method.
     */
    protected abstract Object determineCurrentLookupKey();
  
  .............

}

在动态切换数据源方法时选择了AOP方式实现,这里实现的简单粗暴,具体应用时根据实际需要灵活变通吧

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SpringMyBatis是两个非常流行的Java开发框架,可以很好地支持双数据源事务。 在Spring配置多数据源,我们需要使用`@Configuration`注解将Java类标记为配置类,并通过`@Bean`注解来声明并注册多个数据源。对于每个数据源,我们需要配置对应的数据源对象、事务管理器和MyBatis的SqlSessionFactoryBean。然后,使用`@Primary`注解标记一个数据源为主数据源,它将作为默认的数据源。 接下来,在事务的配置中,我们需要使用`@EnableTransactionManagement`注解开启Spring的事务管理功能,并使用`@Transactional`注解来标记需要进行事务管理的方法。在方法内部,我们可以使用`TransactionTemplate`或`PlatformTransactionManager`进行事务的控制。 在使用MyBatis时,我们可以通过`@MapperScan`注解来自动扫描并注册MyBatis的Mapper接口。在进行事务配置时,可以使用`@Transactional`注解来标记需要进行事务管理的方法,从而确保在执行数据库操作时,能够正确地开启、提交或回滚事务。 在使用双数据源时,我们需要在方法或类的上方使用`@Transactional(value = "transactionManager")`注解来指定需要使用哪个数据源的事务管理器。这样,在执行相关操作时,Spring就会根据指定的数据源来管理事务,保证数据的一致性。 总结一下,SpringMyBatis提供了完善的支持来实现双数据源事务。我们可以通过合理的配置和注解来实现多个数据源的管理,并使用事务注解来控制事务的开启、提交和回滚。这样,就可以在一个应用程序中同时访问和操作多个数据源,保证数据的一致性和完整性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值