Springboot 实现mybatis+jpa+动态数据源

        本文介绍SpringBoot 框架中实现mybatis和jpa 同时实现多数据源。Springboot 版本1.4.0.Release 因为Springboot版本不到2.0 ,所以还是遇到了一些小挫折,不过最终解决。

1. yml文件配置多数据源

2. 动态数据源基础类 DynamicRoutingDataSource.java

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

/**
 * 动态数据源设置
 *
 * @date 2019-11-05
 */
public class DynamicRoutingDataSource extends AbstractRoutingDataSource {

    private final Logger logger = LoggerFactory.getLogger(getClass());

    /**
     * Set dynamic DataSource to Application Context
     *
     * @return
     */
    @Override
    protected Object determineCurrentLookupKey() {
        logger.debug("Current DataSource is [{}]", 
        DynamicDataSourceContextHolder.getDataSourceKey());
        return DynamicDataSourceContextHolder.getDataSourceKey();
    }
}

3.动态数据源上下文  DynamicDataSourceContextHolder.java

package com.hi.base.config.datasource;

import com.alibaba.ttl.TransmittableThreadLocal;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;

/**
 * 动态数据源上下文配置 DynamicDataSourceContextHolder.java
 *
 * @author lida
 * @date 2019-11-05
 */
public class DynamicDataSourceContextHolder {

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

    /**
     * Maintain variable for every thread, to avoid effect other thread
     */
    private static final ThreadLocal<String> CONTEXT_HOLDER = new TransmittableThreadLocal<>();

    /**
     * All DataSource List
     */
    public static List<Object> dataSourceKeys = new ArrayList<>();

    /**
     * To switch DataSource
     *
     * @param key the key
     */
    public static void setDataSourceKey(String key) {
        CONTEXT_HOLDER.set(key);
    }

    /**
     * Get current DataSource
     *
     * @return data source key
     */
    public static String getDataSourceKey() {
        return CONTEXT_HOLDER.get();
    }

    /**
     * To set DataSource as default
     */
    public static void clearDataSourceKey() {
        CONTEXT_HOLDER.remove();
    }

    /**
     * Check if give DataSource is in current DataSource list
     *
     * @param key the key
     * @return boolean boolean
     */
    public static boolean containDataSourceKey(String key) {
        return dataSourceKeys.contains(key);
    }
}

4. 多数据源配置类

import com.fasterxml.jackson.datatype.hibernate4.Hibernate4Module;
import com.hi.base.config.Constants;
import com.hi.base.config.liquibase.AsyncSpringLiquibase;
import com.zaxxer.hikari.HikariDataSource;
import liquibase.integration.spring.SpringLiquibase;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.liquibase.LiquibaseProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.env.Environment;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import javax.inject.Inject;
import javax.sql.DataSource;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

@Configuration
@EnableJpaRepositories(value = {"com.hi.base.repository", "com.hi.base.export.refrigerator.business.repository", "com.hi.base.export.airconditioner.business.repository", "com.hi.base.export.common.repository"})
@EnableJpaAuditing(auditorAwareRef = "springSecurityAuditorAware")
@EnableTransactionManagement
public class DatabaseConfiguration {

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

    @Inject
    private Environment env;

    @Value("${spring.datasource.hikari.gf.driverClassName}")
    private String gfDriverClassName;
    @Value("${spring.datasource.hikari.gf.jdbcUrl}")
    private String gfJdbcUrl;
    @Value("${spring.datasource.hikari.gf.username}")
    private String gfUsername;
    @Value("${spring.datasource.hikari.gf.password}")
    private String gfPassword;

    @Value("${spring.datasource.hikari.bx.driverClassName}")
    private String bxDriverClassName;
    @Value("${spring.datasource.hikari.bx.jdbcUrl}")
    private String bxJdbcUrl;
    @Value("${spring.datasource.hikari.bx.username}")
    private String bxUsername;
    @Value("${spring.datasource.hikari.bx.password}")
    private String bxPassword;

    @Value("${spring.datasource.hikari.kt.driverClassName}")
    private String ktDriverClassName;
    @Value("${spring.datasource.hikari.kt.jdbcUrl}")
    private String ktJdbcUrl;
    @Value("${spring.datasource.hikari.kt.username}")
    private String ktUsername;
    @Value("${spring.datasource.hikari.kt.password}")
    private String ktPassword;

    @Value("${spring.datasource.hikari.poolName}")
    private String poolName;

    @Bean
    public SpringLiquibase liquibase(DataSource dataSource, LiquibaseProperties liquibaseProperties) {

        // Use liquibase.integration.spring.SpringLiquibase if you don't want Liquibase to start asynchronously
        SpringLiquibase liquibase = new AsyncSpringLiquibase();
        liquibase.setDataSource(dataSource);
        liquibase.setChangeLog("classpath:config/liquibase/master.xml");
        liquibase.setContexts(liquibaseProperties.getContexts());
        liquibase.setDefaultSchema(liquibaseProperties.getDefaultSchema());
        liquibase.setDropFirst(liquibaseProperties.isDropFirst());
        if (env.acceptsProfiles(Constants.SPRING_PROFILE_NO_LIQUIBASE)) {
            liquibase.setShouldRun(false);
        } else {
            liquibase.setShouldRun(liquibaseProperties.isEnabled());
            log.debug("Configuring Liquibase");
        }
        return liquibase;
    }

