ข้อกำหนดด้านการทำงานคือ บริษัท จำเป็นต้องสร้างแพลตฟอร์มปฏิบัติการขนาดใหญ่:
1. แพลตฟอร์มการดำเนินงานมีฐานข้อมูลของตัวเองรักษาฟังก์ชั่นพื้นฐานเช่นผู้ใช้บทบาทเมนูชิ้นส่วนและสิทธิ์
2. แพลตฟอร์มการดำเนินงานยังต้องให้การดำเนินการแบ็คเอนด์ของบริการอื่น ๆ ที่แตกต่างกัน (บริการ A, บริการ B) และฐานข้อมูลของบริการ A และบริการ B เป็นอิสระ
ดังนั้นแพลตฟอร์มการทำงานจะต้องเชื่อมต่ออย่างน้อยสามไลบรารี: ไลบรารีการดำเนินการไลบรารีและไลบรารี B และหวังว่าจะเปลี่ยนไปใช้แหล่งข้อมูลที่สอดคล้องกันโดยอัตโนมัติสำหรับการร้องขอฟังก์ชั่นแต่ละรายการโดยอัตโนมัติ
ขั้นตอนที่ 1: กำหนดค่าแหล่งข้อมูลหลายแหล่ง
1. กำหนดแหล่งข้อมูล:
แหล่งข้อมูลที่ฉันใช้คือ Druiddatasource ของอาลีบาบา (ไม่เป็นไรกับ DBCP นี่คืออะไรก็ตาม) การกำหนดค่ามีดังนี้:
<!-op dataSource-> <bean id = "opDataSource" init-method = "init" destroy-method = "close"> <property name = "url" value = "$ {db.master.url}" /> <property name = "username redention" value = "$ {db.master.password}" /> <property name = "driverclassName" value = "$ {db.master.driver}" /> <ชื่อคุณสมบัติ = "ค่าเริ่มต้น" value = "5" /> <property name = "maxactive" value = "100" /> < name = "validationQuery" value = "เลือก 'x'" /> <property name = "testOnBorrow" value = "false" /> <property name = "TestOnReturn" value = "false" /> <property name = "testharialle" value = "true" /> value = "300000" /> <property name = "RemoveAbandOned" value = "true" /> <property name = "RemoveAbandOnedTimeOut" value = "1800" /> <ชื่อคุณสมบัติ = "logabandoned" value = "true" /> <! name = "connectionProperties" value = "config.decrypt = true" /> </ebean> <!-Servera DataSource-> <bean id = "ServerAdatasource" init-method = "init" destroy-method = "close"> <property name = "value =" $ {db.servera. value = "$ {db.servera.master.user}" /> <property name = "รหัสผ่าน" value = "$ {db.servera.master.password}" /> <property name = "driverclassname" value "$ {db.servera.master.driver}" value = "100" /> <property name = "minidle" value = "10" /> <property name = "MaxWait" value = "60000" /> <property name = "ValidationQuery" value = "select 'x'" /> <property name = "testonBorrow" value = "false" /> name = "testOnReturn" value = "false" /> <property name = "testharyIdle" value = "true" /> <property name = "TimebetweeneVictionRunsmillis" value = "600000" /> <property name = "MinevictableidletimeLis" value = "300000" /> value = "1800" /> <property name = "logabandoned" value = "true" /> <!-กำหนดค่าตัวกรองสำหรับการตรวจสอบสถิติการสกัดกั้น-> <property name = "filters" value = "config, merestat, Wall, log4j2" /> <property name = "connectionProperties id = "ServerBDataSource" init-method = "init" destroy-method = "close"> <property name = "url" value = "$ {db.serverb.master.url}" /> <property name = "username" value = "$ {db.serverb.master.user value = "$ {db.serverb.master.password}" /> <property name = "driverclassname" value = "$ {db.serverb.master.driver}" /> <property name = "ค่าเริ่มต้น" ค่า = "5" /> <property name = "maxactive" value = "60000" /> <property name = "validationQuery" value = "เลือก 'x'" /> <property name = "testOnBorrow" value = "false" /> <property name = "testOnReturn" value = "false" /> <property name = "testharely name = "minevictableidletimeMillis" value = "300000" /> <property name = "removeAbandOned" value = "true" /> <property name = "removeAbandOnedTimeOut" value = "1800" /> <property name = "logabandoned" value = "true" /> <! value = "config, mergestat, wall, log4j2" /> <property name = "ConnectionProperties" value = "config.decrypt = true" /> </ebean>ฉันกำหนดค่าแหล่งข้อมูลสามแหล่ง: OpDataSource (แหล่งข้อมูลของแพลตฟอร์มปฏิบัติการเอง), ServerAdatasource และ ServerBDataSource
2. กำหนดค่า multipleDataSource
MultipleDataSource เทียบเท่ากับหนึ่งพร็อกซีสำหรับแหล่งข้อมูลสามแหล่งข้างต้น เมื่อมันถูกรวมเข้ากับ Spring/Mybatis อย่างแท้จริง MultipleDataSource และการใช้งานที่กำหนดค่าแยกต่างหากจะไม่แตกต่างกัน:
<!-การรวม Spring MyBatis: กำหนดค่า multipleDataSource-> <bean id = "sqlsessionfactory"> <ชื่อคุณสมบัติ = "dataSource" ref = "multipleDataSource" /> <! <value> classpath*:/sqlmapperxml/*. xml </value> <value> classpath*:/sqlmapperxml/*/*. xml </value> </list> </คุณสมบัติ> <property name = "configlocation" value = "classpath: xml/mybatis-config.xml value = "com.xxx.platform.model" /> <property name = "globalConfig" ref = "globalconfig" /> <property name = "ปลั๊กอิน"> <array> <! value = "alidruid"/> </ebean> </sarray> </property> </ebean> <!-การใช้งาน MyBatis Dynamic-> <bean id = "MappersCannerConfigurer"> <! name = "SQLSessionFactoryBeanName" value = "SQLSessionFactory"> </คุณสมบัติ> </ebean> <!-MP การกำหนดค่าส่วนกลาง-> <bean id = "globalConfig"> <property name = "idType" ค่า = "0"/> <bean id = "transactionManager"> <property name = "dataSource" ref = "multipleDataSource"> </porement> </ebean>
หลังจากทำความเข้าใจกับที่ตั้งของ MultipleDataSource ให้มุ่งเน้นไปที่วิธีการใช้งาน MultipleDataSource ไฟล์กำหนดค่ามีดังนี้:
<bean id = "multipleDataSource"> <property name = "defaultTargetDataSource" ref = "opDataSource" /> <property name = "targetDataSources"> <map> <entry key = "OpDataSource" Value-ref = "OpDataSource" /> key = "serverbdatasource" value-ref = "ServerBdataSource"/> </perty> </porement> </ebean>
รหัส Java ที่ใช้งานมีดังนี้และไม่จำเป็นต้องมีคำอธิบายมากเกินไปและชัดเจนมากในภาพรวม:
นำเข้า org.springframework.jdbc.datasource.lookup.abstractroutingDatasource;/** * * @classname: multipledataSource * @description: กำหนดค่าแหล่งข้อมูลหลายแหล่ง <br> * @author: yuzhu.peng * @date AbstractroutingDataSource {ส่วนตัวคงที่ threadLocal <String> dataSourceKey = ใหม่ MandleItableThreadLocal <String> (); โมฆะคงที่สาธารณะ setDataSourceKey (สตริง DataSource) {dataSourceKey.set (DataSource); } @Override วัตถุที่ได้รับการป้องกัน DECININECURRENTLOOKUPKEY () {ส่งคืน dataSourceKey.get (); } โมฆะคงที่สาธารณะ removedatasourceKey () {dataSourceKey.Remove (); -สืบทอดมาจาก abstractroutingDataSource ของฤดูใบไม้ผลิใช้วิธีการเชิงนามธรรม dectracturrentlookupkey วิธีนี้จะกำหนดแหล่งข้อมูลแหล่งข้อมูลสำหรับการเชื่อมต่อนี้ทุกครั้งที่ได้รับการเชื่อมต่อฐานข้อมูล คุณสามารถเห็นรหัสสปริงที่ชัดเจน:
/*รับการเชื่อมต่อ*/ การเชื่อมต่อสาธารณะ getConnection () พ่น sqlexception {return deMinetArgetDataSource (). getConnection (); } dataSource dataSource deninetArgetDataSource () {assert.notnull (this.resolvedDataSources, "เราเตอร์ DataSource ไม่ได้เริ่มต้น"); /*DECENINECURRENTLOOKUPKENT นี่คืออินเทอร์เฟซนามธรรมโดยได้รับชื่อแหล่งข้อมูลเฉพาะ*/ OBJECT LOOKUPKEY = DECININECURRENTLOOKUPKEY (); DataSource DataSource = (DataSource) this.resolvedDataSources.get (lookupkey); if ((dataSource == null) && (((this.lenientfallback) || (lookupkey == null)))) {dataSource = this.resolvedDefaultDataSource; } if (dataSource == null) {โยน unlueLstateException ใหม่ ("ไม่สามารถกำหนดแหล่งข้อมูลเป้าหมายสำหรับคีย์การค้นหา [" + lookupkey + "]"); } ส่งคืนแหล่งข้อมูล; } /*อินเตอร์เฟสบทคัดย่อ: นั่นคืออินเตอร์เฟสที่ดำเนินการโดย MultipleDataSource* / วัตถุนามธรรมที่ได้รับการป้องกัน dectracturrentlookupkey ();ขั้นตอนที่ 2: สลับแหล่งข้อมูลทุกครั้งที่มีการร้องขอ (ระดับวิธีการบริการ)
แนวคิดการใช้งานคือการใช้แนวคิด AOP ของ Spring เพื่อสกัดกั้นการเรียกใช้วิธีการแต่ละครั้งจากนั้นสลับคีย์ของข้อมูลใน MultipleDataSource ตามชื่อเส้นทางโดยรวมของวิธีการ โครงการของเราสำหรับการดำเนินงานของบริการที่แตกต่างกันนั่นคือฐานข้อมูลที่แตกต่างกันเป็นอิสระจากกัน ไม่แนะนำให้โทรหาแหล่งข้อมูลที่แตกต่างกันในวิธีการบริการเดียวกัน ด้วยวิธีนี้เราจำเป็นต้องพิจารณาแบบไดนามิกว่าต้องวางความถี่ของการสลับไว้ในระดับ DAO นั่นคือระดับ SQL หรือไม่ นอกจากนี้การจัดการธุรกรรมไม่สะดวก
มาดูการใช้งาน AOP ของแหล่งข้อมูลการสลับแบบไดนามิก:
นำเข้า java.lang.reflect.proxy; นำเข้า org.apache.commons.lang.classutils; นำเข้า org.aspectj.lang.joinpoint; นำเข้า org.aspectj.lang.annotation.aspect; นำเข้า org.aspectj.lang.annotation.before * * @author yuzhu.peng * @since 2018-01-15 * / @ @quant @order (1) คลาสสาธารณะ MultipleDataSourceInterceptor { /** * interceptor ให้ความสนใจเป็นพิเศษกับการเปลี่ยนแหล่งข้อมูลก่อนที่จะขอคลาสการดำเนินธุรกิจทั้งหมด เนื่องจากมีการใช้แหล่งข้อมูลหลายแหล่งจึงเป็นการดีที่สุดที่จะเรียก Mapper เฉพาะใน *ServiceImpl มิฉะนั้นเมื่อเรียกตารางที่ไม่ใช่แหล่งข้อมูลเริ่มต้นข้อยกเว้นที่ไม่มีอยู่ในตารางจะได้รับการรายงาน** @param joinpoint* @throws throwable*/ @Before ("การดำเนินการ (* com.xxxx.platform.service ..*. serviceImpl.* (.. ))") JoinPoint.getTarget (). getClass (); String className = clazz.getName (); if (classutils.isassignable (clazz, proxy.class)) {classname = joinpoint.getSignature (). getDeclaringTypename (); } // ตั้งค่าแหล่งข้อมูลเซิร์ฟเวอร์ด้วยชื่อคลาสมิฉะนั้นค่าเริ่มต้นคือแหล่งข้อมูลในพื้นหลังถ้า (classname.contains (". servera.")) {multipleDataSource.setDataSourceKey (dbconstant.data_source_servera); } อื่นถ้า (classname.contains (". serverb.")) {multipleDataSource.setDataSourceKey (dbconstant.data_source_serverb); } else {multipleDataSource.setDataSourceKey (dbconstant.data_source_op); }} /*** เมื่อการดำเนินการเสร็จสมบูรณ์หากแหล่งข้อมูลปัจจุบันถูกปล่อยออกมาหากไม่ได้เปิดตัวความขัดแย้งของแหล่งข้อมูลจะเกิดขึ้นเมื่อคลิกบ่อยครั้ง มันเป็นตารางของแหล่งข้อมูลอื่น แต่จะทำงานไปยังแหล่งข้อมูลอื่น รายงานไม่มีอยู่** @param joinpoint* @throws throwable*/ @after ("Execution (* com.xxxx.service ..*.* ServiceImpl.* (.. ))") โมฆะสาธารณะ removedatasoruce -สกัดกั้นวิธีการบริการทั้งหมดตัดสินว่าฟังก์ชันแหล่งข้อมูลใดเป็นชื่อที่มีคุณสมบัติครบถ้วนของวิธีการแล้วเลือกแหล่งข้อมูลที่เกี่ยวข้อง หลังจากการแจกแจงเสร็จสมบูรณ์ให้ปล่อยแหล่งข้อมูลปัจจุบัน โปรดทราบว่าฉันใช้ @order ของสปริงคำอธิบายประกอบและฉันจะพูดถึงเรื่องต่อไปเมื่อกำหนด AOPs หลายรายการคำสั่งซื้อมีประโยชน์มาก
อื่น:
ในตอนแรกโครงการไม่ได้แนะนำการทำธุรกรรมดังนั้นทุกอย่างก็โอเค คุณสามารถเข้าถึงแหล่งข้อมูลที่ถูกต้องได้ทุกครั้ง หลังจากเข้าร่วมการจัดการธุรกรรมของฤดูใบไม้ผลิคุณไม่สามารถสลับแหล่งข้อมูลแบบไดนามิกได้ (ดูเหมือนว่าการทำธุรกรรมจะไม่ได้ผล แต่ทั้งสองไม่ถูกต้องในเวลาเดียวกัน) ต่อมาฉันพบว่าเหตุผลคือลำดับการดำเนินการของ AOP ดังนั้นฉันจึงใช้คำสั่งฤดูใบไม้ผลิที่กล่าวถึงข้างต้น:
คำสั่งที่เล็กลงการดำเนินการเป็นอันดับแรก ณ จุดนี้คุณไม่เพียงสามารถสลับแหล่งข้อมูลแบบไดนามิก แต่ยังใช้ธุรกรรมได้สำเร็จ (ในแหล่งข้อมูลเดียวกัน)
ข้างต้นเป็นเนื้อหาทั้งหมดของบทความนี้ ฉันหวังว่ามันจะเป็นประโยชน์ต่อการเรียนรู้ของทุกคนและฉันหวังว่าทุกคนจะสนับสนุน wulin.com มากขึ้น