SSM框架实现连接多个数据库(set加事务失效),(注解加事务不失效)

96 阅读2分钟

首先在properties中配置多个数据源的连接信息 system.driver=com.mysql.jdbc.Driver system.url=jdbc:mysql://192.168.23.129:3306/qingcheng_system?characterEncoding=UTF-8 system.username=root system.password=962464110

goods.driver=com.mysql.jdbc.Driver goods.url=jdbc:mysql://192.168.23.129:3306/qingcheng_goods?characterEncoding=UTF-8 goods.username=root goods.password=962464110 然后再xml中配置连接:

<bean id="dataSource_system"
      class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
    <property name="username" value="${system.username}" />
    <property name="password" value="${system.password}" />
    <property name="driverClassName" value="${system.driver}" />
    <property name="url" value="${system.url}" />
</bean>
<bean id="dataSource_goods"
      class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
    <property name="username" value="${goods.username}" />
    <property name="password" value="${goods.password}" />
    <property name="driverClassName" value="${goods.driver}" />
    <property name="url" value="${goods.url}" />
</bean>

<bean id = "dataSource" class="com.six.pool.DynamicDataSource">
    <property name="targetDataSources">
        <map key-type="java.lang.String">
            <!--增加数据源-->
            <entry value-ref="dataSource_system" key="dataSource_system"></entry>
            <entry value-ref="dataSource_goods" key="dataSource_goods"></entry>
        </map>
    </property>
    <!--默认连接-->
    <property name="defaultTargetDataSource" ref="dataSource_goods" />
</bean>

编写工具类,和继承determinecurrentlookupkey public class DynamicDataSourceHolder { private static final ThreadLocal THREAD_DATA_SOURCE = new ThreadLocal();

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

public static void setDataSource(String dataSource)
{
    THREAD_DATA_SOURCE.set(dataSource);
}

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

}

public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() {

    // 从自定义的位置获取数据源标识
    return DynamicDataSourceHolder.getDataSource();
}

} 调用的时候直接set就可以了,例如 public List

findMenu() {

    DynamicDataSourceHolder.setDataSource("dataSource_system");
    //查找出一级菜单
    List<Menu> list = menuDao.findMenu();
    //查找二级菜单
    for (Menu menu : list) {
        String pid = menu.getPath();
        List<Menu> list2 = menuDao.findMenuByPid(pid);
        //查找三级菜单
        for (Menu menu1 : list2) {
            String pid1 = menu1.getPath();
            List<Menu> list3 = menuDao.findMenuByPid(pid1);
            menu1.setChildren(list3);
        }
        menu.setChildren(list2);

    }
    return list;
}

如果有多个方法需要设置其他的数据库,可以使用注解的方式:下面的代码是基于上面的代码书写,并且加入注解后,即使有事务控制,也不会失效: 写入自定义注解: package com.six.pool;

import java.lang.annotation.*;

import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.ElementType.TYPE; import static java.lang.annotation.RetentionPolicy.RUNTIME;

@Target({ TYPE, METHOD }) @Retention(RUNTIME) public @interface DataSource { String value(); } 注解使用类: package com.six.pool;

import org.aspectj.lang.JoinPoint; import org.aspectj.lang.reflect.MethodSignature;

import java.lang.reflect.Method;

public class DataSourceAspect {

/**
 * 拦截目标方法,获取由@DataSource指定的数据源标识,设置到线程存储中以便切换数据源
 *
 * @param
 * @throws Exception
 */
public void intercept(JoinPoint joinPoint) throws Exception {
    //获取切入点的类反射
    Class<?> targetClass=joinPoint.getTarget().getClass();
    //获取方法参数
    MethodSignature signature=(MethodSignature) joinPoint.getSignature();
    for(Class<?> clazz:targetClass.getInterfaces())
    {
        dealDataSource(clazz,signature.getMethod());
    }
    //默认使用目标类型的注解,如果没有则使用其实现的接口注解
    dealDataSource(targetClass, signature.getMethod());
}

/**
 * 提取目标对象方法注解和类型注解中的数据源标识
 *
 * @param clazz
 * @param method
 */
private void dealDataSource(Class<?> clazz, Method method) {
    try
    {
        //获取方法参数
        Class<?>[] paramTypeArr=method.getParameterTypes();
        //检查是否为MDataSource注解
        if(clazz.isAnnotationPresent(DataSource.class))
        {
            //获取注解对象
            DataSource dataSource=clazz.getAnnotation(DataSource.class);
            DynamicDataSourceHolder.setDataSource(dataSource.value());
        }
        //方法注解,可以覆盖类型注解
        Method m=clazz.getMethod(method.getName(), paramTypeArr);
        if(m!=null && m.isAnnotationPresent(DataSource.class))
        {
            DataSource dataSource=m.getAnnotation(DataSource.class);
            DynamicDataSourceHolder.setDataSource(dataSource.value());
        }
    } catch (Exception e)
    {
        System.out.println(clazz + ":" + e.getMessage());
    }
}

}

在xml中配置自定义切入点aop拦截方法:

<bean id="dataSourceAspect" class="com.six.pool.DataSourceAspect"></bean>
<aop:config>
    <aop:aspect ref="dataSourceAspect" id="myAspect">
        <aop:pointcut expression="execution(* com.six.service.serviceimpl.*.*(..))" id="pointcut"/>
        <aop:before pointcut-ref="pointcut" method="intercept"/>
    </aop:aspect>
</aop:config>

如果添加事务后,注解方法更换数据库也失效,可尝试控制事务的传播级别:(非必须) 作者不需要控制,依然可以使用注解的方式更换数据库 @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)