    @Bean
    public Hibernate4Module hibernate4Module() {
        return new Hibernate4Module();
    }

    /**
    // springboot2.0之前的版本不支持这样的写法,所以下面得手动构造hikari 数据源,坑!
    @Bean("db1")
    @Primary
    @ConfigurationProperties(prefix = "spring.datasource.hikari.31")
    public DataSource db1() {
        return DataSourceBuilder.create().build();
    }*/

    /**
     * db1 DataSource
     * 数据源1
     * @return data source
     */
    @Bean(name = "db1")
    public DataSource db1() {
        HikariDataSource dataSource = new HikariDataSource();
        dataSource.setDriverClassName(gfDriverClassName);
        dataSource.setJdbcUrl(gfJdbcUrl);
        dataSource.setUsername(gfUsername);
        dataSource.setPassword(gfPassword);
        dataSource.setPoolName(poolName);
        return dataSource;
    }

    /**
     * db2 DataSource
     * 数据源2
     * @return data source
     */
    @Bean(name = "db2")
    public DataSource db2() {
        HikariDataSource dataSource = new HikariDataSource();
        dataSource.setDriverClassName(bxDriverClassName);
        dataSource.setJdbcUrl(bxJdbcUrl);
        dataSource.setUsername(bxUsername);
        dataSource.setPassword(bxPassword);
        dataSource.setPoolName(poolName);
        return dataSource;
    }

    /**
     * db3 DataSource
     * 数据源3
     * @return data source
     */
    @Bean(name = "db3")
    public DataSource db3() {
        HikariDataSource dataSource = new HikariDataSource();
        dataSource.setDriverClassName(ktDriverClassName);
        dataSource.setJdbcUrl(ktJdbcUrl);
        dataSource.setUsername(ktUsername);
        dataSource.setPassword(ktPassword);
        dataSource.setPoolName(poolName);
        return dataSource;
    }

    /**
     * Dynamic data source.
     * 配置动态数据源 一定要加@Primary注解,不然给liquibase传参会出现问题。
     * @return the data source
     */
    @Bean(name = "dynamicDataSource")
    @Primary
    public DataSource dynamicDataSource() {
        DynamicRoutingDataSource dynamicRoutingDataSource = new DynamicRoutingDataSource();
        Map<Object, Object> dataSourceMap = new HashMap<>();
        dataSourceMap.put("gf", db1());
        dataSourceMap.put("bx", db2());
        dataSourceMap.put("kt", db3());


        // Set master datasource as default
        dynamicRoutingDataSource.setDefaultTargetDataSource(db1());
        // Set master and slave datasource as target datasource
        dynamicRoutingDataSource.setTargetDataSources(dataSourceMap);

        // To put datasource keys into DataSourceContextHolder to judge if the datasource is exist
        DynamicDataSourceContextHolder.dataSourceKeys.addAll(dataSourceMap.keySet());
        return dynamicRoutingDataSource;
    }

    @Bean(name = "sqlSessionFactory")
    public SqlSessionFactory sqlSessionFactoryBean() throws IOException {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dynamicDataSource());
        bean.setConfigLocation(new DefaultResourceLoader().getResource("classpath:config/mybatis-config.xml"));

        try {
            return bean.getObject();
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

/*  如下针对只有jpa+springboot2.0的 ,在springboot1.4+mybatis不适用
    @Bean(name = "entityManagerFactoryBean")
    public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean(EntityManagerFactoryBuilder builder) {

        // 获取application.yml中spring.jpa.properties的配置
        Map<String, String> properties = jpaProperties.getProperties();

        return builder
            // 设置动态数据源
            .dataSource(dynamicDataSource())
            .properties(properties)
            .packages("com.hi.base.domain")
            .persistenceUnit("persistenceUnit")
            .build();
    }

    @Primary
    @Bean(name = "entityManagerFactory")
    public EntityManagerFactory entityManagerFactory(EntityManagerFactoryBuilder builder) {
        return this.entityManagerFactoryBean(builder).getObject();
    }

    @Primary
    @Bean(name = "transactionManager")
    public PlatformTransactionManager transactionManager(EntityManagerFactoryBuilder builder) {
        return new JpaTransactionManager(entityManagerFactory(builder));
    }*/

    @Bean
    public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }

}

5. mybatis 扫描类


import com.hi.base.config.datasource.DatabaseConfiguration;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 扫描mybatis的接口
 *
 * @author lida
 *
 */
@Configuration
// 因为这个对象的扫描,需要在DatabaseConfiguration的后面注入,所以加上下面的注解
@AutoConfigureAfter(DatabaseConfiguration.class)
public class MyBatisMapperScannerConfig {
    @Bean
    public MapperScannerConfigurer mapperScannerConfigurer() {
        MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
        //获取之前注入的beanName为sqlSessionFactory的对象
        mapperScannerConfigurer.setSqlSessionFactoryBeanName("sqlSessionFactory");
        //指定xml配置文件的路径
        mapperScannerConfigurer.setBasePackage("com.hi.base.mapper");
        return mapperScannerConfigurer;
    }
}

