Функциональные требования заключаются в том, что компании необходимо создать большую операционную платформу:
1. Операционная платформа имеет свою собственную базу данных, поддерживая основные функции, такие как пользователи, роли, меню, детали и разрешения.
2. Операционная платформа также должна обеспечить внутренние операции других различных услуг (служба A, сервис B), а базы данных обслуживания A и обслуживания B являются независимыми.
Следовательно, платформа операции должна подключить как минимум три библиотеки: библиотека операций, библиотека и библиотеки B, и надеется автоматически переключиться на соответствующий источник данных для каждого запроса функции (моя окончательная реализация заключается в переключении на уровень метода службы и переключение на метод каждого уровня DAO. Функции нашей системы относительно независимы друг от друга).
Шаг 1: Настройте несколько источников данных
1. Определите источник данных:
Источник данных, который я использую, - это Druiddatasource от Alibaba (в порядке DBCP, это то, что). Конфигурация заключается в следующем:
<!-OP DataSource-> <bean id = "OpdataSource" init-method = "init" Drest-method = "close"> <name = "url" value = "$ {db.master.url}" /> <name = "username" value = "$ {db.master.user}" /> <property name = "passle" value = "$ {db.master.password}" /> <name = "driverclassname" value = "$ {db.master.driver}" /> <name = "initialsize" value = "5" /> <property name = "maxactive" value = "100" /> name = "minidle" value = "10" /> <свойство ". name="validationQuery" value="SELECT 'x'" /> <property name="testOnBorrow" value="false" /> <property name="testOnReturn" value="false" /> <property name="testWhileIdle" value="true" /> <property name="timeBetweenEvictionRunsMillis" value="600000" /> <property name="minEvictableIdleTimeMillis" value = "300000" /> <name = "removeabandone" value = "true" /> <name = "removabandontimeout" value = "1800" /> <name = "logabandoned" value = "true" /> <!-Configure Filters для мониторинга статистики-> <property name = "filters" value = "configer, meragestate, wall, vall4j2" /" /". value = "config.decrypt = true" /> < /bean> <!-DataSource Servera-> <bean id = "serverAdatasource" init-method = "init" dross-method = "close"> <name = "url" value = "$ {db.servera.master.url}" /> <name = "username" value="${db.serverA.master.user}" /> <property name="password" value="${db.serverA.master.password}" /> <property name="driverClassName" value="${db.serverA.master.driver}" /> <property name="initialSize" value="5" /> <property name="maxActive" value = "100" /> <name = "minidle" value = "10" /> <name = "maxwait" value = "60000" /> <name = "validationQuery" value = "select 'x'" /> <name = "testonbourrow" vally = "false" /> <name = "testonretur name = "testonReturn" value = "false" /> <name = "testWhileIdle" value = "true" /> <name = "timeBowneEvictionRunsmillis" value = "600000" /> <name = "mineVictableIdletImelis" value = "300000" /> <Property name = "removAganed" value = "true" /"300000" /> <свойство = "RemovAdened" value = "true" /"300000" /> <Property name = "removAgandoned" = "true" /" /" name 300000 ". value = "1800" /> <name = "logabandoned" value = "true" /> <!-Настройка фильтров для мониторинга перехватов статистики-> <name = "filters" value = "config, merestat, wall, log4j2" /> <name = "connectionProperties" value = "config.decrypt = true" /> < /bean> <! id = "serverbdatasource" init-method = "init" destry-method = "close"> <name = "url" value = "$ {db.serverb.master.url}" /> <name = "insername" value = "$ {db.serverb.master.user}" /> <Свойство name = "пароль" пароль " value = "$ {db.serverb.master.password}" /> <name = "driverclassname" value = "$ {db.serverb.master.driver}" /> <name = "initialsize" value = "5" /> <property name = "maxactive" value = "100" /> name name ". value = "60000" /> <name = "valyationQuery" value = "select 'x'" /> <name = "testonBorrow" value = "false" /> <name = "testonreturn" value = "false" /> <name = "testwhileIdle" value = "true" /> <propation = "timeWeedEvictionRunsmillismillis". name = "mineVictableIdletImeMillis" value = "300000" /> <name = "removeabandone" value = "true" /> <name = "removeabandOntimeout" value = "1800" /> <name = "logabandoned" value = "true" /> <!-Фильтры конфигурации для статистики мониторинга-> <свойства. value = "config, merestat, wall, log4j2" /> <name = "connectionProperties" value = "config.decrypt = true" /> < /bean>Я настроил три источника данных: OpdataSource (источник данных самой операционной платформы), ServerAdatasource и ServerBdatasource.
2. Настройте мультилектуальные
Multipledatasource эквивалентен одному прокси для трех вышеуказанных источников данных. Когда он действительно сочетается с пружиной/mybatis, мультизлеточный и отдельно настроенный использование данных не отличается:
<!-Spring Integration mybatis: configure multipledatasource-> <bean id = "sqlSessionFactory"> <name = "dataSource" ref = "multipledatasource"/> <!-Автоматическое сканирование mapping.xml файл-> <свойство = "mapperlocations"> <sall> <value> classpath*:/skmlmapper/************************* <dulch> classpath*:/sqlmapperxml/*/*. xml </value> </list> </property> <name = "configlocation" value = "classpath: xml/mybatis-config.xml"> </property> <property name = "typealiasspackage" value = "xxx.platform.model"/name "/name"/name "/name"/name "/"/property name "/"/name ". ref = "globalconfig"/> <name = "plugins"> <Array> <!-Конфигурация плагина лиц-> <bean id = "paginationinterceptor"> <name = "dialectpe" value = "mysql"/> <property name = "Optimizetype" value = "alidruid"/> </bean> </array> </bean> </bean> </bean> </bean> </bean> </bean> </bean> </bean> </bean> </bean> </bean> </bean> </bean> </bean> </bean> </bean> </bean> </bean> </bean> </bean> </bean> </bean> dynamic implementation --> <bean id="mapperScannerConfigurer"> <!-- For Dao interface dynamic implementation, you need to know where the interface is --> <property name="basePackage" value="com.XXX.platform.mapper" /> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property> </bean> <!-- Глобальная конфигурация MP-> <bean id = "globalconfig"> <name = "idtype" value = "0"/> <name = "dbcolumnunderline" value = "true"/> </bean> <!-Конфигурация управления транзакциями Multipledatasource-> <bean id = "transactionManager"> <properation = "dataSource" ref = "multipledatas"/multipledatas "/uleptledatas"/ulptullyat lemlepledatas "/"/"/"/"/"/"/"/"/"!
После понимания местоположения мультилетасура, давайте сосредоточимся на том, как реализовать мультилектуальные. Файл конфигурации следующим образом:
<bean id = "multipledatasource"> <name = "defaultTargetDatasource" ref = "opdataSource" /> <name = "targetDataSources"> <Map> <inpit key = "opdataSource" value-ref = "opdatasource" /> <intry "quiredatasource" value-ref = "opdataSource" /> <inpitude quiedAtasourc key = "serverbdatasource" value-ref = "serverbdatasource"/> </map> </property> </bean>
Реализированный код Java выглядит следующим образом, и нет необходимости в слишком большом количестве объяснений, и это очень ясно с первого взгляда:
Импорт org.springframework.jdbc.datasource.lookup.abstractroutingdatasource;/** * * @classname: multipledatasource * @description: настройка нескольких источников данных <br> * @author: yuzhu.peng * @date: 12 января 2018 г. в 4:37:25. AbstractroutingDatasource {private Static Final Threadlocal <string> dataSourcekey = new enhytableThreadLocal <String> (); public static void setDataSourcekey (String DataSource) {dataSourceKey.set (dataSource); } @Override защищенный объект degineCurrentLookupkey () {return dataSourcekey.get (); } public static void elementatasourcekey () {dataSourceKey.remove (); }}Унаследованный от AbstractingDatasource Spring Spring, реализует абстрактный метод DetureCurrentLookupkey. Этот метод будет определять данные источника данных для этого соединения каждый раз, когда получается подключение к базе данных. Вы можете видеть, что весенний код будет понятен:
/*Получить соединение*/ public connection getConnection () throws sqlexception {return detrinetArgetDataSource (). GetConnection (); } Защищенный DataSource DetrinetArgetDatasource () {assert.notnull (this.ResolvedDataSources, "маршрутизатор данных не инициализирован"); /*DegineCurrentLookupkey здесь - абстрактный интерфейс, получение конкретного имени источника данных*/ Object LookupKey = degineCurrentLookupKey (); DataSource 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; } /*Аннотация интерфейс: то есть интерфейс, реализованный нашим мультизлеточным* / защищенным абстрактным объектом DegineCurrentLookupkey ();Шаг 2: Динамически переключать источник данных каждый запрос (уровень обслуживания метода)
Идея реализации состоит в том, чтобы использовать идею AOP Spring для перехвата каждого вызова метода службы, а затем динамически переключать ключ данных в мультизлеторе в соответствии с общим именем пути метода. Наш проект для операций различных сервисов, то есть различных баз данных, не зависит друг от друга. Не рекомендуется вызывать различные источники данных в одном и том же методе обслуживания. Таким образом, нам нужно динамически определить, должна ли частота переключения размещать на уровне DAO, то есть уровень SQL. Кроме того, управление транзакциями не удобно.
Давайте посмотрим на реализацию AOP источников данных динамического переключения:
Импорт java.lang.reflect.proxy; import org.apache.commons.lang.classutils; import org.aspectj.lang.joinpoint; импорт org.aspectj.lang.annotation.aspept; импорт org.aspectj.lang.annotation.before; импорт org.spramework.core.nantation./ * * * * @author yuzhu.peng * @since 2018-01-15 * / @aspoy @order (1) public class multipledatasourceinterceptor { /** * Перехватчик обращает особое внимание на переключение источников данных перед запросом всех классов реализации бизнеса. Поскольку используются несколько источников данных, лучше всего вызовать Mapper только в *ServiceImpl. В противном случае, призывая таблицу, которая не является источником данных по умолчанию, исключение, которое не существует в таблице, будет сообщено** @param joinpoint* @Throws Throwable*/ @before ("excution (* com.xxxx.platform.service ..*.* ServiceImpl. joinpoint.getTarget (). getClass (); String classname = clazz.getName (); if (classutils.isassignable (clazz, proxy.class)) {classname = joinpoint.getSignature (). getDeclaringTypEname (); } // Установите источник данных Server } else if (className.contains (". } else {multipledatasource.setDataSourceKey (dbconstant.data_source_op); }} /*** Когда операция завершена, если текущий источник данных выпускается, если он не будет выпущен, конфликт источника данных произойдет при частоте щелчка. Это таблица другого источника данных, но она будет работать с другим источником данных. В отчете не существует** @param joinpoint* @Throws Throwable*/ @after ("execution (* com.xxxx.service ..*.* ServiceImpl.* (..))") public void lementatasoruce (joinpoint joinpoint) бросает бросает {multipledatasource.remodatoSourcekey (); }}Перехватите все методы ServiceImpl, судья, какая функция источника данных принадлежит полностью квалифицированному имени метода, а затем выберите соответствующий источник данных. После того, как распределение будет завершено, выпустите текущий источник данных. Обратите внимание, что я использовал Spring's @Order, Annotation, и я расскажу об этом дальше, при определении нескольких AOP, заказ очень полезен.
другой:
Вначале проект не вводил транзакции, так что все было в порядке. Вы можете получить доступ к правильному источнику данных каждый раз. После присоединения к управлению транзакциями пружины вы не можете динамически переключить источник данных (кажется, что транзакция не эффективна, но оба не являются действительными в одно и то же время). Позже я обнаружил, что причиной был порядок выполнения AOP, поэтому я использовал упомянутый выше весенний заказ:
Чем меньше порядок, исполнение первое. На этом этапе вы можете не только динамически переключать источники данных, но и успешно использовать транзакции (в том же источнике данных).
Выше всего содержание этой статьи. Я надеюсь, что это будет полезно для каждого обучения, и я надеюсь, что все будут поддерживать Wulin.com больше.