Das gleiche Projekt umfasst manchmal mehrere Datenbanken, dh mehrere Datenquellen. Mehrere Datenquellen können in zwei Situationen unterteilt werden:
1) Zwei oder mehr Datenbanken haben keine Korrelation und sind unabhängig voneinander. Tatsächlich kann dies als zwei Projekte entwickelt werden. In der Spielentwicklung ist beispielsweise eine Datenbank eine Plattformdatenbank, und die anderen Datenbanken, die den Spielen unter der Plattform entsprechen, sind ebenfalls verfügbar.
2) Zwei oder mehr Datenbanken sind Master-Slave-Beziehungen, wie z. B. MySQL, gefolgt von mehreren Sklaven; oder Master-Sklave-Kopie mit MHA;
Derzeit gibt es ungefähr zwei Möglichkeiten, Feder-Multi-Daten-Quellen aufzubauen, und Sie können entsprechend der Situation von Multi-Daten-Quellen wählen.
1. Verwenden Sie Federkonfigurationsdateien, um mehrere Datenquellen direkt zu konfigurieren
Beispielsweise können Sie bei keiner Korrelation zwischen zwei Datenbanken mehrere Datenquellen in der Federkonfigurationsdatei direkt konfigurieren und dann Transaktionskonfigurationen durchführen, wie unten gezeigt:
<Context: Komponent-scan Base-Package = "net.aazj.service, net.aazj.aop" /> <context: component-scan base-package = "net.aazj.aop" /> <! init-method = "init" destroy-method = "close"> <Eigenschaft name = "url" value = "$ {jdbc_url}" /> <Eigenschaft name = "userername" value = "$ {jdbc_username}" /> <Property name = "Passwort" value = "$ {{Jdbc_password}" /> <! value = "0" /> <!-Maximale Anzahl von Verbindungen, die vom Verbindungspool verwendet werden-> <Eigenschaft name = "MaxActive" value = "20" /> <!-Maximale Anzahl der verfügbaren Verbindungen-> <Eigenschaft name = "Maxidle" value = "20" /> <!-Minimale Anzahl der verfügbaren Verbindungen-> <Eigenschaftsname ". name = "maxwait" value = "60000" /> < /bean> <bean id = " value="classpath*:config/mappers/**/*.xml" /></bean> <!-- Transaction manager for a single JDBC DataSource --><bean id="transactionManager"> <property name="dataSource" ref="dataSource" /></bean> <!-- Define transactions using annotation--><tx:annotation-driven Transaction-Manager = "TransactionManager"/> <bean> <Eigenschaft name = "Basepackage" value = "net.aazj.mapper"/> <Eigenschaft name = "sqlSessionFactoryBeAnname" value = "Konfiguration der zweiten Datenquelle
<bean name="dataSource_2" init-method="init" destroy-method="close"> <property name="url" value="${jdbc_url_2}" /> <property name="username" value="${jdbc_username_2}" /> <property name="password" value="${jdbc_password_2}" /> <!-- Initialisieren Sie die Verbindungsgröße-> <Eigenschaft name = "initialSize" value = "0" /> <!-Maximale Anzahl der im Verbindungspool verwendeten Verbindungen-> <Eigenschaft name = "MaxActive" value = "20" /> <!-Maximal Idleverbindungsverbindungspool-> <Eigenschaftsname = "Maxidle". Zeit-> <Eigenschaft name = "maxwait" value = "60000" /> < /bean> <bean id = " value = "classPath*: config/mappers2/**/*. xml"/> </bean> <!-Transaktionsmanager für ein einzelnes JDBC-DataSource-> <bean id = "transactionManager_2"> <Eigenschaft name = "dataSource" Ref = "dataSource_2"/> </bean> <! Transaction-Manager = "TransactionManager_2" /> <bean> <Eigenschaft name = "Basepackage" value = "net.aazj.mapper2" /> <Eigenschaft name = "sqlSessionFactoryBeanname" value = " Wie oben gezeigt, konfigurieren wir zwei DataSources, zwei SQLSessionFactory, zwei Transaktionsmanager, und der Schlüssel liegt in der Konfiguration von mapperscannerConfiger - unter Verwendung der SQLSessionFactoryBeanname -Eigenschaft, um verschiedene SQLSessionFactory -Namen zu injizieren. Auf diese Weise injizieren wir den entsprechenden SQLSessionFactory in die Mapper -Schnittstellen, die verschiedenen Datenbanken entsprechen.
Es ist zu beachten, dass diese Konfiguration mehrerer Datenbanken keine verteilten Transaktionen unterstützt, dh mehrere Datenbanken können nicht in derselben Transaktion betrieben werden. Der Vorteil dieser Konfiguration ist, dass sie sehr einfach ist, aber nicht flexibel ist. Es ist nicht sehr geeignet für die Konfiguration der Multi-Daten-Quelle des Master-Slave-Typs. Die Konfiguration der Multi-Daten-Quelle des Master-Slave-Typs muss besonders flexibel sein und erfordert eine detaillierte Konfiguration gemäß der Art des Geschäfts. In einigen zeitaufwändigen Auswahlanweisungen hoffen wir beispielsweise, dass wir sie auf den Sklaven setzen und für Update, Löschen und andere Vorgänge sie nur auf dem Master ausführen können. Für einige ausgewählte Aussagen mit hohen Echtzeitanforderungen müssen wir sie möglicherweise auch auf den Meister setzen. In einem Szenario gehe ich zum Einkaufszentrum, um eine Waffe zu kaufen, und der Kaufbetrieb ist definitiv der Meister. Nach Abschluss des Kaufs müssen wir die Waffen und Goldmünzen, die ich besitze, erneut gezwungen. Dann muss diese Abfrage möglicherweise auch verhindern, dass sie auf den Master ausgeführt werden, und kann nicht auf dem Sklaven ausgeführt werden, da auf dem Sklaven möglicherweise Verzögerungen auftreten. Wir möchten nicht, dass die Spieler feststellen, dass nach dem Kauf erfolgreich ist, keine Waffen im Rucksack finden können.
Daher ist für die Konfiguration der Master-Slave-Multi-Daten-Quelle eine flexible Konfiguration gemäß dem Geschäft erforderlich, das auf dem Slave ausgewählt werden kann und die Auswahl nicht am Slave platziert werden kann. Daher ist die obige Konfiguration der Datenquelle nicht sehr geeignet.
2. Konfiguration der Multi-Daten-Quelle basierend auf AbstractroutingDataSource und AOP
Das grundlegende Prinzip ist, dass wir eine DataSource -Klasse -ThreadLocalRountingDataSource definieren, um AbstractroutingDataSource zu erben und dann Master- und Slave -Datenquellen in ThreadLocalrountDataSource in die Konfigurationsdatei zu injizieren und dann flexibel zu konfigurieren. Sehen wir uns die folgende Code -Implementierung an:
1) Definieren Sie zuerst eine Enum, um verschiedene Datenquellen darzustellen:
Paket net.aazj.enums; /** * Kategorie der Datenquelle: Master/Slave */public enum dataSources {Master, Slave} 2) Verwenden Sie den Cheflokal, um die Taste zu speichern, welche Datenquelle für jeden Thread auswählen soll:
Paket net.aazj.util; import net.aazj.enums.datasources; öffentliche Klasse DataSourcetypemanager {private statische endgültige ThreadLocal <DataSources> DataSourcetypes = new ThreadLocal <DataSources> () {@Override Protected 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) Definieren Sie ThreadLocalRountingDataSource und erben Sie AbstractroutingDataSource: Erben:
Paket net.aazj.util; import org.springframework.jdbc.datasource.lookup.abstractingDataSource; public class ThreadLocalRountingDataSource erweitert AbstractroutingDataSource {@Override Protected Object DeterminecurrentLookupkey () {return DataSourcetypemanager.get (); }}4) Master- und Slave -Datenquellen in die ThreadLocalRountingDataSource in der Konfigurationsdatei injizieren:
<Kontext: Komponenten-scan-Basis-Package = "net.aazj.service, net.aazj.aop" /> <context: component-scan base-package = "net.aazj.aop" /> <!-Einführung von Eigenschaftsdateien-> <context: Property-PlaceHolder-Standort = "ClassPath: Konfiguration /Db.Properties" /> <!-Data Sourder Master-> < name="dataSourceMaster" init-method="init" destroy-method="close"> <property name="url" value="${jdbc_url}" /> <property name="username" value="${jdbc_username}" /> <property name="password" value="${jdbc_password}" /> <!-- Initialize the connection size --> <property name = "initialSize" value = "0" /> <!-Maximale Anzahl von Verbindungen, die vom Verbindungspool verwendet werden-> <Eigenschaft name = "MaxActive" value = "20" /> <!-Maximale Anzahl der verfügbaren Verbindungen-> <Eigenschaftsname = "Maxidle" value = "20" /> <! <Eigenschaft name = "maxwait" value = "60000" /> < /bean> <!-Datenquelle Slave konfigurieren-> <bean name = "dataSourceslave" init-method = "init" destroy-method = "close"> <Property name = "url" value value = "$ {jdbc_username_slave}" /> <Eigenschaft name = "password" value = "$ {jdbc_password_slave}" /> <!-Initialisierung der Verbindungsgröße-> <Eigenschaft Name "InitialSize" value = "0" /> <!-Maximum-Number der von dem Verbindungspool verwendeten Verbindungsbeamten. Pool-> <Eigenschaft name = "maxidle" value = "20" /> <!-minimaler Leerlaufverbindungspool-> <Eigenschaft name = "minidle" value = "0" /> <!-Maximale Verbindungszeit Wartezeit abrufen-> <Eigenschaft name = "maxwait" value = "60000" /> < /bean> <bean id = "dataSource"> <"> <"> <"> <"> <"> <"> <">" < ref = "dataSourcemaster"/> <Eigenschaft name = "targetDataSources"> <map key-type = "net.aazj.enums.datasources"> <Eintragsetaste "master" master "Value-ref =" dataSourcemaster "/> <Eintragsschlüssel =" Slave-ref = ref = "dataSourceslave"/> <! "/<!-add multury-ref =" dataSourceslave "/> <!-add multury-ref =" ^ mac/> <!-add multury-ref = " </Property> </bean> <bean id = " <!-Transaktionsmanager für eine einzelne JDBC-Datenquelle-> <bean id = "TransactionManager"> <Eigenschaft name = "dataSource" ref = "dataSource" /> < /bean> <!-Definition von Transaktionen mit Annotation-> <tx: Annotationsmarke-Manager = "TransactionManit" /> <boan- value = "net.aazj.mapper"/> <!-<Eigenschaft name = "sqlSessionFactoryBeanname" value = " In der obigen Spring -Konfigurationsdatei definieren wir DataSourcemaster und DataSourceslave für die Master -Datenbank und die SLAVE -Datenbank und geben sie dann in <bean id = "dataSource"> ein, damit unsere DataSource -DataSourcemaster und DataSourceslave nach den verschiedenen Schlüssel auswählen kann.
5) Verwenden Sie Spring AOP, um den Schlüssel der DataSource anzugeben, sodass DataSource dataSourcemaster und DataSourceslave gemäß dem Schlüssel ausgewählt wird:
Paket net.aazj.aop; import net.aazj.enums.datasources; import net.aazj.util.datasourcetypemanager; import org.aspespectj.lang.joinpoint; import org.aspespec.lang.Annotation.aspe; @Aspespe // für AOP @component // für auto scanPublic class DataSourceInterceptor {@pointcut ("Ausführung (public * net.aazj.service .. *. GetUser (..)") public void DataSourceslave () {}; @Before ("DataSourcesLave ()") public void vor (joinpoint jp) {dataSourcetypemanager.set (DataSources.slave); } // ...} Hier definieren wir eine Aspektklasse. Wir verwenden @Before, dataSourcetypeManager.set (DataSources.Slave) vor der Methode in Übereinstimmung mit @pointcut ("Ausführung (öffentlich * net.aazj.Service .. *. Daher werden die SQL -Anweisungen für diese Methode in der Slave -Datenbank ausgeführt.
Wir können den DataSourceInterceptor -Aspekt kontinuierlich erweitern und verschiedene Definitionen darin vorstellen, um die Datenquelle anzugeben, die der entsprechenden Datenquelle für eine bestimmte Servicemethode entspricht.
Auf diese Weise können wir die leistungsstarken Funktionen von Frühlings -AOP verwenden, um sie sehr flexibel zu konfigurieren.
6) Analyse des Prinzips des AbstractroutingDataSource
ThreadLocalRountingDataSource erbt AbstractroutingDataSource und implementiert die abstrakte methodisch geschützte abstrakte ObjektdeterminenzurrecurrentLookupkey (); So implementieren Sie die Routing -Funktion für verschiedene Datenquellen. Beginnen wir mit dem Quellcode, um die Prinzipien zu analysieren:
Öffentliche Abstract -Klasse AbstractroutingDataSource erweitert AbstractDataSource -Geräte implementiert initialisierungsbeanabstrackTroutingDataSource -Geräte initializingBean. Wenn die Spring dann die Bohne initialisiert, wird die Schnittstelle der Initialisierungs -void -NachpropertieSt () Ausnahme ausgelöst. Lassen Sie uns sehen, wie AbstractRoutingDataSource diese Schnittstelle implementiert: @Override public void AfterPertieStieSt () {if (this.targetDataSources == null) {Wirf eine neue IllegalArgumentException ("Eigentum 'TargetDataSources' erforderlich"); } this.ResolvedDataSources = new HashMap <Objekt, DataSource> (this.targetDataSources.size ()); für (map.entry <Objekt, Objekt> Eintrag: this.targetDataSources.EntrySet ()) {Object Lookupkey = ResolveSpecifiedLookupkey (Entry.getKey ()); DataSource dataSource = ResolredecifiedDataSource (Eintrag.getValue ()); this.ResolvedDataSources.put (Lookupkey, DataSource); } if (this.DefaultTargetDataSource! }} TargetDataSources ist der DataSourcemaster und DataSourceslave, den wir in die XML -Konfigurationsdatei einfügen. Die Nachpropertie -Methode wird injiziert.
DataSourcemaster und DataSourceslave, um eine HashMap -ResoledDataSources zu konstruieren. Es ist zweckmäßig, die entsprechende Datenquelle von der Karte entsprechend dem Schlüssel später zu erhalten.
Schauen wir uns an, wie Connection getConnection () SQLEXception auswirkt. In der AbstractDataSource -Schnittstelle wird die Schnittstelle implementiert:
@Override Public Connection getConnection () löst SQLEXception {return DeterInetargetDataSource (). GetConnection (); }Der Schlüssel besteht darin, die DeterminetargetDataSource () zu bestimmen, die basierend auf dem Methodennamen zu sehen ist, und wir sollten entscheiden, welche DataSource hier verwendet werden soll:
Protected DataSource DeterInetargetDataSource () {assert.notnull (this.ResolvedDataSources, "DataSource -Router nicht initialisiert"); Object Lookupkey = DeterminecurrentLookupkey (); DataSource dataSource = this.ResolvedDataSources.get (Lookupkey); if (dataSource == null && (this.lenientfallback || LookUpKey == null)) {dataSource = this.ReLedDefaultDataSource; } if (dataSource == null) {neue illegaleStateException werfen ("" Zieldatenource kann nicht bestimmen, um Suchschlüssel [" + Lookupkey +"] "); } return DataSource;} Object Lookupkey = DeterminecurrentLookupkey (); Diese Methode wird von uns implementiert, wo wir den wichtigsten Wert in ThreadLocal gespeichert werden. Nachdem Sie den Schlüssel erhalten haben, erhalten Sie die Datenquelle, die dem Schlüssel in der initialisierten MAP initialisiert entspricht, von After PropertieStieSet (). Der in ThreadLocal gespeicherte Schlüsselwert wird festgelegt, bevor die relevanten Methoden im Dienst über AOP aufgerufen werden. OK, hier ist es fertig!
3. Zusammenfassung
Aus diesem Artikel können wir die Kraft und Flexibilität von AOP erleben.
Das obige ist die Informationssortierung der MyBatis-Multi-Daten-Quellenverarbeitung. Ich hoffe, es kann Freunden in Not helfen