spring+hibernate+mysql实现主从数据库动态切换

注明:项目采用spring+springMVC+hibernate框架,mysql数据库,配置采用javaConfig
本文实现的是mysql的一主一从配置,仅仅限于学习,用于项目中还需要很多处理。
版本:spring:4.2.5,springMVC:4.2.5,hibernate:5.2.4,AspectJ:1.8.9
一、配置mysql主从库

这个就不多说了,网上一搜一大堆,也不是本文的重点。

二、hibernate配置

@Configuration
@EnableTransactionManagement
@ComponentScan(value = {"com.service", "com.dao"})
@PropertySources(value = {@PropertySource("classpath:database.properties")})
public class HibernateConfig {
    @Autowired
    private Environment env;
    @Bean
    public DataSource masterDatasource() {
        BasicDataSource dataSource = new BasicDataSource();
        dataSource.setDriverClassName(env.getProperty("master.jdbc.driverClassName"));
        dataSource.setUrl(env.getProperty("master.jdbc.url"));
        dataSource.setUsername(env.getProperty("master.jdbc.username"));
        dataSource.setPassword(env.getProperty("master.jdbc.password"));
        //省去繁琐的配置
        return dataSource;
    }

    @Bean
    public DataSource slaveDatasource() {
        BasicDataSource dataSource = new BasicDataSource();
        dataSource.setDriverClassName(env.getProperty("slave.jdbc.driverClassName"));
        dataSource.setUrl(env.getProperty("slave.jdbc.url"));
        dataSource.setUsername(env.getProperty("slave.jdbc.username"));
        dataSource.setPassword(env.getProperty("slave.jdbc.password"));
        //...
        return dataSource;
    }

    @Bean
    public DataSource dataSource() {
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        //默认设置为从库,因为查询需求比增删改要多;这里可以根据项目需求更改(多从或多主结构)
        dynamicDataSource.setDefaultTargetDataSource(slaveDatasource());
        Map targetDataSources = new HashMap();
        targetDataSources.put("master", masterDatasource());
        //接下来设置主库,注意:master这个名字要和contextHolder的set的名字一样
        dynamicDataSource.setTargetDataSources(targetDataSources);
        return dynamicDataSource;
    }


    @Bean
    public LocalSessionFactoryBean SessionFactory() {
        LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
        sessionFactory.setDataSource(dataSource());
        //...
        return sessionFactory;
    }

    @Bean
    public PlatformTransactionManager transactionManager() {
        HibernateTransactionManager transactionManager = new HibernateTransactionManager();
        transactionManager.setSessionFactory(SessionFactory().getObject());
        return transactionManager;
    }

}

注明:配置好master和slave;至于slave下面的datasource这是一个总的数据源,hibernate的sessionFactory只和这个datasource关联,具体调用哪个数据源有datasource来决定。

DynamicDataSource:

public class DynamicDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        return DataSourceContextHolder.getType();
    }
}

AbstractRoutingDataSource是spring管理多数据源的一个抽象类。

DataSourceContextHolder:

public class DataSourceContextHolder {
    //当前线程绑定的数据源
    private static final ThreadLocal contextHolder = new ThreadLocal<>();
    public static void master() {
        contextHolder.set("master");
    }
    public static String getType() {
        return contextHolder.get();
    }
    public static void remove() {
        contextHolder.remove();
    }
}

三、aop切面

@Aspect
public class DynamicChangeDataSourceAop {
    @Pointcut("execution(* com.dao.impl.*.save*(..)) " +
            "|| execution(* com.dao.impl.*.add*(..)) " +
            "|| execution(* com.dao.impl.*.update*(..)) " +
            "|| execution(* com.dao.impl.*.change*(..)) " +
            "|| execution(* com.dao.impl.*.delete*(..)) " +
            "|| execution(* com.dao.impl.*.del*(..))")
    public void masterPointCut() {
    }
    @Around("masterPointCut()")
    public void matserDataSource(ProceedingJoinPoint joinPoint) {
        try {
            DataSourceContextHolder.master();
            System.out.println("----切换到主库----");
            joinPoint.proceed();
            DataSourceContextHolder.remove();
        } catch (Throwable throwable) {
            throwable.printStackTrace();    //出错了
        }
    }
}

首先是masterPointCut方法,定义的切点为:返回值不限,com.dao.impl下所有类以save/add/update/change/delete/del开头的方法都要进入该切面,参数不限。
然后在调用proceed()之前切换到了主库。
四、配置Aop

@Configuration
@EnableAspectJAutoProxy
@ComponentScan(basePackages = {"com.aop"})
public class AopConfig {
    @Bean
    public DynamicChangeDataSourceAop dynamicChangeDataSourceAop() {
        return new DynamicChangeDataSourceAop();
    }
}

在spring中创建aop切面的bean。

到此就完成了主从库的动态切换了。

你可能感兴趣的:(Spring)