Saya menulis blog "Spring+MyBatis+MySQL untuk membangun kerangka kerja akses database terdistribusi" sebelum menjelaskan cara mengakses beberapa database melalui sumber data dinamis konfigurasi mybatis. Namun, solusi sebelumnya memiliki beberapa keterbatasan (juga dijelaskan dalam blog asli): hanya berlaku untuk situasi di mana jumlah database kecil dan tetap. Tidak ada yang bisa dilakukan tentang situasi di mana dinamika database meningkat.
Skema yang disebutkan di bawah ini dapat mendukung penambahan dan penghapusan database dinamis, dan jumlahnya tidak terbatas.
Persiapan Lingkungan Database
Berikut ini adalah contoh, pertama membangun 3 database secara lokal untuk pengujian. Perlu dicatat bahwa solusi ini tidak membatasi jumlah database, dan mendukung penyebaran database yang berbeda pada server yang berbeda. Seperti yang ditunjukkan pada gambar, db_project_001, db_project_002, db_project_003.
Bangun Proyek Layanan Mikrosa Backend Java
Buat Proyek Maven Spring Boot:
Konfigurasi: Kelas Manajemen Konfigurasi Sumber Data.
DataSource: Logika manajemen sumber data yang diimplementasikan dengan sendirinya.
DBMGR: Mengelola hubungan pemetaan antara pengkodean proyek dan IP dan nama basis data (bagian data ini dalam proyek aktual disimpan dalam cache Redis dan dapat ditambahkan dan dihapus secara dinamis).
Mapper: Antarmuka akses database.
Model: Model Pemetaan.
Istirahat: Antarmuka Restful yang dirilis oleh Microservices ke luar, digunakan di sini untuk pengujian.
Application.yml: Mengkonfigurasi parameter JDBC dari database.
Implementasi kode terperinci
1. Tambahkan Konfigurasi Sumber Data
Paket com.elon.dds.config; import javax.sql.datasource; impor org.apache.iathis.session.sqlsessionfactory; impor org.mybatis.spring.sqlsessionFactoryBean; impor org.mybatis.spring.notation.mappappapation.mporation.mappapring. org.springframework.beans.factory.annotation.Qualifier; impor org.springframework.boot.autoconfigure.jdbc.datasourceBuilder; impor org.springframework.boot.context.properies.onfigurationpropertion; impor org.springframework.context.annotation.bean; impor org.springframework.context.annotation.configuration; Impor com.elon.dds.datasource.dynamicDataSource;/*** Manajemen konfigurasi sumber data. * * @author elon * @Version 26 Februari 2018 * / @configuration @mapperscan (Basepackages = "com.elon.dds.mapper", value = "sqlSessionFactory") DataSourConfig kelas publik { /** * Buat sumber data berdasarkan parameter konfigurasi. Gunakan subkelas turunan. * * @return data sumber */ @bean (name = "DataSource") @configurationProperties (prefix = "spring.datasource") public DataSource getDataSource () {DataSourceBuilder Builder = DataSourceBuilder.create (); builder.type (DynamicDataSource.class); return builder.build (); } /*** Buat pabrik sesi. * * @param dataSource Data source* @return Session Factory*/ @Bean(name="sqlSessionFactory") public SqlSessionFactory getSqlSessionFactory(@Qualifier("dataSource") DataSource dataSource) { SqlSessionFactoryBean bean = new SqlSessionFactoryBean(); bean.setDataSource (DataSource); coba {return bean.getObject (); } catch (Exception e) {E.PrintStackTrace (); kembali nol; }}} 2. Tentukan sumber data dinamis
1) Pertama, tambahkan kelas identitas basis data untuk membedakan akses basis data yang berbeda.
Karena kami membuat database terpisah untuk proyek yang berbeda, kami menggunakan pengkodean proyek sebagai indeks database. Layanan Microservices mendukung concurrency multi-threaded dan menggunakan variabel utas.
Paket com.elon.dds.datasource;/*** kelas manajemen identitas database. Digunakan untuk membedakan berbagai basis data yang terhubung ke sumber data. * * @Author elon * @Version 2018-02-25 */kelas publik DBIdentifier {/** * Gunakan pengkodean proyek yang berbeda untuk membedakan database */private statis threadlocal <string> proyekcode = threadlocal baru <string> (); public static string getProjectCode () {return ProjectCode.get (); } public static void setProjectCode (kode string) {ProjectCode.set (kode); }}2) DynamicDataSource berasal dari DataSource, di mana pengalihan dinamis koneksi database diimplementasikan
import java.lang.reflect.Field;import java.sql.Connection;import java.sql.SQLException;import org.apache.logging.log4j.LogManager;import org.apache.logging.log4j.Logger;import org.apache.tomcat.jdbc.pool.DataSource;import org.apache.tomcat.jdbc.pool.poolproperties; import com.elon.dds.dbmgr.projectdbmgr;/*** Tentukan kelas Data Data Data. Berasal dari sumber data dasar, diimplementasikan secara dinamis dengan sendirinya. * * @Author elon * @Version 2018-02-25 */kelas publik DynamicDataSource memperluas DataSource {private static logger log = logManager.getLogger (DynamicDataSource.class); /*** Metode ini adalah untuk terhubung ke database yang berbeda saat meminta data dari berbagai proyek. */ @Override Public Connection getConnection () {String ProjectCode = DBIdentifier.GetProjectCode (); // 1. Dapatkan sumber data DataSource DDS = DDSholder.Instance (). GetDDS (ProjectCode); // 2. Buat if (dds == null) {coba {DataSource newDDS = initdds (proyekcode); Ddsholder.instance (). AddDDS (ProjectCode, NewDDS); } catch (IllegalArgumentException | IllegalAccessException e) {LOG.Error ("Init Data Source Fail. ProjectCode:" + ProjectCode); kembali nol; }} dds = ddsholder.Instance (). getDds (ProjectCode); coba {return dds.getConnection (); } catch (sqlexception e) {e.printstacktrace (); kembali nol; }} /*** Salin salinan dengan objek data saat ini sebagai templat. * * @return dds * @Throws IllegalAccessException * @throws IllegalArgumentException */ Private DataSource initdds (String ProjectCode) melempar IllegalArgumentException, IllegalAccessException {DataSource DDS = DataSource baru (); // 2. Salin properti PoolConfiguration PoolProperties Properti = PoolProperties baru (); Bidang [] pFields = poolproperties.class.getDecledFields (); untuk (bidang f: pFields) {f.setAccessible (true); Nilai objek = f.get (this.getPoolProperties ()); coba {f.set (properti, nilai); } catch (Exception e) {log.info ("Set Nilai Gagal. Nama ATHRA:" + f.getName ()); melanjutkan; }} dds.setPoolProperties (properti); // 3. Atur nama database dan IP (umumnya, port, nama pengguna dan kata sandi secara seragam diperbaiki) string urlformat = this.getUrl (); String url = string.format (urlFormat, proyekdbmgr.instance (). GetDbip (proyekcode), proyekdbmgr.instance (). GetDbname (proyekcode)); dds.setUrl (url); mengembalikan DDS; }}3) Kontrol rilis koneksi data melalui DDSTimer (rilis sumber data yang tidak digunakan yang telah melebihi waktu yang ditentukan)
Paket com.elon.dds.datasource; impor org.apache.tomcat.jdbc.pool.datasource;/*** Manajemen timer sumber data dinamis. Koneksi basis data yang tidak memiliki akses untuk waktu yang lama ditutup. * * @author elon * @Version 25 Februari 2018 * /kelas publik ddstimer { /** * periode waktu idle. Koneksi basis data yang belum diakses lebih dari waktu ini akan dirilis. Standarnya adalah 10 menit. */ private static long IdlePeriodTime = 10 * 60 * 1000; / *** Sumber Data Dinamis*/ Private DataSource DDS; / *** waktu akses terakhir*/ private long lasteTime; ddstimer publik (DataSource DDS) {this.dds = dds; this.lastusetime = system.currentTimemillis (); } / *** Diperbarui waktu akses terbaru* / public void refreshTime () {lastetime = system.currentTimeMillis (); } /*** Deteksi apakah koneksi data ditutup karena batas waktu. * * @return true - time out; false - tidak diatur waktu*/ public boolean checkAndAndClose () {if (system.currentTimeMillis () - lasteusime> iDleperiodtime) {dds.close (); Kembali Benar; } return false; } public DataSource getDDS () {return dds; }}4) Tambahkan DDSholder untuk mengelola sumber data yang berbeda dan menyediakan penambahan sumber data dan fungsi kueri
Paket com.elon.dds.datasource; import java.util.hashmap; import java.util.iterator; import java.util.map; import java.util.map.entry; import java.util.timer; impor org.apache.tomcat.jdbc.pool.pool. * * @author elon * @Version 25 Februari 2018 * /kelas publik ddsholder { /** * Kelola daftar sumber data dinamis. <Pengkodean Proyek, Sumber Data> */ Private Map <String, DDStimer> DDSMAP = HashMap baru <String, DDStimer> (); / *** Sumber data yang tidak digunakan secara berkala melalui tugas waktu*/ timer statis pribadi clearidletask = new timer (); static {clearidletask.schedule (new clearidleTimertask (), 5000, 60 * 1000); }; private ddsholder () {} /** Dapatkan objek singleton* / public static ddsholder instance () {return ddsholderbuilder.instance; } /*** Tambahkan sumber data dinamis. * * @param ProjectCode Project Encoding * @param DDS DDS */ public disinkronkan void addDDS (String ProjectCode, DataSource DDS) {ddstimer ddst = ddstimer baru (DDS); ddsmap.put (ProjectCode, DDST); } / *** Sumber Data Dinamis Kueri** @param ProjectCode Project Encoding* @return DDS* / DataSource GetDDS yang disinkronkan publik (String ProjectCode) {if (ddsmap.containskey (proyek)) {ddstimer ddst = ddsmap.get (proyekcode); ddst.refreshtime (); return ddst.getdds (); } return null; } /*** Sumber data yang jelas yang diuraikan tanpa siapa pun. */ public disinkronkan void clearidledds () {iterator <entry <string, ddstimer >> iter = ddsmap.entryset (). iterator (); untuk (; iter.hasnext ();) {entri <string, ddstimer> entri = iter.next (); if (entry.getValue (). checkAndClose ()) {iter.remove (); }}} / *** Singleton Artifact Class* @Author Elon* @Version 26 Februari 2018* / Private Static Class DDSholderbuilder {private static DDSholder instance = new DDSholder (); }}5) Tugas Pengatur Waktu ClearIdleTimertask digunakan untuk menghapus sumber data idle secara teratur
Paket com.elon.dds.datasource; import java.util.timertask;/*** Hapus tugas koneksi idle. * * @Author elon * @Version 26 Februari 2018 */kelas publik ClearIdletImertask memperluas timertask {@Override public void run () {ddsholder.instance (). clearidledds (); }}3. Kelola hubungan pemetaan pengkodean proyek dengan IP dan nama database
Paket com.elon.dds.dbmgr; import java.util.hashmap; impor java.util.map;/*** Manajemen basis data proyek. Menyediakan antarmuka untuk meminta nama basis data dan IP berdasarkan pengkodean proyek. * @Author elon* @Version 25 Februari 2018* /kelas publik ProjectDbmgr { /*** Simpan hubungan pemetaan antara pengkodean proyek dan nama data. Ini kode yang sulit. Dalam pengembangan aktual, data relasional ini dapat disimpan ke cache Redis; * Menambahkan proyek baru atau menghapus proyek hanya perlu memperbarui cache. Pada saat itu, antarmuka kelas ini hanya perlu dimodifikasi untuk mendapatkan data dari cache. */ peta pribadi <string, string> dbnamemap = new HashMap <string, string> (); /*** Simpan hubungan pemetaan antara pengkodean proyek dan IP basis data. */ peta privat <string, string> dbipmap = new HashMap <string, string> (); ProjectDBMgr () {dbnamemap.put ("Project_001", "DB_PROject_001"); dbnamemap.put ("proyek_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"); } public static ProjectDbmgr instance () {return ProjectDBMGrBuilder.Instance; } // Dalam pengembangan aktual, diubah untuk mendapatkan string publik getDbName (String ProjectCode) {if (dbnamemap.containsKey (proyekcode)) {return dbnamemap.get (proyekcode); } kembali ""; } // Dalam pengembangan aktual, kami berubah untuk mendapatkan getDbip string publik (String ProjectCode) {if (dbipmap.containskey (proyekcode)) {return dbipmap.get (proyekcode); } kembali ""; } private static class ProjectDbMgrBuilder {private static ProjectDbmgr instance = new ProjectDbmgr (); }} 4. Tentukan mapper untuk akses basis data
Paket com.elon.dds.mapper; impor java.util.list; impor org.apache.ibatis.annotations.mapper; impor org.apache.ibatis.annotations.Result; impor org.apache.ibatis.annotations.Result; impor org.apache.ibatis.annotation. Definisi antarmuka pemetaan mybatis. * * @author elon * @version February 26, 2018*/@Mapperpublic interface UserMapper{ /** * Query all user data* @return User data list*/ @Results(value= { @Result(property="userId", column="id"), @Result(property="name", column="name"), @Result(property="age", column="age") }) @Select ("Pilih ID, Nama, Usia dari TBL_USER") Daftar <User> getUsers ();} 5. Tentukan model objek kueri
paket com.elon.dds.model; pengguna kelas publik {private int userid = -1; name string pribadi = ""; private int usia = -1; @Override public string toString () {return "name:" + name + "| usia:" + usia; } public int getUserId () {return userId; } public void setUserId (int userId) {this.userid = userId; } public string getName () {return name; } public void setName (name string) {this.name = name; } public int getage () {usia kembali; } public void setage (int usia) {this.age = usia; }} 6. Tentukan antarmuka yang tenang untuk meminta data pengguna
Paket com.elon.dds.rest; impor java.util.list; impor org.springframework.beans.factory.annotation.Autowired; impor org.springframework.web.bind.annotation.requestmapping; impor org.springframework.bind.notation.requestmapping; org.springframework.bind.nnotation.requestmapping; org.springframework.web.bind.annotation.requestparam; impor org.springframework.web.bind.annotation.restcontroller; impor com.elon.dds.dataSource.dbidentifier; impor com.elon.dds.mapper.usermapper; comelon. * * @author elon * @Version 26 Februari 2018 */ @restcontroller @requestMapping (value = "/user") kelas publik wsuser {@autowired private usermapper userMapper; /** * Query all user information in the project* * @param projectCode Project encoding* @return User list*/ @RequestMapping(value="/v1/users", method=RequestMethod.GET) public List<User> queryUser(@RequestParam(value="projectCode", required=true) String projectCode) { DBIdentifier.setProjectCode(projectCode); return usermapper.getUsers (); }}Diperlukan bahwa parameter kode proyek dimasukkan dalam setiap kueri.
7. Tulis kode startup untuk aplikasi boot musim semi
Paket com.elon.dds; impor org.springframework.boot.springapplication; impor org.springframework.boot.autoconfigure.springbootApplication;/*** halo dunia! * */@SpringbootApplicationPublic Class App {public static void main (string [] args) {System.out.println ("Hello World!"); Springapplication.run (app.class, args); }} 8. Konfigurasikan sumber data di application.yml
IP database dan nama basis data digunakan dengan %s. Beralih secara dinamis dalam meminta data pengguna.
Spring: DataSource: URL: JDBC: mysql: //%s: 3306/%s? UseUnicode = true & characterencoding = UTF-8 Nama Pengguna: Kata Sandi Root: Driver-Class-Name: com.mysql.jdbc.driverlogging: config: classpath: log4j2.xml
Rencana Uji
1. Permintaan data Project_001 dan kembalikan secara normal
2. Permintaan data Project_002 dan kembalikan secara normal
Meringkaskan
Di atas adalah kode implementasi untuk mengakses beberapa database melalui sumber data dinamis konfigurasi Spring Boot. Saya harap ini akan membantu semua orang. Jika Anda memiliki pertanyaan, silakan tinggalkan saya pesan dan editor akan membalas semua orang tepat waktu. Terima kasih banyak atas dukungan Anda ke situs web Wulin.com!