ssm 多数据源 注解版本

application.xml 配置如下

XML 复制代码
<!-- 使用 DruidDataSource 数据源 -->
 <bean id="primaryDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
 </bean>
<!-- 使用 数据源 1-->
 <bean id="logDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
 </bean>

 <!-- 配置动态加载数据源 -->
    <bean id="druidDynamicDataSource" class="com.iss.sso.utils.DynamicDataSource">
        <property name="defaultTargetDataSource" ref="primaryDataSource" />
        <property name="targetDataSources">
            <map>
                <entry key="primaryDataSource" value-ref="primaryDataSource"/>
                <entry key="scheduleDataSource" value-ref="logDataSource"/>
                
            </map>
        </property>
    </bean>
<!--3.配置SqlSessionFactory对象-->
 <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">

        <!--往下才是mybatis和spring真正整合的配置-->
        <!--注入数据库连接池-->
            <property name="dataSource" ref="druidDynamicDataSource"/>
        <!--
            配置mybatis全局配置文件:mybatis-config.xml
            指定Mybatis的配置文件位置。如果指定了该属性,
            那么会以该配置文件的内容作为配置信息构建对应的SqlSessionFactoryBuilder,
            但是后续属性指定的内容会覆盖该配置文件里面指定的对应内容
        -->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
        <!--
            扫描entity包,使用别名,多个用;隔开
            一般对应实体类所在的包,这个时候会自动取对应包中不包括包名的简单类名作为包括包名的别名。
            多个package之间可以用逗号或者分号等来进行分隔。(value的值一定要是包的全名)
        -->
        <property name="typeAliasesPackage" value="com.sso.**.domain"/>
        <!--
            扫描sql配置文件:mapper需要的xml文件
            Mapper文件存放的位置,当Mapper文件跟对应的Mapper接口处于同一位置的时候可以不用指定该属性的值
        -->
        <property name="mapperLocations" value="classpath:/mapper/*.xml"/>
     
    </bean>
-----     动态数据源
<bean id="dynamicDataSourceAspect" class="com.sso.utils.DynamicDataSourceAspect">
    </bean>
 <!--配置事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--引入数据源-->
        <property name="dataSource" ref="druidDynamicDataSource"/>
    </bean>

    <!-- 开启事务注解 -->
    <tx:annotation-driven transaction-manager="transactionManager"/>

 <!--定义事务增强,并制定事务管理器  -->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <!--设置传播行为-->
            <tx:method name="save*" propagation="REQUIRED" isolation="DEFAULT"/>
            <tx:method name="update*" propagation="REQUIRED" isolation="DEFAULT"/>
            <tx:method name="del*" propagation="REQUIRED" isolation="DEFAULT"/>
            <tx:method name="select*" propagation="SUPPORTS" isolation="DEFAULT" read-only="true"/>
            <tx:method name="*" propagation="REQUIRED" isolation="DEFAULT"/>
        </tx:attributes>
    </tx:advice>
 <aop:config proxy-target-class="true">
        <aop:pointcut id="myPointcut" expression="execution(* com.sso.*.dao.*.*(..))"/>
        <aop:advisor advice-ref="dynamicDataSourceAspect" pointcut-ref="myPointcut" order="1"/>
        <!--把事务控制在Service层-->
        <aop:advisor advice-ref="txAdvice" pointcut-ref="myPointcut" order="2"/>
    </aop:config>

动态数据源 核心类

java 复制代码
/**
 * 动态数据源加载
 *
 * @author
 */
public class DynamicDataSource extends AbstractRoutingDataSource {

    private static final Logger logger = LoggerFactory.getLogger(DynamicDataSource.class);

    /**
     * 数据源标识,保存在线程变量中,避免多线程操作数据源时互相干扰
     */
    private static final ThreadLocal<String> key = new ThreadLocal<String>();

    @Override
    protected Object determineCurrentLookupKey() {
        logger.debug("===当前数据源: {}===", key.get());
        return key.get();
    }

    /**
     * 设置数据源
     *
     * @param dataSource 数据源名称
     */
    public static void setDataSource(String dataSource) {
        key.set(dataSource);
    }

    /**
     * 获取数据源
     *
     * @return
     */
    public static String getDatasource() {
        return key.get();
    }

    /**
     * 清除数据源
     */
    public static void clearDataSource() {
        key.remove();
    }

}

spring aop 植入

java 复制代码
@Aspect
@Order(-10)
@Component
public class DynamicDataSourceAspect implements MethodBeforeAdvice, AfterReturningAdvice {

    Logger log = LoggerFactory.getLogger("切换数据源");

    /**
     * 目标方法正常完成后被织入,关闭数据源
     *
     * @param o
     * @param method
     * @param objects
     * @param o1
     * @throws Throwable
     */
    @Override
    public void afterReturning(Object o, Method method, Object[] objects, Object o1) {
        //这里做一个判断,有使用DataSourceAnnotation注解时才关闭数据源,有一个主要的数据源,就没有必要每次都去关闭
        if (method.isAnnotationPresent(DataSourceAnnotation.class)) {
            DynamicDataSource.clearDataSource();
            log.debug("数据源已关闭");
        }
    }

    /**
     * 拦截目标方法,获取由@DataSourceAnnotation指定的数据源标识,设置到线程存储中以便切换数据源
     */
    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        if (method.isAnnotationPresent(DataSourceAnnotation.class)) {
        String clazzName = method.getDeclaringClass().getName();
        String methodName = method.getName();
            log.info("{}.{} 准备切换数据源", clazzName, methodName, DynamicDataSource.getDatasource());
            DataSourceAnnotation dataSourceAnnotation = method.getAnnotation(DataSourceAnnotation.class);
            DynamicDataSource.setDataSource(dataSourceAnnotation.value());
            log.info("{}.{} 数据源切换为:{}", clazzName, methodName, DynamicDataSource.getDatasource());
        } else {
            DynamicDataSource.setDataSource(DataSourceAnnotation.PRIMARY);
        }
    }
}
相关推荐
better_liang3 小时前
每日Java面试场景题知识点之-消息队列MQ核心场景与实战
java·面试·kafka·消息队列·rabbitmq·rocketmq·mq
小江的记录本3 小时前
【JVM虚拟机】垃圾回收GC:四种引用类型:强引用、软引用、弱引用、虚引用(附《思维导图》+《面试高频考点清单》)
java·jvm·spring boot·后端·python·spring·面试
小马爱打代码4 小时前
Spring源码 第四篇:Spring 5 源码深度拆解:AOP 全流程核心原理
java·后端·spring
better_liang4 小时前
每日Java面试场景题知识点之-SpringBoot启动流程
java·面试·springboot·源码解析·启动流程
RyFit4 小时前
Java + AI 实战:Spring AI 从入门到企业级落地
java·人工智能·spring
ZhengEnCi5 小时前
01-如何监听接口调用情况?
java·spring boot·后端
JAVA面经实录9176 小时前
MyBatis学习体系
java·mybatis
java1234_小锋6 小时前
在 Spring AI 中如何实现函数调用(Function Calling)?请说明其基本原理和应用场景。
java·人工智能·spring
小马爱打代码7 小时前
Spring源码 第九篇:Spring 5 源码深度拆解 - Spring 事件驱动模型
java·后端·spring
ForgeAI码匠8 小时前
ForgeAdmin|Spring Boot 3 后台框架的自动配置设计:少写配置,多做组合
java·spring boot·后端