Spring Boot2 (4): процесс реализации использования нескольких источников данных Spring Boot.

Spring Boot

Эта статья была опубликована одновременно в личном техническом блоге [Birds Don't Poop], подробностиможет быть заколотВы также можете отсканировать QR-код в конце статьи, чтобы подписаться на свой личный аккаунт [Птицы не гадят]

предисловие

В реальных бизнес-сценариях невозможно иметь только одну базу данных, поэтому существуют подбазы данных и подтаблицы, а также появляется несколько источников данных. Реализовано разделение чтения и записи, основная библиотека отвечает за добавления, изменения и удаления, а подчиненная библиотека отвечает за запросы. В этой статье будет показано, как Spring Boot реализует несколько источников данных, динамическое переключение источников данных, разделение чтения-записи и другие операции.

развертывание кода

Быстро создать новый проект Spring-Boot Project

1. Добавьте зависимости maven

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.0.1</version>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
</dependency>

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>

2. Конфигурация приложения для чтения из нескольких источников данных

Как и в предыдущем уроке, сначала настройте application.yml

#指定配置文件为test
spring:
  profiles:
    active: test

#配置Mybatis
mybatis:
  configuration:
    # 开启驼峰命名转换,如:Table(create_time) -> Entity(createTime)。不需要我们关心怎么进行字段匹配,mybatis会自动识别`大写字母与下划线`
    map-underscore-to-camel-case: true

#打印SQL日志
logging:
  level:
    com.niaobulashi.mapper.*: DEBUG

Среди них журнал SQL печатается, потому что это источник с несколькими данными, а разные исходные xml-файлы базы данных различаются в пакете сопоставления, поэтому он представлен *.

Настройте application-test.yml следующим образом.

spring:
  datasource:
    #主库
    master:
      jdbc-url: jdbc:mysql://127.0.0.1:3306/test?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true
      username: root
      password: root
      driver-class-name: com.mysql.cj.jdbc.Driver
    #从库
    slave:
      jdbc-url: jdbc:mysql://127.0.0.1:3306/test2?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true
      username: root
      password: root
      driver-class-name: com.mysql.cj.jdbc.Driver

Начиная с узла spring.datasource, различайте главную библиотеку и подчиненную библиотеку. База данных, подключенная к основной библиотеке, называется test, а база данных, подключенная к подчиненной библиотеке, — test2.

Уведомление: Здесь следует отметить, что начиная с Spring Boot2 некоторые конфигурации изменились при настройке нескольких источников данных.Многие руководства в Интернете используютspring.datasource.url. ПоявитсяjdbcUrl is required with driverClassName.Проблема.

Решение: при настройке нескольких источников данныхspring.datasource.urlконфигурация изменена наspring.datasource.jdbc-url

3. Добавьте информацию о конфигурации основной библиотеки

По мнению известного блогера: чистая улыбка, давайте разберем пост в блоге

Сначала посмотрим на код конфигурации основной библиотеки:

@Configuration
@MapperScan(basePackages = "com.niaobulashi.mapper.master", sqlSessionTemplateRef = "masterSqlSessionTemplate")
public class DataSourceMasterConfig {

    /**
     * 是application-test.yml中的spring.datasource.master配置生效
     * @return
     */
    @Bean(name = "masterDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.master")
    @Primary
    public DataSource masterDataSource() {
        return DataSourceBuilder.create().build();
    }

    /**
     * 将配置信息注入到SqlSessionFactoryBean中
     * @param dataSource    数据库连接信息
     * @return
     * @throws Exception
     */
    @Bean(name = "masterSqlSessionFactory")
    @Primary
    public SqlSessionFactory masterSqlSessionFactory(@Qualifier("masterDataSource") DataSource dataSource) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/master/*.xml"));
        return bean.getObject();
    }

    /**
     * 事务管理器,在实例化时注入主库master
     * @param dataSource
     * @return
     */
    @Bean(name = "masterTransactionManager")
    @Primary
    public DataSourceTransactionManager masterTransactionManager(@Qualifier("masterDataSource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

    /**
     * SqlSessionTemplate具有线程安全性
     * @param sqlSessionFactory
     * @return
     * @throws Exception
     */
    @Bean(name = "masterSqlSessionTemplate")
    @Primary
    public SqlSessionTemplate masterSqlSessionTemplate(@Qualifier("masterSqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
        return new SqlSessionTemplate(sqlSessionFactory);
    }
}

проблема: посмотри на этоmasterSqlSessionFactory,SqlSessionFactoryBeanтолько получилspring.datasource.masterИнформация о подключении к базе данных и не получена информация о конфигурации нескольких баз данных.mybatis.configurationВ результате нам нужно настроить правила именования регистра верблюдов, а информация о конфигурации не вводится вSqlSessionFactoryBean. Это приводит к тому, что при обнаружении запроса соответствующие поля user_id, dept_id, create_time не могут быть проанализированы при обнаружении символов подчеркивания.

Решение: добавить конфигурацию в конфигурацию

В то же время введите информацию о конфигурации в SqlSessionFactoryBean.

/**
 * 将配置信息注入到SqlSessionFactoryBean中
 * @param dataSource    数据库连接信息
 * @return
 * @throws Exception
 */
@Bean(name = "slaveSqlSessionFactory")
public SqlSessionFactory slaveSqlSessionFactory(@Qualifier("slaveDataSource") DataSource dataSource) throws Exception {
    SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
    // 使配置信息加载到类中,再注入到SqlSessionFactoryBean
    org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
    configuration.setMapUnderscoreToCamelCase(true);
    bean.setConfiguration(configuration);
    bean.setDataSource(dataSource);
    bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/slave/*.xml"));
    return bean.getObject();
}

4. Добавьте информацию о конфигурации из библиотеки

Это то же самое, что добавить основную информацию о конфигурации библиотеки, но разница в том, что нет необходимости добавлять@PrimaryПредпочтительная аннотация

код показывает, как показано ниже

@Configuration
@MapperScan(basePackages = "com.niaobulashi.mapper.slave", sqlSessionTemplateRef = "slaveSqlSessionTemplate")
public class DataSourceSlaveConfig {

    /**
     * 是application-test.yml中的spring.datasource.master配置生效
     * @return
     */
    @Bean(name = "slaveDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.slave")
    public DataSource slaveDataSource() {
        return DataSourceBuilder.create().build();
    }

    /**
     * 将配置信息注入到SqlSessionFactoryBean中
     * @param dataSource    数据库连接信息
     * @return
     * @throws Exception
     */
    @Bean(name = "slaveSqlSessionFactory")
    public SqlSessionFactory slaveSqlSessionFactory(@Qualifier("slaveDataSource") DataSource dataSource) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        // 使配置信息加载到类中,再注入到SqlSessionFactoryBean
        org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
        configuration.setMapUnderscoreToCamelCase(true);
        bean.setConfiguration(configuration);
        bean.setDataSource(dataSource);
        bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/slave/*.xml"));
        return bean.getObject();
    }

    /**
     * 事务管理器,在实例化时注入主库master
     * @param dataSource
     * @return
     */
    @Bean(name = "slaveTransactionManager")
    public DataSourceTransactionManager slaveTransactionManager(@Qualifier("slaveDataSource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

    /**
     * SqlSessionTemplate具有线程安全性
     * @param sqlSessionFactory
     * @return
     * @throws Exception
     */
    @Bean(name = "slaveSqlSessionTemplate")
    public SqlSessionTemplate slaveSqlSessionTemplate(@Qualifier("slaveSqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
        return new SqlSessionTemplate(sqlSessionFactory);
    }
}

5. Метод расширенной настройки сообщит об ошибке

Такую конфигурацию я тоже видел в интернете.Информация о конфигурации для настройки Mybatis через аннотацию @ConfigurationProperties следующая

/**
 * 试application.yml中的mybatis.configuration配置生效,如果不主动配置,由于@Order配置顺序不同,讲导致配置不能及时生效
 * 使配置信息加载到类中,再注入到SqlSessionFactoryBean
 * @return
 */
@Bean
@ConfigurationProperties(prefix = "mybatis.configuration")
public org.apache.ibatis.session.Configuration configuration() {
    return new org.apache.ibatis.session.Configuration();
}

вprefix, id в основной библиотеке и ведомой библиотеке один и тот же, он должен быть разным, иначе idea выдаст ошибкуDuplicate prefix

В результате только основная библиотека может выполнить конфигурацию Mybatis, а подчиненная библиотека недействительна.

@Bean(name = "masterSqlSessionFactory")
    @Primary
    public SqlSessionFactory masterSqlSessionFactory(@Qualifier("masterDataSource") DataSource dataSource, org.apache.ibatis.session.Configuration configuration) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        // 使配置信息加载到类中,再注入到SqlSessionFactoryBean
        bean.setConfiguration(configuration);
        bean.setDataSource(dataSource);
        bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/master/*.xml"));
        return bean.getObject();
    }

Эта проверка действительна только для основной библиотеки, а синтаксический анализ метода верблюжьего случая из библиотеки недействителен. Исследуйте это позже. . .

6. Код уровня данных

Структура кода следующая

где код SysUserMasterDao

public interface SysUserMasterDao {
	
	/**
	 * 根据userId查询用户信息
	 * @param userId  用户ID
	 */
	List<SysUserEntity> queryUserInfo(Long userId);

	/**
	 * 查询所有用户信息
	 */
	List<SysUserEntity> queryUserAll();

	/**
	 * 根据userId更新用户的邮箱和手机号
	 * @return
	 */
	int updateUserInfo(SysUserEntity user);

}

7. Оператор выполнения данных под ресурсом

SysCodeMasterDao.xml

<mapper namespace="com.niaobulashi.mapper.master.SysUserMasterDao">

    <!--查询所有用户信息-->
    <select id="queryUserAll" resultType="com.niaobulashi.entity.SysUserEntity">
        SELECT
            ur.*
        FROM
            sys_user ur
        WHERE
            1 = 1
    </select>

    <!--根据用户userId查询用户信息-->
    <select id="queryUserInfo" resultType="com.niaobulashi.entity.SysUserEntity">
        SELECT
            ur.*
        FROM
            sys_user ur
        WHERE
            1 = 1
          AND ur.user_id = #{userId}
    </select>

    <!-- 根据UserId,更新邮箱和手机号 -->
    <update id="updateUserInfo" parameterType="com.niaobulashi.entity.SysUserEntity">
        UPDATE sys_user u
        <set>
            <if test="email != null">
                u.email = #{email},
            </if>
            <if test="mobile != null">
                u.mobile = #{mobile},
            </if>
        </set>
        WHERE
        u.user_id = #{userId}
    </update>

</mapper>

8. Тест уровня контроллера

@RestController
public class SysUserController {

    @Autowired
    private SysUserMasterDao sysUserMasterDao;

    @Autowired
    private SysUserSlaveDao sysUserSlaveDao;

    /**
     * 查询所有用户信息Master
     * @return
     */
    @RequestMapping("/getUserMasterAll")
    private List<SysUserEntity> getUserMaster() {
        System.out.println("查询主库");
        List<SysUserEntity> userList = sysUserMasterDao.queryUserAll();
        return userList;
    }

    /**
     * 查询所有用户信息Slave
     * @return
     */
    @RequestMapping("/getUserSlaveAll")
    private List<SysUserEntity> getUserSlave() {
        System.out.println("查询从库");
        List<SysUserEntity> userList = sysUserSlaveDao.queryUserAll();
        return userList;
    }

    /**
     * 根据userId查询用户信息Master
     * @return
     */
    @RequestMapping("/getUserMasterById")
    private List<SysUserEntity> getUserMasterById(@RequestParam(value = "userId", required = false) Long userId) {
        List<SysUserEntity> userList = sysUserMasterDao.queryUserInfo(userId);
        return userList;
    }

    /**
     * 根据userId查询用户信息Slave
     * @return
     */
    @RequestMapping("/getUserSlaveById")
    private List<SysUserEntity> getUserSlaveById(@RequestParam(value = "userId", required = false) Long userId) {
        List<SysUserEntity> userList = sysUserSlaveDao.queryUserInfo(userId);
        return userList;
    }

}

Отправить запрос на все пользовательские интерфейсы

Основная библиотека:http://localhost:8080/getUserMasterAll

Из библиотеки:http://localhost:8080/getUserSlaveAll

Суммировать

1. Реализуйте разделение операций чтения и записи на уровне базы данных с помощью нескольких источников данных.

2. База данных ссылок с несколькими источниками данных использует spring.datasource.jdbc-url.

3. Конфигурацию mybatis.configuration из нескольких источников данных необходимо вручную внедрить в SqlSessionFactory.

Пример кода — гитхаб

Птицы не гадят: будущий архитектор, работающий над Coding