团队博客: https://juejin.cn/post/6844903721927720967
Обзор
Давайте сначала поговорим о бизнес-предыстории. С непрерывным развитием системных сервисов наша система будет наводнена различными предприятиями. В это время мы должны начать думать о том, как улучшить детализацию системы. Например, общий пример : Система электронной коммерции может быть разделена на товарные модули, модули заказов, адресные модули и т. д. Эти модули могут быть извлечены независимо, чтобы сформировать отдельную услугу Это будет включать связь между различными модулями, некоторые простые услуги, мы можемrpc
Интерфейс общается напрямую, но некоторые сервисы не применяют этот режим.В этой статье в основном рассказывается о多数据源
Некоторые ямы встречались на дороге.
Несколько источников данных
Структура проекта
Адрес источника:GitHub.com/JayMeasurementon/SP…
файл конфигурации:DataSourceConfig
@Bean(name = "masterDataSource")
@Qualifier("masterDataSource")
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource masterDataSource() {
return DataSourceBuilder.create().build();
}
@Bean(name = "slaveDataSource")
@Qualifier("slaveDataSource")
@ConfigurationProperties(prefix = "spring.datasource.db2")
public DataSource slaveDataSource() {
return DataSourceBuilder.create().build();
}
@Bean
@Primary
public DynamicDataSource dataSource(@Qualifier("masterDataSource") DataSource master,
@Qualifier("slaveDataSource") DataSource slave) {
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put(DatabaseType.db1, master);
targetDataSources.put(DatabaseType.db2, slave);
DynamicDataSource dataSource = new DynamicDataSource();
dataSource.setTargetDataSources(targetDataSources);// 该方法是AbstractRoutingDataSource的方法
dataSource.setDefaultTargetDataSource(master);// 默认的datasource设置为myTestDbDataSource
return dataSource;
}
@Bean
public SqlSessionFactory sqlSessionFactory(@Qualifier("masterDataSource") DataSource myTestDbDataSource,
@Qualifier("slaveDataSource") DataSource myTestDb2DataSource) throws Exception {
SqlSessionFactoryBean fb = new SqlSessionFactoryBean();
fb.setDataSource(this.dataSource(myTestDbDataSource, myTestDb2DataSource));
fb.setTypeAliasesPackage(env.getProperty("mybatis.type-aliases-package"));
fb.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(env.getProperty("mybatis.mapper-locations")));
return fb.getObject();
}
Процесс создания проекта может относиться к:«Спринг-мибати, прочитал и писать разделение»
база данных
test_1:
CREATE TABLE `school` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`school_name` varchar(255) DEFAULT NULL,
`province` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
test_2:
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(255) DEFAULT NULL,
`password` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
1. Ссылка на базу данных ненормальная
Эта ссылка на базу данных является ненормальной, ссылаясь на切换数据源
Когда ссылка на базу данных ненормальна
Запустите наш сервис:
Это означает, что с конфигурацией нашего сервиса проблем нет, так что же такое так называемое исключение связи с базой данных?
Test:
@Autowired
private SchoolService schoolService;
@Autowired
private UserService userService;
@Test
public void addUser() {
userService.inserUser("root2","root2");
}
@Test
public void addSchool() {
schoolService.addSchool("ceshi1", "ceshi1");
}
Установите источник данных по аннотации:
@Service
@DataSource("db2")
public class UserService
@Service
@DataSource("db1")
public class SchoolService
Мы создали тестовый класс для обнаружения обработки двух источников данных.
Из результатов:
1,schoolService
удалось (дб:test_1
)
2,UserService
не удалось (дб:test_2
)
errorMessage:
org.springframework.jdbc.BadSqlGrammarException:
### Error updating database. Cause: java.sql.SQLSyntaxErrorException: Table 'test_1.user' doesn't exist
### The error may involve com.jaycekon.mybatis.multi.mapper.UserMapper.insert-Inline
### The error occurred while setting parameters
### SQL: INSERT INTO `user`(`username`, `password`) VALUES ( ?, ?);
### Cause: java.sql.SQLSyntaxErrorException: Table 'test_1.user' doesn't exist
; bad SQL grammar []; nested exception is java.sql.SQLSyntaxErrorException: Table 'test_1.user' doesn't exist
Приведенное выше исключение, первая яма, с которой мы можем столкнуться:UserService
Исключение ссылки на источник данных в
Анализ аномалий
1. Источник данных связан сtest_1
Указывает, что источник данных не был успешно переключен
2. Соблюдайте метод аспекта, мониторdataSource
@Before("@annotation(com.jaycekon.mybatis.multi.config.DataSource)")
3.@DataSource
@Retention(RetentionPolicy.CLASS)
@Target({ElementType.TYPE})
public @interface DataSource
Из приведенных выше аннотаций можно узнать, что наш объект аннотации — это TYPE (класс), а вAspectJ
Мониторинг аннотаций в поддерживает только мониторинг аннотаций методов, а не аннотаций классов, поэтому описанным выше способом мы не можем добиться динамического переключения источников данных, аннотируя весь класс:
@Service
@DataSource("db2")
public class UserService
@Service
@DataSource("db1")
public class SchoolService
Решение
1. ИзменитьDataSource
Для аннотации метода отслеживайте каждый метод, которому необходимо переключить источник данных.тупой.
2. Пройти@Pointcut("execution(* com.jaycekon.demo.mapper.*.*(..))")
В форме Pointcut вы можете отслеживать все классы и методы в пакете.Этот метод в порядке, но каждый раз, когда вы создаете новый класс, вам может потребоваться изменить конфигурацию.
3. Режим, используемый в настоящее время в текущем использовании различных источников данныхmapper
,type-aliases
,config
Отдельный
Для настройки см.:портал
Измененный каталог (файл конфигурации должен содержать только два элемента):
2. Исключение сопоставления картографа
После изменения нового файла конфигурации мы можем обратиться к следующему коду (похожему на db2):
@Configuration
@MapperScan(value = "com.jaycekon.mybatis.multi.mapper.db1")
@EnableTransactionManagement
public class DataSourceConfig {
private static final String MAPPER_LOCATION = "mybatis.mapper-locations.db1";
@Autowired
private Environment env;
@Bean(name = "masterDataSource")
@Qualifier("masterDataSource")
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource masterDataSource() {
return DataSourceBuilder.create().build();
}
@Bean(name = "db1SqlSessionFactory")
@Primary
public SqlSessionFactory sqlSessionFactory(@Qualifier("masterDataSource") DataSource myTestDbDataSource) throws Exception {
SqlSessionFactoryBean fb = new SqlSessionFactoryBean();
fb.setDataSource(myTestDbDataSource);
fb.setTypeAliasesPackage(env.getProperty("mybatis.type-aliases-package"));
fb.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(env.getProperty(MAPPER_LOCATION)));
return fb.getObject();
}
@Bean
public DataSourceTransactionManager transactionManager(@Qualifier("masterDataSource") DataSource myTestDbDataSource) {
return new DataSourceTransactionManager(myTestDbDataSource);
}
}
На самом деле конфигурационный файл здесь скрывает яму, когда мы начнем компиляцию, проблем не будет, но когда мы зайдем(db2)
Когда возникает проблема:
org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.jaycekon.mybatis.multi.mapper.db2.UserMapper.insert
Мы видим, что,db1(school)
Модульные тесты хороши, ноdb2(user)
Но что-то пошло не так.
Анализ аномалий
1,Mapper
Сканирование не нашло соответствующегоXML
документ
2. Существует несколько источников данныхSqlSessionFactory
, необходимоMapper
файл привязан к соответствующемуSqlSessionFactory
3. Решение — сканироватьMapper
, привяжите его к соответствующемуSqlSessionFactory
:
@MapperScan(value = "com.jaycekon.mybatis.multi.mapper.db2", sqlSessionFactoryRef = "db2SqlSessionFactory")
существует@MapperScan
Соответствующее объяснение можно найти в:
* Specifies which {@code SqlSessionFactory} to use in the case that there is
* more than one in the spring context. Usually this is only needed when you
* have more than one datasource.
Начать тестовый урок --pass
,запуск программы--pass
Если вы думаете, что эта яма здесь, вы меня недооцениваете~
2.1 Сопоставление псевдонимов типов
Как правило, с нашими модульными тестами и сервисами проблем не возникает, и разумно выполнить следующую разработку в обычном режиме.Spring-Boot
Для разработки нам нужно сделать операцию перед публикацией.Jar包
, а затем запустите службу с помощью командной строки:
java -jar target/spring-boot-mybatis-multi.jar
И тогда возникнут следующие проблемы:
Failed to parse mapping resource: 'class path resource [mybatis-mappers/db2/UserMapper.xml]';
nested exception is org.apache.ibatis.builder.BuilderException: Error parsing Mapper XML.
Cause: org.apache.ibatis.builder.BuilderException: Error resolving class.
Cause: org.apache.ibatis.type.TypeException: Could not resolve type alias 'User'.
Cause: java.lang.ClassNotFoundException: Cannot find class: User
в конфигурацииSqlSessionFactory
мы установилиTypeAliasesPackage
путь сканирования:
@Bean(name = "db1SqlSessionFactory")
@Primary
public SqlSessionFactory sqlSessionFactory(@Qualifier("masterDataSource") DataSource myTestDbDataSource) throws Exception {
...
fb.setTypeAliasesPackage(env.getProperty("mybatis.type-aliases-package"));
...
}
Но он ничего не сделал, почему так?
Анализ аномалий
1. Не работает сканирование псевдонимов
2. Перейдите на Github, чтобы найти соответствующий контент, и вы найдете тот же опыт:портал
Решение
1. Не используйте псевдонимы (不是个好办法
)
2. Вmybatis/spring-boot-starter
В этом проекте официальныйDemo
Мы перехватываем часть более критического кода посередине:
SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
factory.setDataSource(dataSource);
factory.setVfs(SpringBootVFS.class);
Мы используем方法2
Попробуйте и посмотрите, решит ли это проблему:
VFS
Некоторые пояснения:
虚拟文件系统(VFS),用来读取服务器里的资源
Лично понимаю, что вновь созданныйSqlSessionFactory
Не удалось загрузить файл конфигурации, в результате@Primary
Все кромеSqlSessionFactory
Невозможно загрузить соответствующие файлы конфигурации.
3. Исключение конфигурации
После полной настройки модульный тест прошел, и служба успешно запустилась.Следующим шагом является выполнение операции показа и разработка различных функций.После завершения разработки перейдите к этапу тестирования.Когда данные возвращены, это шутка~~
Почему возвращаются пустые данные?
Анализ аномалий
1. Данные возвращаются, с сервисом проблем нет
2,schoolName
соответствующая база данныхschool_name
, при промежуточных преобразованиях необходимо использовать преобразование имени в регистре верблюдов.
Преобразование имени CamelCasemybatis.configuration.map-underscore-to-camel-case
существует проблема.
Решение
1. Добавить конфигурациюmybatis.configuration.map-underscore-to-camel-case=true
2. СоздатьMybatisConfig
класс конфигурации (db2
похожий):
@Bean
@ConfigurationProperties(prefix = "mybatis.configuration")
@Scope("prototype")
public org.apache.ibatis.session.Configuration globalConfiguration() {
return new org.apache.ibatis.session.Configuration();
}
@Bean(name = "db1SqlSessionFactory")
@Primary
public SqlSessionFactory sqlSessionFactory(@Qualifier("masterDataSource") DataSource myTestDbDataSource,
org.apache.ibatis.session.Configuration config) throws Exception {
...
fb.setConfiguration(config);
...
}
3.@Scope("prototype")
Класс конфигурации здесь использует область с несколькими экземплярами, в основном для решения проблемы, связанной с тем, что одноэлементный режим повлияет на ссылку на источник данных.
Время ожидания подключения к базе данных истекло
Когда вы публикуете проект на сервер, проблем с отладкой интерфейса нет, через одну ночь вы вдруг обнаруживаете, что сервис не работает, что происходит?
{
"msg": "\n### Error updating database. Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: No operations allowed after connection closed.\n### SQL: ******\n###
Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: No operations allowed after connection closed.\n; SQL [];
No operations allowed after connection closed.; nested exception is com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException:
No operations allowed after connection closed.",
"code": 500
}
MySQL5.0
В дальнейшем выполняется процесс долгосрочного подключения к БД.DB连接
После 8 часов бездействия (по умолчанию "wait_timeout" сервера Mysql составляет 8 часов), Mysql автоматически закроет соединение. Это проблема, в пуле соединенийconnections
Если он бездействует более 8 часов, mysql отключает его, а сам пул соединений не знаетconnection
истек, если в это время естьClient
проситьconnection
, пул соединений сделает недействительнымConnection
предоставлятьClient
, вызовет указанное выше исключение.
Поэтому при настройке источника данных необходимо настроить соответствующие параметры пула соединений, регулярно проверять правильность соединения и регулярно очищать недействительные соединения.Цитировать
Решение — улучшите соответствующую конфигурацию:
spring.datasource.jdbcUrl=jdbc:mysql://localhost:3306/test_1
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.default-auto-commit = false
spring.datasource.default-read-only = true
spring.datasource.max-idle = 10
spring.datasource.max-wait = 10000
spring.datasource.min-idle = 5
spring.datasource.initial-size = 5
spring.datasource.validation-query = SELECT 1
spring.datasource.test-on-borrow = false
spring.datasource.test-while-idle = true
spring.datasource.time-between-eviction-runs-millis = 18800
spring.datasource.db2.jdbcUrl=jdbc:mysql://localhost:3306/test_2
spring.datasource.db2.username=root
spring.datasource.db2.password=123456
spring.datasource.db2.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.db2.default-auto-commit = false
spring.datasource.db2.default-read-only = true
spring.datasource.db2.max-idle = 10
spring.datasource.db2.max-wait = 10000
spring.datasource.db2.min-idle = 5
spring.datasource.db2.initial-size = 5
spring.datasource.db2.validation-query = SELECT 1
spring.datasource.db2.test-on-borrow = false
spring.datasource.db2.test-while-idle = true
spring.datasource.db2.time-between-eviction-runs-millis = 18800
4. Исключение транзакции
Поскольку мы находимся в нескольких источниках данных, мы используем несколькоsqlSessionFactory
Поэтому в разделе управления транзакциями будут вопросы, связанные с исключениями управления транзакциями.Заинтересованная детская обувь может относиться к:woohoo.atom IK OS.com/main/Web дом…, рекомендует интегрированныйDemo
Суммировать
Конфигурация Mybatis с несколькими источниками данных в основном делится на два типа: один — источник данных динамической конфигурации, а другой — с несколькими источниками данных.sqlsessionFactory
, некоторые ямы в этой статье в основном основаны на многихsqlSessionFactory
.
Все вышеперечисленные проблемы возникают в процессе разработки, и вы, возможно, сталкивались с ними более или менее, и надеемся оказать вам соответствующую помощь.
Если вы не согласны с вашим личным мнением, поправьте меня.
Демонстрационный адрес:GitHub.com/JayMeasurementon/SP…