springMvc结合hibernate多数据源和多事务管理器

这个示例使用的spring 版本是4.3.3,hibernate版本是4.1.12,ide是idea2016,数据库是mysql和sqlserver2008。

要到达的效果是:

1、可以使用注解在服务层选择数据源@DataSource

2、使用事务注解@Transactional选择不同的事务管理器

动态切换数据源确切的来说是在同一类型数据库的情况下的。意思就是说 , 在系统中的使用的数据库分布在多台数据库服务器或者在同台服务器上的多个数据库. 在运行时期间根据某种标识符来动态的选择当前操作的数据库.
     1.数据源是相同类型的数据库: 一个SessionFactory+动态数据源+一个事务管理器
     2.数据源是不同类型的数据库: 根据类型配置多套SessionFactory


一、这里要配置三个数据源,每个数据库都只有一张表,每张表都只有id和name这两列。

sqlserver的数据源

@Bean
public DataSource sqlServerSource() {
    BasicDataSource ds = new BasicDataSource();
    ds.setDriverClassName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
    ds.setUrl("jdbc:sqlserver://localhost:1433;DatabaseName=testHibernate;SelectMethod=Cursor");
    ds.setUsername("sa");
    ds.setPassword("sa123");
    return ds;
}

mysql数据源分别是两个不同的数据库

@Bean
public DataSource dataSource() {
    BasicDataSource ds = new BasicDataSource();
    ds.setDriverClassName("com.mysql.jdbc.Driver");
    ds.setUrl("jdbc:mysql://localhost:3306/myspittles");
    ds.setUsername("root");
    ds.setPassword("root");
    return ds;
}

@Bean
public DataSource dataSourceHi2() {
    BasicDataSource ds = new BasicDataSource();
    ds.setDriverClassName("com.mysql.jdbc.Driver");
    ds.setUrl("jdbc:mysql://localhost:3306/hiberate2");
    ds.setUsername("root");
    ds.setPassword("root");
    return ds;
}

二、SessionFactory工厂配置两个一个是myslq的作为默认的SessionFactory,一个是sqlserver的:

mysql  SessionFactory:

@Bean
public SessionFactory sessionFactoryMySql() {
    try {
        LocalSessionFactoryBean lsfb = new LocalSessionFactoryBean();
        lsfb.setDataSource(dynamicDataSource());
        lsfb.setPackagesToScan("com.model");//扫描时实体类所在的路径
        Properties props = new Properties();
        props.setProperty("dialect", "org.hibernate.dialect.MySQL5Dialect");//hibernate对mysql的方言
        props.setProperty("show_sql", "true");
        props.setProperty("current_session_context_class", "thread");
        lsfb.setHibernateProperties(props);
        lsfb.afterPropertiesSet();
        SessionFactory object = lsfb.getObject();
        return object;
    } catch (IOException e) {
        return null;
    }
}

sqlserver SessionFactory:这个是在事务中进行切换

public SessionFactory sessionFactorySqlServer() {
    try {
        LocalSessionFactoryBean lsfb = new LocalSessionFactoryBean();
        lsfb.setDataSource(dynamicDataSource());
        lsfb.setPackagesToScan("com.model");//扫描时实体类所在的路径
        Properties props = new Properties();
        props.setProperty("dialect", "org.hibernate.dialect.SQLServer2008Dialect");//hibernate对sqlserver的方言
        props.setProperty("show_sql", "true");
        props.setProperty("current_session_context_class", "thread");
        lsfb.setHibernateProperties(props);
        lsfb.afterPropertiesSet();
        SessionFactory object = lsfb.getObject();
        return object;
    } catch (IOException e) {
        return null;
    }
}

对应有两个事务管理器,一个是默认事务管理器,一个是sqlserver的事务管理器:

默认事务管理器(mysql):

/**
 * myqls数据库的事务管理器
 *
 * @return
 */
@Bean
public PlatformTransactionManager annotation1() {
    System.out.println(sessionFactoryMySql());
    HibernateTransactionManager transactionManager = new HibernateTransactionManager();
    transactionManager.setSessionFactory(sessionFactoryMySql());
    return transactionManager;
}
sqlserver的事务管理器:在使用sqlserver数据库的时侯 @Transactional(value = "SqlServer")就能选择事务

/**
 * SqlServer数据库的事务管理器
 *
 * @return
 */
@Bean(name = "SqlServer")
public PlatformTransactionManager annotation2() {
    System.out.println(sessionFactorySqlServer());
    HibernateTransactionManager transactionManager = new HibernateTransactionManager();
    transactionManager.setSessionFactory(sessionFactorySqlServer());
    return transactionManager;
}

配置动态数据源:

@Bean
public DynamicDataSource dynamicDataSource() {
    DynamicDataSource source = new DynamicDataSource();
    source.setDefaultTargetDataSource(dataSource());
    Map<Object, Object> targetDataSources = new HashMap<Object, Object>();
    targetDataSources.put("hi1", dataSourceHi1());
    targetDataSources.put("hi2", dataSourceHi2());
    targetDataSources.put("sqlServer", sqlServerSource());
    targetDataSources.put("oracle", oracleDataSource());
    source.setTargetDataSources(targetDataSources);
    return source;
}

二、数据源注解

声明数据源注解类:DataSource.java,java注解请:百度一下

/**
 * Created by IBM on 2018/4/20.
 */
