Один и тот же проект иногда включает в себя несколько баз данных, то есть несколько источников данных. Несколько источников данных можно разделить на две ситуации:
1) Две или более базы данных не имеют корреляции и не зависят друг от друга. На самом деле, это может быть разработано как два проекта. Например, в разработке игр одна база данных является базой данных платформы, а другие базы данных, соответствующие играм под платформой, также доступны;
2) Две или более базы данных-это отношения с мастером-рабов, такие как MySQL, строит мастер-мастер, за которым следует несколько рабов; или копия мастер-солевой, построенная с MHA;
В настоящее время существует примерно два способа построения весенних многоданных источников, и вы можете выбрать в соответствии с ситуацией многоданных источников.
1. Используйте файлы конфигурации пружины для непосредственной настройки нескольких источников данных
Например, в случае отсутствия корреляции между двумя базами данных вы можете напрямую настроить несколько источников данных в файле конфигурации пружины, а затем выполнить конфигурации транзакций, как показано ниже:
<Контекст: Component-Scan Base-package = "net.aazj.service, net.aazj.aop" /> <Контекст: компонент-сканирование Base-package = "net.aazj.aop" /> <!-Введение файлов свойств-> <Контекст: Propertive Placeholder Plocation = "classpath: config /db.properties" /> <!-Настройка данных. init-method = "init" Drest Method = "close"> <name = "url" value = "$ {jdbc_url}" /> <name = "username" value = "$ {jdbc_username}" /> <name = "password" value = "$ {jdbc_password}" /> <!-initize startize-> «СВОДОВОЙ СВОД-> <pretized->« СВЕДЕНИЕ ». value = "0" /> <!-Максимальное количество соединений, используемых пулом соединений-> <имя свойства = "maxactive" value = "20" /> <!-Максимальное количество доступных соединений-> <name = "maxidle" value = "20" /> <!-Минимум, доступное соединения-> <свойство = "minidle" value = "0" /> <! name = "maxwait" value = "60000" /> < /bean> <bean id = "sqlSessionFactory"> <name = "dataSource" ref = "dataSource" /> <name = "configlocation" value = "classPath: config /mybatis-config.xml" /> <properation veight = "mapperlocations" value = "classpath*: config/mappers/**/*. xml"/> </bean> <!-Manager транзакции для одного DATASOURCE-> <Bean ID = "TransactionManager"> <Название свойства = "DataSource" Ref = "DataSource"/> </> </bean> <! Transaction-Manager = "TransactionManager"/> <Bean> <свойство name = "basepackage" value = "net.aazj.mapper"/> <name = "sqlSessionFactoryBeanName" value = "sqlSessionFactory"/> </bean> <!-Enables antable @AspectJ Style aop aop-> <aop: apectj-ayut-aut-aut-aut-aut-aut-aut-aut-aut-aut-aut-ayutj/aoth-ayutj/auth-aut-aut-aut-aut-aut-aut-aut-aut-aut-aut-aut-aut.Конфигурация второго источника данных
<bean name = "dataSource_2" init-method = "init" Dressome-method = "close"> <name = "url" value = "$ {jdbc_url_2}" /> <name = "username" value = "$ {jdbc_username_2}" /> <name = "value =" $ {jdb_paswride_2} " /> <property =" value = "$ {jdb_passwride_2}" /> <property = "value =" $ {jdb_sply_2} " /> <property vame =" Инициализируйте размер соединения-> <name = "initialSize" value = "0" /> <!-Максимальное количество соединений, используемых в пуле соединений-> <name = "maxactive" value = "20" /> <!-максимальный пул соединений в максимальном состоянии-> <name = "maxidle" value = "20" /> <!-minimum idle connection pool-> <name name = "minidle" value = "20" /> <!-Минимум Idle Connection-> <name = "minidle" value = " />". Time-> <name = name = "maxwait" value = "60000" /> < /bean> <bean id = "sqlsessionFactory_slave"> <name = "dataSource" ref = "dataSource_2" /> <name = "configlocation" value = "classpath: config /mybatis-config-2.xml" /> <propatore = "mapperocator value = "classpath*: config/mappers2/**/*. xml"/> </bean> <!-менеджер транзакций для одного DATASOURCE-> <BEAN ID = "TransactionManager_2"> <Свойство = "DataSource" Ref = "DataRce_2"/> </bean> <!-определение транзакции. Transaction-manager = "transactionManager_2" /> <bean> <name = "basepackage" value = "net.aazj.mapper2" /> <name = "sqlSessionFactoryBeanName" value = "sqlSessionFactory_2" /> < /bean> Как показано выше, мы настраиваем два данных, два SQLSessionFactory, два транзакции, и ключ заключается в конфигурации MapperScannerConfigurer - с использованием свойства SQLSessionFactoryBeanName для внедрения различных имен SQLSessionFactory. Таким образом, мы вводим соответствующий SQLSessionFactory в интерфейсы Mapper, соответствующие различным базам данных.
Следует отметить, что эта конфигурация нескольких баз данных не поддерживает распределенные транзакции, то есть несколько баз данных не могут работать в одной и той же транзакции. Преимущество этой конфигурации состоит в том, что она очень просто, но она не гибкая. Он не очень подходит для конфигурации многоданного источника типа мастер-раба. Конфигурация многоданного источника типа главного труда должна быть особенно гибкой и требует подробной конфигурации в соответствии с типом бизнеса. Например, для некоторых трудоемких выбранных операторов мы надеемся поместить их в раб, а для обновления, удаления и других операций мы можем выполнить их только в мастере. Кроме того, для некоторых выбранных операторов с высокими требованиями в реальном времени нам также может потребоваться поставить их на мастер - например, в сценарии я иду в торговом центре, чтобы купить оружие, и операция покупки определенно является мастером. После того, как покупка завершена, нам нужно перекрасить оружие и золотые монеты, которые у меня есть. Тогда этот запрос также может потребоваться, чтобы они не были выполнены на мастере, и не может быть выполнен на рабе, потому что на рабе могут быть задержки. Мы не хотим, чтобы игроки обнаруживали, что после успешной покупки они не могут найти оружие в рюкзаке.
Следовательно, для конфигурации мульти-дат-источника типа мастер-раб, гибкая конфигурация требуется в соответствии с бизнесом, который может быть размещен на рабов, и который выбран не может быть помещен в раб. Следовательно, вышеуказанная конфигурация источника данных не очень подходит.
2. Конфигурация многоданного источника на основе AbstractingDatasource и AOP
Основной принцип состоит в том, что мы определяем ThreadlocalRountingDataSource Sehopasource, чтобы унаследовать AbstractroutingDataSource, а затем внедрить источники данных мастера и подчиненных в ThreadlocalRountingDataSource в файл конфигурации, а затем гибко настроить через AOP, где выбирать мастер -исходный и где выбирать исходные данные. Давайте посмотрим на реализацию кода ниже:
1) Сначала определите перечисление для представления различных источников данных:
пакет net.aazj.enums; /** * Категория источника данных: Master/Slave */public enum DataSources {Master, Slave} 2) Используйте Headlocal, чтобы сохранить ключ, из какого источника данных выбрать для каждого потока:
пакет net.aazj.util; Импорт net.aazj.enums.datasources; открытый класс dataSourcetypeManager {private static final Threadlocal <TaSources> dataSourcetypes = new ThreadLocal <TATASOURCES> () {@Override Защищенные DataSources initialValue () {return dataSources.master; }}; public static DataSources get () {return dataSourceTypes.get (); } public static void set (dataSources dataSourcetype) {dataSourcetypes.set (dataSourcetype); } public static void reset () {dataSourcetypes.set (dataSources.master0); }} 3) Определите ThreadlocalRountingDatasource и наследуйте AbstractroutingDataSource:
пакет net.aazj.util; Импорт org.springframework.jdbc.datasource.lookup.abstractroutingDataSource; Общедоступный класс ThreadlocalRountingDatasource Extress AbstractroutingDatasource {@Override защищенного объекта DegineCurrentLookupkey () {return dataSourceTypeManager.get (); }}4) Внедрить источники данных в мастер и подчиненные
<Контекст: Component-Scan Base-package = "net.aazj.service, net.aazj.aop" /> <Контекст: компонент-сканирование Base-package = "net.aazj.aop" /> <!-Введение файлов свойств-> <Контекст: Property Placeholder Location = "ClassPath: Config /DB.Properties" /> <!-DataSter DataSe Data-> <Bean-name name veachemasteries " /> <! init-method = "init" Drest Method = "close"> <name = "url" value = "$ {jdbc_url}" /> <name = "username" value = "$ {jdbc_username}" /> <name = "password" value = "$ {jdbc_password}" /> <!-initize startize-> «СВОДОВОЙ СВОД-> <pretized->« СВЕДЕНИЕ ». value = "0" /> <!-Максимальное количество соединений, используемых пулом соединений-> <имя свойства = "maxactive" value = "20" /> <!-Максимальное количество доступных соединений-> <name = "maxidle" value = "20" /> <!-Минимум, доступное соединения-> <свойство = "minidle" value = "0" /> <! name = "maxwait" value = "60000" /> < /bean> <!-Настройка источника данных Slave-> <bean name = "dataSourceslave" init-method = "init" dressome-method = "close"> <name = "url" value = "$ {jdbc_url_slave}" /> <name = "username" value = "$ {jdbc_username_slave}" /> <name = "password" value = "$ {jdbc_password_slave}" /> <!-Инициализировать размер подключения-> <Название свойства = "initialsize" value = "0" /> <!-Максимальное число подключений, используемых пулом соединения-> <name name = "maxactive". <property name="maxIdle" value="20" /> <!-- Minimum idle connection pool--> <property name="minIdle" value="0" /> <!-- Get maximum connection waiting time--> <property name="maxWait" value="60000" /></bean> <bean id="dataSource"> <property name="defaultTargetDataSource" ref="dataSourceMaster" /> <name = name = "targetdatasources"> <map key-type = "net.aazj.enums.datasources"> <inpit key = "master" value-ref = "dataSourcemaster"/> <intpirt key = "slave" value-ref = "dataSourcesslave"/> <!-Вы можете добавить несколько данных здесь-> </bean> </bean> </> </> </> </> <! id = "sqlSessionFactory"> <name = "dataSource" ref = "dataSource"/> <name = "configlocation" value = "classPath: config/mybatis-config.xml"/> <name = "mapperlocations" value = "classpath*: config/marpers/*********************************** JDBC DataSource-> <bean id = "transactionManager"> <name = "dataSource" ref = "dataSource" /> < /bean> <!-Определение транзакций с использованием аннотации-> <TX: Annotation Transaction-Manager = "TransactionManager" /> <BEAN> <FORTIN name = "sqlSessionFactoryBeanName" value = "sqlSessionFactory"/> -> </bean> В приведенном выше файле конфигурации пружины мы определяем DataSourCemaster и DataSourceslave для основной базы данных и базы данных подчиненного, а затем внедрить его в <Bean Id = "DataSource">, чтобы наш DataSource мог выбрать DataSourcemaster и DataSourcesSlave в соответствии с различными ключами.
5) Используйте Spring AOP, чтобы указать ключ DataSource, поэтому DataSource выберет DataSourCemaster и DataSourcesLave в соответствии с ключом:
пакет net.aazj.aop; Импорт net.aazj.enums.datasources; импорт net.aazj.util.datasourcetypemanager; Импорт org.aspectj.lang.joinpoint; import org.aspectj.lang.annotation.aspect; import org.aspectj.lang.annotation.fore; import org.aspectj.lang.annotation.pointcut; import org.springforwork.stepytypeme.componter; @Aspept // для aop @component // для автоматического сканированного класса dataSourceInterceptor {@pointcut ("execution (public * net.aazj.service .. *. GetUser (..))") public void dataSourceslave () {}; @Before ("dataSourcesLave ()") public void до (joinpoint jp) {dataSourceTypeManager.set (dataSources.Slave); } // ...} Здесь мы определяем аспект. Мы используем @be, чтобы вызовать dataSourcetypeManager.set (dataSources.Slave) перед методом в соответствии с @pointcut ("execution (public * net.aazj.service .. *. GetUser (..))"), и тип ключа устанавливается на DataSources.Slave, так что DataSource будет выбирать DataSourceslave в соответствии с Keys. Следовательно, операторы SQL для этого метода будут выполнены в базе данных рабов.
Мы можем постоянно расширять аспект DataSourceInterceptor и сделать в нем различные определения для указания данных, соответствующего соответствующему источнику данных для определенного метода обслуживания.
Таким образом, мы можем использовать мощные функции Spring AOP, чтобы настроить его очень гибкой.
6) Анализ принципа AbstractingDatasource
ThreadlocalRountingDatasource наследует AbstractroutingDataSource, реализуя его абстрактный метод, защищенный абстрактный объект DeTemneCurrentLookupkey (); Таким образом, реализация функции маршрутизации для различных источников данных. Давайте начнем с исходного кода для анализа принципов:
Общественный абстрактный класс AbstractroutingDataSource расширяет AbstractDatasource реализует инициализацию BeanabStractroutingDatasource реализует инициализирующие подборы. Затем, когда пружина инициализирует бобы, он назовет интерфейс инициализируемого bean void effectropertiesset (), который бросает исключение; Давайте посмотрим, как AbstractroutingDataSource реализует этот интерфейс: @Override public void AfterproperTiesset () {if (this.TargetDataSources == null) {бросить новый allodalargumentException («Property 'TargetDataSources' требуется»); } this.ResolvedDataSources = new HashMap <Object, dataSource> (this.TargetDataSources.size ()); for (map.Entry <Object, Object> entry: this.TargetDataSources.EntrySet ()) {Object LookupKey = ResolvesPecifiedLookupKey (entry.getKey ()); DataSource DataSource = ResolvespecifiedDatasource (entry.getValue ()); this.ResolvedDatasources.put (LookupKey, DataSource); } if (this.defaultTargetDatasource! = null) {this.ResolvedDefaultDataSource = ResolvesPecifiedDatasource (this.DefaultTargetDataSource); }} TargetDataSources - это DataSourCemaster и DataSourcesLave, который мы вводим в файл конфигурации XML. Метод Afterpropertiesset вводится.
DataSourcemaster и DataSourceslave для построения HashMap - ResolvedDataSources. Удобно получить соответствующий источник данных с карты в соответствии с ключом позже.
Давайте посмотрим на то, как Connection getConnection () бросает SQLException; В интерфейсе AbstractDatasource реализован:
@Override public connection getConnection () Throws SQLexception {return detrinetArgetDatasource (). GetConnection (); }Ключ состоит в том, чтобы DeTerinetArgetDatasource (), который можно увидеть на основе имени метода, и мы должны решить, какой данные здесь использовать здесь:
Защищенный данных DETRINETARGETDATASOURCE () {assert.notnull (this.ResolvedDataSources, «маршрутизатор данных не инициализирован»); Object Lookupkeke = DetrineCurrentLookupKey (); DataSource DataSource = this.ResolvedDataSources.get (LookupKey); if (dataSource == null && (this.lenientFallback || lookupkey == null)) {dataSource = this.ResolvedDefaultDataSource; } if (dataSource == null) {throw new allogalStateException («Не может определить целевой дат данных для ключа поиска [" + lookupkekey + "]"); } return DataSource;} Object Lookupkeke = DetrineCurrentLookupKey (); Этот метод реализован нами, где мы получаем значение ключа, сохраненное в Threadlocal. После получения ключа получите дат данных, соответствующий ключу в инициализированной карте ResolvedDataSources от AfterPropertiesset (). Значение ключа, сохраненное в Threadlocal, устанавливается перед вызовом соответствующих методов в службе через AOP. Хорошо, вот это сделано!
3. Резюме
Из этой статьи мы можем испытать силу и гибкость AOP.
Выше приведено информационная сортировка многоданной обработки Mybatis Multi-Data. Я надеюсь, что это может помочь нуждающимся друзьям