天天看點

SpringBoot中Spring JPA和Mybatis整合及多資料庫主從資料源配置

Spring JPA和Mybatis整合,首先添加必要的依賴

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
	<groupId>org.mybatis.spring.boot</groupId>
	<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.1.10</version>
</dependency>
           

假設我們項目需要用到兩個庫os和ol,這兩個庫分别又有主從庫,分離讀寫,這是我的配置,僅作示例

@Configuration
@EnableJpaRepositories(basePackages = { "com.luckly.mall.repository.os" }, entityManagerFactoryRef = "entityManagerFactoryOs", transactionManagerRef = "transactionManagerOs")
@EntityScan(basePackages = { "com.luckly.mall.model" })
@MapperScan(basePackages = "com.luckly.mall.dao.os", sqlSessionFactoryRef = "sqlSessionFactoryOs", sqlSessionTemplateRef = "sqlSessionTemplateOs")
@EnableTransactionManagement
public class DataSourceConfigOs {

    private static final Logger log = LoggerFactory.getLogger(DataSourceConfigOs.class);

    @Autowired
    private Environment         env;

    @Bean(name = "dbMasterOs")
    @Primary
    @ConfigurationProperties(prefix = "spring.datasource.os.master")
    public DataSource dbMaster() {
        return DruidDataSourceBuilder.create().build();
    }

    @Bean(name = "dbSlaveOs")
    @ConfigurationProperties(prefix = "spring.datasource.os.slave")
    public DataSource dbSlave() {
        return DruidDataSourceBuilder.create().build();
    }

    @Bean(name = "dataSourceOs")
    public DataSource dataSource() {
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        dynamicDataSource.setDefaultTargetDataSource(dbMaster());
        HashMap<Object, Object> dataSourceMap = new HashMap<>();
        dataSourceMap.put(DataSourceKey.DB_MASTER, dbMaster());
        dataSourceMap.put(DataSourceKey.DB_SLAVE, dbSlave());
        dynamicDataSource.setTargetDataSources(dataSourceMap);
        return dynamicDataSource;
    }
}
           

entityManagerFactory,entityManager,sqlSessionFactory,sqlSessionTemplate,JdbcTemplate, transactionManager的代碼沒有貼出來,各位用的時候可以直接在下面加。

dbMaster,dbSlave分别是os庫的讀寫庫,ol庫類似,直接再寫一個DataSourceConfigOl,把os改成ol就好了,

主從資料源讀寫分離的話會設計到資料源切換,下面是具體代碼,主要有五個類

//
public enum DataSourceKey {
    DB_MASTER,
    DB_SLAVE
}

//
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TargetDataSource {

    DataSourceKey dataSourceKey() default DataSourceKey.DB_MASTER;
}

//
public class DynamicDataSourceHolder {

    public static final ThreadLocal<DataSourceKey> holder;

    public static void setDataSource(final DataSourceKey dataSourceKey) {
        DynamicDataSourceHolder.holder.set(dataSourceKey);
    }

    public static DataSourceKey getDataSource() {
        return DynamicDataSourceHolder.holder.get();
    }

    public static void clearDataSource() {
        DynamicDataSourceHolder.holder.remove();
    }

    static {
        holder = new ThreadLocal<DataSourceKey>();
    }
}

//
public class DynamicDataSource extends AbstractRoutingDataSource {

    @Override
    protected Object determineCurrentLookupKey() {
        return DynamicDataSourceHolder.getDataSource();
    }

}

//
@Aspect
@Component
@Order(-1)
public class DynamicDataSourceAspect {

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

    @Before("@annotation(targetDataSource)")
    public void doBefore(JoinPoint joinPoint, TargetDataSource targetDataSource) {
        DataSourceKey dataSourceKey = targetDataSource.dataSourceKey();
        DynamicDataSourceHolder.setDataSource(dataSourceKey);
    }

    @After("@annotation(targetDataSource)")
    public void doAfter(JoinPoint joinPoint, TargetDataSource targetDataSource) {
        DataSourceKey dataSourceKey = targetDataSource.dataSourceKey();
        DynamicDataSourceHolder.clearDataSource();
    }
           

但這樣有個問題,必須在每個方法上添加@targetDataSource,是以稍微改一下

@Pointcut("execution(* com.luckly.mall.dao..*.*(..))")
    public void pointCut() {

    }


 @Before("pointCut()")
    public void before(final JoinPoint joinPoint) {
        final Object target = joinPoint.getTarget();
        final String method = joinPoint.getSignature().getName();
        final Class<?> classz = target.getClass();
        final Class<?>[] parameterTypes = ((MethodSignature) joinPoint.getSignature()).getMethod().getParameterTypes();

        try {
            final Method m = classz.getMethod(method, parameterTypes);
            if (m != null && m.isAnnotationPresent(TargetDataSource.class)) {
                TargetDataSource targetDataSource = m.getAnnotation(TargetDataSource.class);
                DynamicDataSourceHolder.setDataSource(targetDataSource.dataSourceKey());
                logger.info("current datasource : {}", targetDataSource.dataSourceKey());
            } else if (m.getAnnotation(TargetDataSource.class) == null) {
                    DynamicDataSourceHolder.setDataSource(DataSourceKey.DB_MASTER);
                    logger.info("current datasourcesd : {}", DataSourceKey.DB_MASTER);
                
            }
        } catch (Exception e) {

        }

    }
           

這樣有targetDataSource注解就走注解指定的,沒有就走主庫,當然在這裡你還可以通過切點,方法等進行比對或正則更細的處理,比如query開始的就走從庫,加個method.startsWith("query"),setDataSource(DataSourceKey.DB_SLAVE)

繼續閱讀