@Target({ElementType.METHOD})//这个注解是应用在方法上
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface DataSource {
    /**
     * 数据源的标识
     * @return
     */
    String value() default "";
}
注解解析器类:
/**
 * 数据源注解解析器
 * 这个解析器的主要功能,是解析目标方法上如果有DataSource注解,那么解析出这个注解中的value值(权限的值)
 * Created by IBM on 2018/4/20.
 */
public class DataSourcesParser {
    public static String parse(Class targetClass,String method)throws Exception{
        /**
         * Class对象的getMethods和getDeclaredMethods都是获取类对象的方法
         * getMethod():获取当前类及所有继承的父类的public修饰的方法。仅包括public
         * getDeclaredMethod():获取当前类的所有方法,包括public/private/protected/default修饰的方法。
         */
        String methodAccess="";
        //Method[]methods=targetClass.getMethods();
        Method[]methods1=targetClass.getDeclaredMethods();
        for(Method m:methods1){
            if(m.getName()==method){
                if(m.isAnnotationPresent(DataSource.class)){
                    //得到方法上的注解
                    DataSource dataSource=m.getAnnotation(DataSource.class);
                    methodAccess=dataSource.value();
                }
            }
        }
        return methodAccess;
    }
}

这样就能使用@DataSource注解了。

三、定义aop切面类来解析和切换数据源

切面配置类:AspectConfig.java

/**
 * Created by IBM on 2018/2/24.
 */
@Configuration
@EnableAspectJAutoProxy
public class AspectConfig {

    @Bean
    public DataSourceAspect dataSourceAspect(){
        return new DataSourceAspect();
    }

}

切面类:

/**
 * Created by IBM on 2018/4/20.
 * 使用注解创建切面
 * 定义切面
 */
@Aspect
@Order(2)//切面的执行顺序,使他能够在事务启动前执行
@Component
public class DataSourceAspect {

    // 这里是关键点,把切面的连接点放在了我们的注解上
    @Pointcut("@annotation(com.config.annontionConfig.DataSource)")
    public void controllerAspect() {

    }
    // 在这里定义前置切面

    @Before("controllerAspect()")
    public void beforeMethod(JoinPoint joinPoint) throws Throwable {
        // 这里执行保存日志的动作
        /**
         * 1.获取访问目标方法应该具备的权限
         *  为解析目标方法的PrivilegeInfo注解,根据我们定义的解析器,需要得到:目标类的class形式 方法的名称
         */
        Class targetClass = joinPoint.getTarget().getClass();
        String methodName = joinPoint.getSignature().getName();
        //得到该方法的访问权限
        String methodAccess = DataSourcesParser.parse(targetClass, methodName);//获取数据源注解解析器解析出来的数据源
        DynamicDataSourceHolder.setDataSource(methodAccess);//切换数据源
        String a=  DynamicDataSourceHolder.getDataSource();
        System.out.println("......"+methodAccess+"......");
    }
    //@After("controllerAspect()")
    public void afterMethod(JoinPoint joinPoint)throws Throwable{
        // 这里执行保存日志的动作
        /**
         * 1.获取访问目标方法应该具备的权限
         *  为解析目标方法的PrivilegeInfo注解,根据我们定义的解析器,需要得到:目标类的class形式 方法的名称
         */
        Class targetClass = joinPoint.getTarget().getClass();
        String methodName = joinPoint.getSignature().getName();
        //得到该方法的访问权限
        String methodAccess = DataSourcesParser.parse(targetClass, methodName);
        System.out.println("......"+methodAccess+"......");
    }
}
动态数据源类:

/**
 * Created by IBM on 2018/2/7.
 */
public class DynamicDataSource extends AbstractRoutingDataSource {
    /**
     * 获得数据源
     */
    @Override
    protected Object determineCurrentLookupKey() {
        //
        return DynamicDataSourceHolder.getDataSource();
    }
}
/**
 * 创建DynamicDataSourceHolder用于持有当前线程中使用的数据源标识
 * Created by IBM on 2018/2/7.
 */
public class DynamicDataSourceHolder {
    /**
     * 注意:数据源标识保存在线程变量中,避免多线程操作数据源时互相干扰
     */
    private static final ThreadLocal<String> THREAD_DATA_SOURCE = new ThreadLocal<String>();

    // 获取数据源类型
    public static String getDataSource() {
        return THREAD_DATA_SOURCE.get();
    }
    // 设置数据源类型
    public static void setDataSource(String dataSource) {
        THREAD_DATA_SOURCE.set(dataSource);
    }
    // 清除数据源类型
    public static void clearDataSource() {
        THREAD_DATA_SOURCE.remove();
    }
}

有了上面这些就能够在服务层中切换不同的数据源:

sqlserver数据库

@DataSource(value = "sqlServer")
@Override
@Transactional(value = "SqlServer")
public SqlServer findById(int id) {
    return repository.findById(id);
}

mysql 的hiberate2数据库

@DataSource("hi2")
@Override
@Transactional
public void save2(Hiber2 hiber2) {
    String a = DynamicDataSourceHolder.getDataSource();
    repository.save2(hiber2);
}

@DataSource("hi2")会在进入事务前先进入上面的切面,所以能够切换数据源。

切换了数据源后使用@Transactional(value = "SqlServer")切换事务,也就是切换hibernate。

项目的截图:


项目的源码:

https://download.csdn.net/download/u014572215/10370883点击打开链接




猜你喜欢

转载自blog.csdn.net/u014572215/article/details/80064611