ฉันเขียนบล็อก "Spring+MyBatis+MySQL เพื่อสร้างเฟรมเวิร์กการเข้าถึงฐานข้อมูลแบบกระจาย" ก่อนที่จะอธิบายวิธีการเข้าถึงฐานข้อมูลหลายฐานข้อมูลผ่านแหล่งข้อมูลแบบไดนามิก Spring+MyBatis อย่างไรก็ตามโซลูชันก่อนหน้านี้มีข้อ จำกัด บางอย่าง (ยังอธิบายไว้ในบล็อกต้นฉบับ): ใช้เฉพาะกับสถานการณ์ที่จำนวนฐานข้อมูลมีขนาดเล็กและคงที่ ไม่มีอะไรจะทำเกี่ยวกับสถานการณ์ที่ฐานข้อมูลเพิ่มขึ้น
รูปแบบที่กล่าวถึงด้านล่างสามารถรองรับการเพิ่มฐานข้อมูลแบบไดนามิกและการลบและจำนวนไม่ จำกัด
การเตรียมสภาพแวดล้อมฐานข้อมูล
นี่คือ MySQL เป็นตัวอย่างการสร้างฐานข้อมูลแรก 3 ฐานข้อมูลสำหรับการทดสอบ ควรสังเกตว่าโซลูชันนี้ไม่ได้ จำกัด จำนวนฐานข้อมูลและรองรับการปรับใช้ฐานข้อมูลที่แตกต่างกันบนเซิร์ฟเวอร์ที่แตกต่างกัน ดังที่แสดงในรูป db_project_001, db_project_002, db_project_003
สร้างโครงการ Microservice Java Backend
สร้างโครงการ Maven Spring Boot:
config: คลาสการจัดการการกำหนดค่าแหล่งข้อมูล
DataSource: ตรรกะการจัดการแหล่งข้อมูลที่นำมาใช้ด้วยตัวเอง
DBMGR: จัดการความสัมพันธ์การแมประหว่างการเข้ารหัสโครงการและ IP ฐานข้อมูลและชื่อ (ส่วนนี้ของข้อมูลในโครงการจริงจะถูกเก็บไว้ในแคช REDIS และสามารถเพิ่มและลบได้แบบไดนามิก)
MAPPER: อินเทอร์เฟซการเข้าถึงฐานข้อมูล
รุ่น: โมเดลการทำแผนที่
REST: ส่วนต่อประสานที่เหลือปล่อยออกมาจาก Microservices ไปด้านนอกใช้ที่นี่สำหรับการทดสอบ
Application.yml: กำหนดค่าพารามิเตอร์ JDBC ของฐานข้อมูล
การใช้รหัสโดยละเอียด
1. เพิ่มการกำหนดค่าแหล่งข้อมูล
แพ็คเกจ com.elon.dds.config; นำเข้า javax.sql.datasource; นำเข้า org.apache.ibatis.session.sqlsessionfactory; นำเข้า org.mybatis.spring.sqlsessionfactorybean; นำเข้า org.mybatis.spring.spring.spring. org.springframework.beans.factory.annotation.qualifier; นำเข้า org.springframework.boot.autoconfigure.jdbc.datasourceBuilder; นำเข้า org.springframework.boot.context.properties.configrumenties; นำเข้า org.springframework.context.annotation.bean; นำเข้า org.springframework.context.annotation.configuration; นำเข้า com.elon.dds.datasource.dynamicDataSource;/*** การจัดการการกำหนดค่าแหล่งข้อมูล * * @author elon * @version 26 กุมภาพันธ์ 2018 * / @การกำหนดค่า @mapperscan (basepackages = "com.elon.dds.mapper", value = "sqlsessionfactory") คลาสสาธารณะ DataSourceConfig { /** * สร้างแหล่งข้อมูลตามพารามิเตอร์การกำหนดค่า ใช้คลาสย่อยที่ได้รับ * * @return Data Source */ @bean (name = "DataSource") @configurationProperties (คำนำหน้า = "Spring.datasource") DataSource Public GetDataSource () {DataSourceBuilder Builder = dataSourceBuilder.create (); builder.type (DynamicDatasource.class); return builder.build (); } /*** สร้างโรงงานเซสชัน ** @Param DataSource แหล่งข้อมูล* @return Session Factory*/ @Bean (name = "SQLSessionFactory") สาธารณะ SQLSessionFactory GetSQlSessionFactory (@qualifier ("DataSource") DataSource) Bean.setDatasource (แหล่งข้อมูล); ลอง {return bean.getObject (); } catch (exception e) {e.printstacktrace (); คืนค่า null; }}} 2. กำหนดแหล่งข้อมูลแบบไดนามิก
1) ก่อนเพิ่มคลาสข้อมูลข้อมูลฐานข้อมูลเพื่อแยกความแตกต่างการเข้าถึงฐานข้อมูลที่แตกต่างกัน
เนื่องจากเราสร้างฐานข้อมูลแยกต่างหากสำหรับโครงการต่าง ๆ เราจึงใช้การเข้ารหัสโครงการเป็นดัชนีของฐานข้อมูล Microservices รองรับการทำงานร่วมกันแบบหลายเธรดและใช้ตัวแปรเธรด
แพ็คเกจ com.elon.dds.datasource;/*** คลาสการจัดการข้อมูลประจำตัวฐานข้อมูล ใช้เพื่อแยกความแตกต่างฐานข้อมูลที่เชื่อมต่อกับแหล่งข้อมูล * * @author elon * @version 2018-02-25 */คลาสสาธารณะ dbidentifier {/** * ใช้การเข้ารหัสโครงการที่แตกต่างกันเพื่อแยกแยะฐานข้อมูล */private static threadlocal <String> projectCode = ใหม่ ThreadLocal <String> (); สตริงคงที่สาธารณะ getProjectCode () {return projectCode.get (); } โมฆะคงที่สาธารณะ setProjectCode (รหัสสตริง) {ProjectCode.Set (รหัส); -2) DynamicDataSource นั้นได้มาจาก DataSource ซึ่งมีการใช้การสลับการเชื่อมต่อฐานข้อมูลแบบไดนามิก
นำเข้า java.lang.reflect.field; นำเข้า java.sql.connection; นำเข้า java.sql.sqlexception; นำเข้า org.apache.logging.log4j.logManager นำเข้า org.apache.logging.log4j.logger; org.apache.tomcat.jdbc.pool.poolproperties; นำเข้า com.elon.dds.dbmgr.projectdbmgr;/*** กำหนดแหล่งข้อมูลที่ได้มาจากไดนามิก ได้มาจากแหล่งข้อมูลพื้นฐานที่ใช้งานด้วยตัวเองแบบไดนามิก * * @author elon * @version 2018-02-25 */คลาสสาธารณะ DynamicDataSource ขยาย DataSource {Logger แบบคงที่ส่วนตัว = logmanager.getLogger (DynamicDataSource.class); /*** วิธีนี้คือการเชื่อมต่อกับฐานข้อมูลที่แตกต่างกันเมื่อขอข้อมูลจากโครงการต่าง ๆ */ @Override การเชื่อมต่อสาธารณะ getConnection () {String projectCode = dBidentifier.getProjectCode (); // 1. รับ Data Source DataSource DDS = DDSholder.Instance (). getDDS (ProjectCode); // 2. สร้าง if (dds == null) {ลอง {dataSource newdds = initdds (ProjectCode); ddsholder.instance (). adddds (ProjectCode, newdds); } catch (unlegalArgumentException | unglemalAccessException e) {log.error ("แหล่งข้อมูลเริ่มต้นล้มเหลว ProjectCode:" + ProjectCode); คืนค่า null; }} dds = ddSholder.Instance (). getDDS (ProjectCode); ลอง {return dds.getConnection (); } catch (sqlexception e) {e.printstacktrace (); คืนค่า null; }} /*** คัดลอกคัดลอกด้วยวัตถุข้อมูลปัจจุบันเป็นเทมเพลต * * @return DDS * @THROWS unglegalAccessException * @THROWS unglemalArgumentException */ ข้อมูลส่วนตัวเริ่มต้น (String ProjectCode) โยน unledalArgumentException, ungleclAccessException {DataSource DDS = dataSource ใหม่ (); // 2. คัดลอกคุณสมบัติของ PoolConfiguration PoolProperties คุณสมบัติ = ใหม่ poolproperties (); ฟิลด์ [] pfields = poolproperties.class.getDeclaredFields (); สำหรับ (ฟิลด์ f: pfields) {f.setAccessible (จริง); ค่าวัตถุ = f.get (this.getPoolProperties ()); ลอง {f.set (คุณสมบัติ, ค่า); } catch (exception e) {log.info ("Set Value Fail. ชื่อ Attt:" + F.getName ()); ดำเนินการต่อ; }} dds.setpoolproperties (คุณสมบัติ); // 3. ตั้งค่าชื่อฐานข้อมูลและ IP (โดยทั่วไปพอร์ตชื่อผู้ใช้และรหัสผ่านจะได้รับการแก้ไขอย่างสม่ำเสมอ) สตริง urlformat = this.getUrl (); string url = string.format (urlformat, projectdbmgr.instance (). getDbip (ProjectCode), ProjectDbMgr.Instance (). getDbName (ProjectCode)); dds.seturl (url); ส่งคืน DDS; -3) ควบคุมการปล่อยการเชื่อมต่อข้อมูลผ่าน DDStimer (การเปิดตัวแหล่งข้อมูลที่ไม่ได้ใช้ซึ่งเกินเวลาที่กำหนด)
แพ็คเกจ com.elon.dds.datasource; นำเข้า org.apache.tomcat.jdbc.pool.datasource;/*** การจัดการตัวจับเวลาแหล่งข้อมูลแบบไดนามิก การเชื่อมต่อฐานข้อมูลที่ไม่มีการเข้าถึงเป็นเวลานานปิด * * @author elon * @version 25 กุมภาพันธ์ 2018 * /คลาสสาธารณะ ddstimer { /** * ระยะเวลาว่าง การเชื่อมต่อฐานข้อมูลที่ยังไม่ได้เข้าถึงมากกว่าเวลานี้จะถูกปล่อยออกมา ค่าเริ่มต้นคือ 10 นาที */ ส่วนตัวคงที่แบบคงที่ความยาวของเวลาว่าง = 10 * 60 * 1000; / *** แหล่งข้อมูลแบบไดนามิก*/ DATASOURCE DDS ส่วนตัว; / *** เวลาการเข้าถึงครั้งสุดท้าย*/ ช่วงเวลาที่ยาวนานส่วนตัว; DDStimer สาธารณะ (DataSource DDS) {this.dds = DDS; this.lastusetime = system.currentTimeMillis (); } / *** อัปเดตเวลาการเข้าถึงล่าสุด* / โมฆะสาธารณะ Refreshtime () {ล่าสุด = System.currentTimeMillis (); } /*** ตรวจพบว่าการเชื่อมต่อข้อมูลถูกปิดเนื่องจากหมดเวลาหรือไม่ * * @return True - หมดเวลา; FALSE - ไม่หมดเวลา*/ Public Boolean CheckandClose () {ถ้า (System.CurrentTimeMillis () - ล่าสุด> idleperiodtime) {dds.close (); กลับมาจริง; } return false; } Public DataSource getDds () {return dds; -4) เพิ่ม DDSholder เพื่อจัดการแหล่งข้อมูลที่แตกต่างกันและให้ข้อมูลการเพิ่มแหล่งข้อมูลและฟังก์ชั่นการสืบค้น
แพ็คเกจ com.elon.dds.datasource; นำเข้า java.util.hashmap; นำเข้า java.util.iterator; นำเข้า java.util.map นำเข้า java.util.map.entry; นำเข้า Java.util.timer; * * @author elon * @version 25 กุมภาพันธ์ 2018 * /คลาสสาธารณะ ddsholder { /** * จัดการรายการแหล่งข้อมูลแบบไดนามิก <การเข้ารหัสโครงการแหล่งข้อมูล> */ แผนที่ส่วนตัว <String, DDStimer> DDSMAP = HASHMAP ใหม่ <String, DDStimer> (); / *** แหล่งข้อมูลที่ไม่ได้ใช้เป็นระยะ ๆ ผ่านงานที่กำหนดเวลา*/ ตัวจับเวลาแบบคงที่ส่วนตัว Clearidletask = new Timer (); Static {Clearidletask.schedule (ใหม่ ClearidletimerTask (), 5000, 60 * 1000); - ส่วนตัว ddsholder () {} /** รับวัตถุซิงเกิลตัน* / อินสแตนซ์ ddsholder แบบคงที่สาธารณะ () {return ddsholderbuilder.instance; } /*** เพิ่มแหล่งข้อมูลแบบไดนามิก * * @param ProjectCode Project การเข้ารหัส * @param DDS DDS */ โมฆะที่ซิงโครไนซ์สาธารณะ (String ProjectCode, DataSource DDS) {DDStimer DDST = ใหม่ DDStimer (DDS); ddsmap.put (ProjectCode, DDST); } / *** การสืบค้นแหล่งข้อมูลแบบไดนามิก** @param ProjectCode การเข้ารหัสโครงการ* @return DDS* / DataSource ที่ซิงโครไนซ์สาธารณะ getDDS (String ProjectCode) {ถ้า (DDSMAP.CONTANSKEY (ProjectCode)) ddst.refreshtime (); ส่งคืน ddst.getdds (); } return null; } /*** แหล่งข้อมูลที่ชัดเจนที่หมดเวลาโดยไม่มีใคร */ public synchronized void clearidledds () {iterator <entry <string, ddstimer >> iter = ddsmap.entryset (). iterator (); สำหรับ (; iter.hasnext ();) {entry <string, ddstimer> entry = iter.next (); if (entry.getValue (). checkandclose ()) {iter.remove (); }}} / *** Singleton Artifact Class* @author Elon* @version 26 กุมภาพันธ์ 2018* / คลาสคงที่คลาสคงที่ DDSholderBuilder {อินสแตนซ์ DDSholder แบบคงที่ส่วนตัว = ใหม่ ddsholder (); -5) งาน Timer ClearidletimerTask ใช้เพื่อล้างแหล่งข้อมูลที่ไม่ได้ใช้งานเป็นประจำ
แพ็คเกจ com.elon.dds.datasource; นำเข้า java.util.timertask;/*** ล้างงานการเชื่อมต่อที่ไม่ได้ใช้งาน * * @author elon * @version 26 กุมภาพันธ์ 2018 */คลาสสาธารณะ ClearidletimerTask ขยาย Timertask {@Override โมฆะสาธารณะ Run () {ddsholder.instance (). Clearidledds (); -3. จัดการความสัมพันธ์การแมปการเข้ารหัสโครงการกับฐานข้อมูล IP และชื่อ
แพ็คเกจ com.elon.dds.dbmgr; นำเข้า java.util.hashmap; นำเข้า java.util.map;/*** การจัดการฐานข้อมูลโครงการ จัดเตรียมอินเทอร์เฟซเพื่อสอบถามชื่อฐานข้อมูลและ IP ตามการเข้ารหัสโครงการ * @author elon* @Version 25 กุมภาพันธ์ 2018* /ProjectDBMGR ชั้นเรียนสาธารณะ { /*** บันทึกความสัมพันธ์การแมประหว่างการเข้ารหัสโครงการและชื่อข้อมูล นี่คือรหัสยาก ในการพัฒนาจริงข้อมูลเชิงสัมพันธ์นี้สามารถบันทึกลงในแคช Redis; * การเพิ่มโครงการใหม่หรือการลบโครงการต้องใช้เฉพาะการอัปเดตแคช ในเวลานั้นอินเทอร์เฟซของคลาสนี้จะต้องได้รับการแก้ไขเพื่อรับข้อมูลจากแคช */ แผนที่ส่วนตัว <สตริง, สตริง> dbnamemap = ใหม่ hashmap <สตริง, สตริง> (); /*** บันทึกความสัมพันธ์การแมประหว่างการเข้ารหัสโครงการและ IP ฐานข้อมูล */ แผนที่ส่วนตัว <สตริงสตริง> dbipmap = ใหม่ hashmap <สตริงสตริง> (); PROMICTDBMGR () {DBNAMEMAP.PUT ("Project_001", "db_project_001"); dbnamemap.put ("project_002", "db_project_002"); dbnamemap.put ("project_003", "db_project_003"); dbipmap.put ("project_001", "127.0.0.1"); dbipmap.put ("project_002", "127.0.0.1"); dbipmap.put ("project_003", "127.0.0.1"); } project projectdbmgr สาธารณะ () {return projectdbmgrbuilder.instance; } // ในการพัฒนาจริงจะมีการเปลี่ยนแปลงเพื่อให้ได้สตริงสาธารณะ getDbName (String ProjectCode) {ถ้า (dbnamemap.containskey (ProjectCode)) {return dbnamemap.get (ProjectCode); } กลับ ""; } // ในการพัฒนาจริงเราเปลี่ยนไปรับสตริงสาธารณะ getDbip (สตริง ProjectCode) {ถ้า (dbipmap.containskey (ProjectCode)) {return dbipmap.get (ProjectCode); } กลับ ""; } คลาสคงที่คลาสคงที่ ProjectDBMGRBUILDER {Private Static ProjectDBMGR อินสแตนซ์ = ใหม่ ProjectDBMGR (); - 4. กำหนด Mapper สำหรับการเข้าถึงฐานข้อมูล
แพ็คเกจ com.elon.dds.mapper; นำเข้า java.util.list; นำเข้า org.apache.ibatis.annotations.mapper; นำเข้า org.apache.ibatis.annotations.result; นำเข้า org.apache.ibatis.annotations. com.elon.dds.model.user;/*** คำจำกัดความอินเตอร์เฟสการแมป mybatis ** @author elon* @version 26 กุมภาพันธ์ 2018*/ @mapperpublic อินเตอร์เฟส usermapper {/*** สอบถามข้อมูลผู้ใช้ทั้งหมด* @return รายการข้อมูลผู้ใช้*/@results (value = {@Result (คุณสมบัติ = "userId", คอลัมน์ = "id"), @Result }) @select ("เลือก ID, ชื่อ, อายุจาก tbl_user") รายการ <user> getusers ();} 5. กำหนดโมเดลวัตถุแบบสอบถาม
แพ็คเกจ com.elon.dds.model; ผู้ใช้ระดับสาธารณะ {ส่วนตัว int userId = -1; ชื่อสตริงส่วนตัว = ""; อายุ int ส่วนตัว = -1; @Override สตริงสาธารณะ toString () {return "ชื่อ:" + ชื่อ + "| อายุ:" + อายุ; } public int getUserId () {return userId; } โมฆะสาธารณะ setUserId (int userId) {this.userId = userId; } สตริงสาธารณะ getName () {ชื่อคืน; } โมฆะสาธารณะ setName (ชื่อสตริง) {this.name = name; } public int getage () {return Age; } การตั้งค่าโมฆะสาธารณะ (อายุ int) {this.age = อายุ; - 6. กำหนดอินเทอร์เฟซ restful สำหรับการสืบค้นข้อมูลผู้ใช้
แพ็คเกจ com.elon.dds.rest; นำเข้า java.util.list; นำเข้า org.springframework.beans.factory.annotation.autoWired; นำเข้า org.springframework.web.bind.annotation.requestmapping; org.springframework.web.bind.annotation.requestparam; นำเข้า org.springframework.web.bind.annotation.restcontroller; นำเข้า com.elon.dds.datasource.dbidentifier; อินเทอร์เฟซ * * @author elon * @version 26 กุมภาพันธ์ 2018 */ @restcontroller @requestmapping (value = "/user") คลาสสาธารณะ wsuser {@autowired ส่วนตัว Usermapper Usermapper; /*** สอบถามข้อมูลผู้ใช้ทั้งหมดในโครงการ** @param ProjectCode การเข้ารหัสโครงการ* @return รายชื่อผู้ใช้*/@requestmapping (value = "/v1/ผู้ใช้", method = requestmethod.get) รายการสาธารณะ <user> queeryuser (@requestparam (value = "projectCode" ส่งคืน usermapper.getusers (); -จำเป็นต้องรวมพารามิเตอร์ ProjectCode ไว้ในแต่ละแบบสอบถาม
7. เขียนรหัสเริ่มต้นสำหรับแอพสปริงบูต
แพ็คเกจ com.elon.dds; นำเข้า org.springframework.boot.springapplication; นำเข้า org.springframework.boot.autoconfigure.springbootapplication;/*** Hello World! * */@SpringBootapplicationPublic คลาสแอพ {โมฆะคงที่สาธารณะหลัก (สตริง [] args) {system.out.println ("สวัสดีโลก!"); springapplication.run (app.class, args); - 8. กำหนดค่าแหล่งข้อมูลใน Application.yml
ชื่อฐานข้อมูล IP และฐานข้อมูลใช้กับ %s สลับในการสืบค้นข้อมูลผู้ใช้
ฤดูใบไม้ผลิ: DataSource: URL: JDBC: MySQL: //%S: 3306/%S? useUnicode = true & characterencoding = UTF-8 ชื่อผู้ใช้: รหัสผ่านรูท: ไดรเวอร์-คลาส-ชื่อ: com.mysql.jdbc.driverlogging: config: classpath: log4j2.xml
แผนทดสอบ
1. สอบถามข้อมูล Project_001 และส่งคืนตามปกติ
2. สอบถามข้อมูล Project_002 และส่งคืนตามปกติ
สรุป
ด้านบนเป็นรหัสการใช้งานสำหรับการเข้าถึงหลายฐานข้อมูลผ่านแหล่งข้อมูลแบบไดนามิกการตั้งค่าสปริง ฉันหวังว่ามันจะเป็นประโยชน์กับทุกคน หากคุณมีคำถามใด ๆ โปรดฝากข้อความถึงฉันและบรรณาธิการจะตอบกลับทุกคนในเวลา ขอบคุณมากสำหรับการสนับสนุนเว็บไซต์ Wulin.com!