6. 为前后端接口做切面,按照登录用户所处公司来切换数据源。

package com.hi.base.config.datasource;

import com.hi.base.domain.sys.User;
import com.hi.base.repository.sys.UserRepository;
import com.hi.base.security.SecurityUtils;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.inject.Inject;

/**
 * 动态据源切面
 *
 * @author lida
 * @date 2019-11-05
 */
@Aspect
@Component
public class DynamicDataSourceRestAspect {

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

    @Inject
    private UserRepository userRepository;

    /**
     * 在Rest上做切面
     */
    @Pointcut("execution(public * com.hi.base.web.rest.*.*(..))")
    public void restAspect() {
    }

    /**
     * Switch DataSource
     *
     * @param point the point
     */
    @Before("restAspect()")
    public void switchDataSource(JoinPoint point) {

        String oldCapany = DynamicDataSourceContextHolder.getDataSourceKey();
        //获取用户表公司。设置默认公司
        DynamicDataSourceContextHolder.setDataSourceKey("gf");
        try {
            String userName = SecurityUtils.getCurrentUserLogin();
            if(!StringUtils.isBlank(userName)) {
                User user = userRepository.findByUserName(userName);
                if(null!=user) {
                    String nowCampany=user.getCompany();
                    if (!StringUtils.isBlank(nowCampany)) {
                        // 通过人员公司设置数据源
                        DynamicDataSourceContextHolder.setDataSourceKey(nowCampany);
                    }
                }
            }

            logger.debug("Switch DataSource to [{}] in Method [{}]", DynamicDataSourceContextHolder.getDataSourceKey(), point.getSignature());
        } catch (Exception e) {
            if (!StringUtils.isBlank(oldCapany)) {
                DynamicDataSourceContextHolder.setDataSourceKey(oldCapany);
            }
            logger.error("数据源设置失败", e);
        }
    }

    /**
     * Restore DataSource
     *
     * @param point the point
     */
    @After("restAspect()")
    public void restoreDataSource(JoinPoint point) {
        DynamicDataSourceContextHolder.clearDataSourceKey();
        logger.debug("Restore DataSource to [{}] in Method [{}]",
            DynamicDataSourceContextHolder.getDataSourceKey(), point.getSignature());
    }
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot是一个基于Spring框架的快速开发脚手架,可以帮助开发者快速搭建一个Spring应用。MyBatisJPA是两个流行的ORM框架,可以帮助开发者将Java对象映射到数据库表。 Spring Boot整合MyBatis的使用: 1. 添加MyBatis和MySQL的依赖: ```xml <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.2.0</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> ``` 2. 配置数据源MyBatis: ```yaml spring: datasource: url: jdbc:mysql://localhost:3306/test?characterEncoding=utf8&useSSL=true username: root password: 123456 driver-class-name: com.mysql.jdbc.Driver # MyBatis Mapper文件的位置 mybatis: mapper-locations: classpath:mapper/*.xml # 实体类的包名 type-aliases-package: com.example.demo.model ``` 3. 编写MyBatis Mapper: ```xml <!-- UserMapper.xml --> <mapper namespace="com.example.demo.mapper.UserMapper"> <select id="getUserById" resultType="com.example.demo.model.User"> select * from user where id = #{id} </select> </mapper> ``` 4. 编写UserMapper接口: ```java @Mapper public interface UserMapper { User getUserById(int id); } ``` 5. 在需要使用的地方注入UserMapper: ```java @Service public class UserService { @Autowired private UserMapper userMapper; public User getUserById(int id) { return userMapper.getUserById(id); } } ``` Spring Boot整合JPA的使用: 1. 添加JPA和MySQL的依赖: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> <version>2.5.5</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> ``` 2. 配置数据源JPA: ```yaml spring: datasource: url: jdbc:mysql://localhost:3306/test?characterEncoding=utf8&useSSL=true username: root password: 123456 driver-class-name: com.mysql.jdbc.Driver jpa: hibernate: ddl-auto: update properties: # 打印JPA的SQL语句 hibernate: show_sql: true format_sql: true ``` 3. 编写实体类: ```java @Entity @Table(name = "user") public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private int id; private String name; private int age; // 省略getter和setter } ``` 4. 编写JpaRepository: ```java @Repository public interface UserRepository extends JpaRepository<User, Integer> { } ``` 5. 在需要使用的地方注入UserRepository: ```java @Service public class UserService { @Autowired private UserRepository userRepository; public User getUserById(int id) { return userRepository.findById(id).orElse(null); } } ``` 总结: Spring Boot整合MyBatisJPA的使用方法基本相似,只是依赖和配置略有不同。MyBatis需要编写Mapper和XML文件,JPA需要编写实体类和JpaRepository接口。使用时只需要在需要使用的地方注入Mapper或Repository即可。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值