Vorwort
Dieser Artikel erzählt hauptsächlich die Geschichte von Springboot, die MyBatis, Druid und PageHelper integrieren und mehrere Datenquellen und Pagination implementieren. Unter ihnen integriert Springboot MyBatis, das in einem früheren Artikel beschrieben wurde, daher werde ich es hier nicht zu sehr erklären. Der Fokus liegt auf der Konfiguration von Druid und PageHelper in mehreren Datenquellen.
Druideneinführung und Verwendung
Schauen wir uns vor der Verwendung von Druid einen kurzen Blick auf Druid an.
Druid ist ein Datenbankverbindungspool. Druid kann derzeit der beste Datenbankverbindungspool sein! Es wird von Entwicklern für seine hervorragenden Funktionen, Leistung und Skalierbarkeit von großer Bedeutung.
Druid hat mehr als 600 Anwendungen auf Alibaba eingesetzt und seit mehr als einem Jahr den strengen Test des großflächigen Einsatzes in Produktionsumgebungen durchgeführt. Druid ist ein Datenbankverbindungs -Pool, der von Alibaba genannt wird, das mit dem Namen Monitoring!
Gleichzeitig ist Druid nicht nur ein Datenbankverbindungspool, der Kern von Druid enthält hauptsächlich drei Teile:
Die Hauptfunktionen von Druid sind wie folgt:
Ich werde nicht über die Einführung sprechen. Weitere Informationen finden Sie in der offiziellen Dokumentation.
Dann beginnen wir vorzustellen, wie man Druid benutzt.
Zunächst die Abhängigkeit von Maven, fügen Sie einfach das Druidenglas hinzu.
<Depopentcy> <gruppe> com.alibaba </gruppeId> <artifactId> druid </artifactId> <version> 1.1.8 </Version> </abhängig>
In Bezug auf die Konfiguration besteht die Hauptsache darin, es in application.Properties oder application.yml wie folgt hinzuzufügen.
Hinweis: Da ich hier zwei Datenquellen verwende, ist es nur ein wenig anders. Die Anweisungen für die Druidenkonfiguration sind unten bereits detailliert, sodass ich sie hier nicht erklären werde.
## Standarddatenquelle Master.DataSource.url = JDBC: MySQL: // localhost: 3306/Springboot? UseUnicode = True & charakteristisches Zeugen = Utf8 & duldMultiqueries = Tru emaster.datasource.username = rootmaster.datasource.password = 123456master.datasource.driverClassName = com.mysql.jdbc.driver ## Eine andere Datenquelle cluster.dataSource.url = jdbc: mysql: // localhost: 3306/Springboot_test? UseUnicode = True & charakteritätszündig = utf8cluster.datasource.username = rootcluster.datasource.datasource Informationen für den Verbindungspool # Initialisieren Sie Größe, Minimum, maximaler Spring.DataSource feder.datasource.maxwait = 60000 # Konfigurieren Sie, wie lange es dauert, um ein Erkennungsintervall auszuführen, um Leerlaufverbindungen zu erkennen, die in Millisekunden fing.datasource.TimeBetweenevictionRunsmillis = 60000 # Konfigurieren Sie die minimale Zeit, um im Pool zu überleben, in Milliskunden Feder. Spring.DataSource.ValidationQuery = Select 1 aus Dual Spring.DataSource.TestHeIdle = True Spring.DataSource.Testonborrow = false Spring.dataSource.TestonReturn = false # öffnen # öffnen Sie PSCache und spezifizieren Sie die Größe von pScache in jeder Verbindung. Spring.DataSource.MaxpoolProparedStatementPerconnectionSize = 20 # Filter für die Überwachungsstatistik abgefangen. Nach dem Entfernen kann die Überwachungsschnittstelle SQL nicht gezählt werden. 'Wall' wird für Firewall Spring verwendet. Langsame SQL -Aufzeichnungen Spring.DataSource.ConnectionProperties = druid.stat.MergeNSQL = true; druid.stat.slowsqlmillis = 5000
Nachdem wir die Konfigurationsdatei erfolgreich hinzugefügt haben, schreiben wir druidenbezogene Klassen.
Zunächst ist die MasterDataSourceConfig.java -Klasse die Konfigurationsklasse der Standarddatenquelle.
@Configuration@mapperscan (Basepackages = MasterDataSourceConfig.package, SQLSessionFactoryRef = "MastersqlSessionFactory") öffentliche Klasse MasterDataSourceConfig {static Final String package = "com.pancm.dao.master"; static final String mapper_location = "classPath: mapper/master/*. xml"; @Value ("$ {master.dataSource.url}") private String url; @Value ("$ {master.dataSource.username}") privater String -Benutzername; @Value ("$ {master.dataSource.Password}") privates Zeichenfolge Passwort; @Value ("$ {Master.DataSource.DriverClassName}") private String -TreiberClassName; @Value ("$ {spring.datasource.initialSize}") private init initialSize; @Value ("$ {spring.datasource.minidle}") Private int Minidle; @Value ("$ {spring.datasource.maxactive}") private int maxactive; @Value ("$ {spring.datasource.maxwait}") private int maxwait; @Value ("$ {Spring.DataSource.TimeBetweenevictionRunsmillis}") Private int timeBetweenevictionRunsmillis; @Value ("$ {Spring.DataSource.MineVictableIdletImemillis}") Private int mineVictableIdletImemillis; @Value ("$ {Spring.DataSource.ValidationQuery}") private String validationQuery; @Value ("$ {Spring.DataSource.TestIDEIDLE}") privater boolescher Test, während Idle; @Value ("$ {Spring.DataSource.TestonBorrow}") Private boolean testonborrow; @Value ("$ {Spring.DataSource.TestonReturn}") Private boolean testonReturn; @Value ("$ {spring.datasource.poolpreparedStatements}") Private boolean PoolpreparedStatements; @Value ("$ {spring.datasource.maxpoolpoolpreparedStatementPerConnectionSize}") private int maxpoolProparedStatementPerconConnectionSize; @Value ("$ {spring.datasource.filters}") private String -Filter; @Value ("{Spring.DataSource.ConnectionProperties}") Private String ConnectionProperties; @Bean (name = "masterdataSource") @Primary public DataSource MasterDataSource () {druidDataSource dataSource = new DruidDataSource (); DataSource.seturl (URL); DataSource.Setusername (Benutzername); DataSource.SetPassword (Passwort); DataSource.SetDriverClassName (TRAVERCLASSNAME); // spezifische Konfigurationsdatenource.SetInitialSize (InitialSize); DataSource.SetMinidle (Minidle); DataSource.SetMaxActive (maxActive); DataSource.SetMaxwait (maxwait); DataSource.SettimeBetweenevictionRunsmillis (TimeBetweenevictionRunsmillis); DataSource.SetMineVictableIdletImemillis (minevictableIdletImemillis); DataSource.setValidationQuery (ValidationQuery); dataSource.settest -idle (test während der IDLE); DataSource.settestonborrow (testonborrow); DataSource.settestonReturn (testonReturn); DataSource.setPoolPreparedStatements (PoolpreparedStatements); DataSource.setMaxPoolProparedStatementPerConnectionSize (maxpoolPreparedStatementPerConnectionSize); try {dataSource.setFilters (Filter); } catch (sqlexception e) {e.printstacktrace (); } dataSource.setConnectionProperties (ConnectionProperties); DataSource zurückgeben; } @Bean (name = "masterTransactionManager") @Primary public DataSourcetransactionManager masterTransactionManager () {neue DataSourcetRansActionManager zurückgeben (MasterDataSource ()); } @Bean (name = "mastersqlSessionFactory") @primary public SQLSessionFactory MastersqlSessionFactory (@Qualifier ("MasterdataSource") DataSource MasterDataSource -Ausnahme {endgültig sqlSessionFactoryBean Sessionfactory SessionFactory.SetDataSource (MasterDataSource); SessionFactory.SetMapperLocations (neuer PathMatchingResourcePenNResolver () .getResources (MasterDataSourceConfig.mapper_location)); return SessionFactory.getObject (); }}Diese beiden Anmerkungen werden unten erklärt:
**@primary **: Logo diese Bean, wenn es mehrere ähnliche Bean -Kandidaten gibt, die Bohne
Priorität wird berücksichtigt. Achten Sie bei der Konfiguration mehrerer Datenquellen, dass es eine primäre Datenquelle geben muss, und verwenden Sie @Primary, um die Bean zu markieren.
**@mapperscan **: Scannen Sie die Mapper -Schnittstelle und die Containerverwaltung.
Es ist zu beachten, dass SQLSessionFactoryRef eine einzigartige Instanz von SQLSessionFactory darstellt.
Nach Abschluss der obigen Konfiguration kann Druid als Verbindungspool verwendet werden. Druid ist jedoch nicht nur ein Verbindungspool. Es kann auch gesagt werden, dass es sich um eine Überwachungsanwendung handelt. Es wird mit einer Webüberwachungsschnittstelle geliefert, auf der die SQL-bezogenen Informationen eindeutig angezeigt werden können.
Mithilfe der Überwachungsfunktion von Druid in Springboot müssen Sie nur StatViewServlet- und WebStatFilter -Klassen schreiben, um Registrierungsdienste und Filterregeln zu implementieren. Hier können wir diese beiden mit **@configuration ** und **@bean ** zusammen schreiben.
Aus Gründen des einfachen Verständnisses werden die relevanten Konfigurationsanweisungen auch im Code geschrieben, sodass ich hier nicht in Details eingehen werde.
Der Code ist wie folgt:
@ConfigurationPublic Class DruidConfiguration {@Bean public ServletRegistrationBean druidStatViewServle () {// Registrieren Service ServletRegistrationBean ServletRegistrationBean = new servletregistrationBean (new statviewServlet (), "/druid/*"); // Whitelist (repräsentiert leer, kann auf alle zugegriffen werden, von Commas für mehrere IPS) servletRegistrationBean.addinitParameter ("erlauben", "127.0.0.1"); // IP Blacklist (Dony hat Vorrang vor, wenn es eine gemeinsame Existenz gibt) ServletregistrationBean.addinitParameter ("Deny", "127.0.0.2"); // Setzen Sie den Benutzernamen und Kennwort für den Anmeldung und Kennwort. servletRegistrationBean.addinitParameter ("LoginPassword", "123456"); // ob es möglich ist, Daten zurückzusetzen. servletRegistrationBean.addinitParameter ("zurückgezogen", "falsch"); Return servletRegistrationBean; } @Bean public FilterregistrationBean druidStatFilter () {FilterregistrationBean FilterregistrationBean = new FilterregistrationBean (New WebStatFilter ()); // Filterregeln FilterregistrationBean.addurlpatterns ("/*"); // Formatinformationen hinzufügen, die nicht ignoriert werden müssen, filterregistrierungsbean.addinitparameter ("Ausschlüsse", "*.js,*. Gif,*. Jpg,*. Png,*. CSS,*. ICO,/Druid/*"); System.out.println ("Druid -Initialisierung erfolgreich!"); Rückgabefilterregistrierungsbean; }}Starten Sie nach dem Schreiben das Programm, geben Sie in den Browser ein:
Multi-Daten-Quellkonfiguration
Führen Sie vor der Durchführung einer Multi-Daten-Quellkonfiguration die folgenden Skripte in den MySQL-Datenbanken von Springboot bzw. Springboot_test aus.
-Skript der Springboot-Bibliothek Erstellen Sie Tabelle "t_user" ("ID" int (11) nicht null auto_increment comment 'self-lcement id "," name "varchar (10) Standardnull-Kommentar' Name '," Alter "int (2) Standardnull-Kommentar" Age ", Primärschlüssel (" Id "). Erstellen Sie Tabelle `t_student` (` id` int (11) nicht null auto_increment, `name` varchar (16) Standardnull,` ay` int (11) Standardnull, Primärschlüssel (`id`)) Engine = innoDb auto_increment = 2 Default charset = utf8Hinweis: Um faul zu sein, wird die Struktur der beiden Tabellen gleich gemacht! Aber es wird den Test nicht beeinflussen!
Die Informationen zu diesen beiden Datenquellen wurden in der Anwendung konfiguriert.
Hier konzentrieren wir uns auf die Konfiguration der zweiten Datenquelle. Es ähnelt dem MasterDataSourceConfig.java oben. Der Unterschied besteht darin, dass es sich von der **@primären ** Annotation und dem Namen unterscheidet, ohne die **@primäre ** Annotation zu verwenden. Es ist zu beachten, dass MasterDataSourceConfig.java -Paket und Mapper genau auf das Verzeichnis scans und gilt für die zweite Datenquelle hier. Dann ist der Code wie folgt:
@Configuration@mapperscan (basepackages = clusterDataSourceConfig.package, SQLSessionFactoryRef = "cluStersQlSessionFactory") öffentliche Klasse clusterDataSourceConfig {static Final String Package = "com.pancm.dao.cluster"; static Final String mapper_location = "classPath: mapper/cluster/*. xml"; @Value ("$ {cluster.dataSource.url}") private String url; @Value ("$ {cluster.dataSource.username}") privater String -Benutzername; @Value ("$ {cluster.dataSource.Password}") privates Zeichenfolgenkennwort; @Value ("$ {cluster.dataSource.driverClassName}") private String -Treiberklasse; // wie MasterDataSourceConfig, hier @Bean (name = "clusterDataSource") public DataSource clusterDataSource () {druidDataSource dataSource = new druiddataSource (); DataSource.seturl (URL); DataSource.Setusername (Benutzername); DataSource.SetPassword (Passwort); DataSource.SetDriverClassName (TRAVERCLASS); // wie MasterDataSourceConfig, hier ... Datenquelle zurückgeben; } @Bean (name = "clusterTransactionManager") public DataSourcetransactionManager clusterTransactionManager () {return neuer DataSourcetransactionManager (clusterDataSource ()); } @Bean (name = "clustersqlSessionFactory") public sqlSessionFactory CluStersQlSessionFactory (@Qualifier ("clusterDataSource") DataSource clusterDataSource -Ausnahme {endgültig sqlSessionFactoryBean Sessionfactory = new SQLSessionFactoryBean (); SessionFactory.SetDataSource (clusterDataSource); SessionFactory.SetMapperLocations (neuer PathMatchingResourcePufaltResolver (). getResources (clusterDatasourceConfig.mapper_location)); return SessionFactory.getObject (); }} Starten Sie nach erfolgreichem Schreiben der Konfiguration das Programm und führen Sie Tests durch.
Verwenden Sie Schnittstellen, um Daten in Springboot- und Springboot_test -Bibliotheken hinzuzufügen.
t_user
Post http: // localhost: 8084/api/user {"name": "zhang san", "Alter": 25} {"name": "li si", "Alter": 25} {"Name": "Wang Wu", "Alter": 25}t_student
Post http: // localhost: 8084/api/student {"name": "student a", "Alter": 16} {"name": "student b", "älter": 17} {"name": "student c", "ay: 18}Rufen Sie nach erfolgreichem Hinzufügen von Daten verschiedene Schnittstellen zur Abfrage an.
fragen:
Holen Sie sich http: // localhost: 8084/api/user? Name = li si
zurückkehren:
{"ID": 2, "Name": "Li Si", "Alter": 25}fragen:
Holen Sie sich http: // localhost: 8084/api/student? Name = student c
zurückkehren:
{"ID": 1, "Name": "Student C", "Alter": 16}Aus den Daten können wir feststellen, dass mehrere Datenquellen erfolgreich konfiguriert wurden.
Implementierung von PageHelper -Pagination
PageHelper ist ein Paging -Plugin für MyBatis, was sehr nützlich ist! Sehr empfohlen hier! ! !
PageHelper ist sehr einfach zu bedienen. Sie müssen nur die Abhängigkeit von PageHelper in Maven hinzufügen.
Die Abhängigkeiten von Maven sind wie folgt:
<Depopenty> <gruppe> com.github.pageHelper </GroupId> <artifactId> PageHelper-Spring-Boot-Starter </artifactid> <version> 1.2.3 </Version> </abhängig>
Hinweis: Ich verwende hier die Springboot -Version! Andere Versionen können auch verwendet werden.
Nach dem Hinzufügen von Abhängigkeiten müssen Sie nur die folgende Konfiguration oder den folgenden Code hinzufügen.
Der erste Typ wird in application.properties oder application.yml hinzugefügt
PageHelper: HelperDialect: Mysql OffsetaPagenum: True RowboundsWithCount: Richtig angemessen: Falsch
Der zweite Typ wird in mybatis.xml -Konfiguration hinzugefügt
<bean id = " name = "properties"> <wert> helperDialect = mysql offsetaspagenum = true rowboundsWithCount = true vernünftig = false </value> </property> </bean> </array> </property> </bean>
Der dritte Typ wird im Code hinzugefügt und initialisiert es, wenn das Programm mithilfe der Annotation **@bean ** gestartet wird.
@Bean public pageHelper pageHelper () {pageHelper pageHelper = new PageHelper (); Eigenschaften Eigenschaften = neue Eigenschaften (); // Datenbankeigenschaften.setProperty ("HelperDialect", "MySQL"); //, ob der Parameterversatz als pagenum Eigenschaften verwendet wird. // ob die Count -Eigenschaften abfragt. // ob Rationalisierung von Paginationseigenschaften.setProperty ("vernünftig", "falsch"); pageHelper.setProperties (Eigenschaften); }Da wir hier mehrere Datenquellen verwenden, ist die Konfiguration hier geringfügig unterschiedlich. Wir müssen es in SessionFactory konfigurieren. Hier stellen wir entsprechende Änderungen an MasterdatasourceConfig.java vor. Fügen Sie bei der MastersQLSessionFactory -Methode den folgenden Code hinzu.
@Bean (name = "MastersqlSessionFactory") @Primary public SQLSessionFactory MastersqlSessionFactory (@Qualifier ("MasterdataSource") dataSource MasterDataSource Ausnahme {endgültig SQLSessionFactoryBean SessionFactory = new sqlSessionFactoryBeanBeanBeanBeanBeanBeanBeanBeanBean. SessionFactory.SetDataSource (MasterDataSource); SessionFactory.SetMapperLocations (neuer PathMatchingResourcePenNResolver () .getResources (MasterDataSourceConfig.mapper_location)); // Pagination Plug-In Interceptor Interceptor = new pageInterceptor (); Eigenschaften Eigenschaften = neue Eigenschaften (); // Datenbankeigenschaften.setProperty ("HelperDialect", "MySQL"); //, ob der Parameterversatz als pagenum Eigenschaften verwendet wird. // ob die Count -Eigenschaften abfragt. // ob Rationalisierung von Paginationseigenschaften.setProperty ("vernünftig", "falsch"); interceptor.setProperties (Eigenschaften); SessionFactory.setPlugins (neuer Interceptor [] {Interceptor}); return SessionFactory.getObject (); }HINWEIS: Wenn auch andere Datenquellen paging werden möchten, lesen Sie bitte den oben genannten Code.
Was Sie hier beachten müssen, ist der angemessene Parameter, was bedeutet, dass Rationalisierung von Paging geschlagen wird, und der Standardwert ist falsch. Wenn dieser Parameter auf true eingestellt ist, wird die erste Seite beim Pagenum <= 0 und Pagenum> Seiten (wenn die Gesamtzahl überschreitet) abfragen, die letzte Seite wird abfragt. Wenn standardmäßig falsch ist, basiert die Abfrage direkt auf den Parametern.
Nachdem Sie PageHelper eingestellt haben, müssen Sie, wenn Sie es verwenden, nur PageHelper.startPage(pageNum,pageSize); Vor der Abfrage SQL. Wenn Sie die Gesamtzahl wissen möchten, kaufen Sie sie nach der SQL -Anweisung der Abfrage und fügen Sie page.getTotal() .
Codebeispiel:
öffentliche Liste <T> findByListentity (t Entity) {list <T> list = null; try {page <?> page = pageHelper.startPage (1,2); System.out.println (getClassName (Entity)+"Setzen Sie zwei Daten auf der ersten Seite!"); list = getMapper (). FindByListentity (Entity); System.out.println ("Es gibt insgesamt:"+page.gettotal ()+"Daten, und die tatsächliche Rückgabe lautet:"+list.size ()+"zwei Daten!"); } catch (Ausnahme e) {logger.Error ("Abfrage"+getClassName (Entity)+"fehlgeschlagen! Der Grund ist:", e); } Rückgabeliste; }Nachdem der Code geschrieben wurde, beginnt der endgültige Test.
Fragen Sie alle Daten in der T_USER -Tabelle ab und paginieren Sie sie.
fragen:
Holen Sie sich http: // localhost: 8084/api/user
zurückkehren:
[{"ID": 1, "Name": "Zhang San", "Alter": 25}, {"ID": 2, "Name": "Li Si", "Age": 25}]Konsolendruck:
Fangen Sie an, abfragen ...
Der Benutzer legt zwei Datenstücke auf der ersten Seite fest!
2018-04-27 19: 55: 50.769 Debug 6152 --- [IO-8084-EXEC-10] CPDMUSERDAO.FindbyListentity_Count: ==> Vorbereitung: Wählen Sie Count (0) aus T_USER, wobei 1 = 1
2018-04-27 19: 55: 50.770 Debug 6152 --- [IO-8084-EXEC-10] CPDMUSERDAO.FindbyListentity_Count: ==> Parameter:
2018-04-27 19: 55: 50.771 Debug 6152 --- [IO-8084-EXEC-10] CPDMUSERDAO.FindbyListentity_Count: <== Total: 1
2018-04-27 19: 55: 50.772 Debug 6152 --- [IO-8084-EXEC-10] cpdao.master.userdao.findbyListentity: ==> Vorbereitung: Wählen Sie ID, Name, Alter, Alter von T_USER, bei dem 1 = 1 Grenzwert?
2018-04-27 19: 55: 50.773 Debug 6152 --- [IO-8084-EXEC-10] cpdao.master.userdao.findbyListentity: ==> Parameter: 2 (Ganzzahl)
2018-04-27 19: 55: 50.774 Debug 6152 --- [IO-8084-EXEC-10] cpdao.master.Userdao.findbyListentity: <== Total: 2: 2
Es gibt insgesamt: 3 Datenstücke, und die tatsächliche Rendite lautet: 2 zwei Daten!
Fragen Sie alle Daten in der T_Student -Tabelle ab und paginieren Sie sie.
fragen:
Holen Sie sich http: // localhost: 8084/api/student
zurückkehren:
[{"ID": 1, "Name": "Student A", "Alter": 16}, {"ID": 2, "Name": "Student B", "Alter": 17}]Konsolendruck:
Fangen Sie an, abfragen ...
Studnet legt zwei Datenstücke auf der ersten Seite fest!
2018-04-27 19: 54: 56.155 Debug 6152 --- [NIO-8084-EXEC-8] cpdcsfindbyListentity_count: ==> Vorbereitung: Wählen Sie Graf (0) aus T_student, wobei 1 = 1
2018-04-27 19: 54: 56.155 Debug 6152 --- [NIO-8084-EXEC-8] cpdcsfindbyListentity_count: ==> Parameter:
2018-04-27 19: 54: 56.156 Debug 6152 --- [NIO-8084-EXEC-8] cpdcsfindbyListentity_count: <== Total: 1
2018-04-27 19: 54: 56.157 Debug 6152 --- [NIO-8084-EXEC-8] CPDCSTUDENTDAO.FINDBYLISTENTITE: ==> Vorbereitung: Wählen Sie ID, Name, Alter, Alter von t_student, wo 1 = 1 Grenzwert?
2018-04-27 19: 54: 56.157 Debug 6152 --- [NIO-8084-EXEC-8] CPDCSTUDENTDAO.FINDBYLISTENTITY: ==> Parameter: 2 (Ganzzahl)
2018-04-27 19: 54: 56.157 Debug 6152 --- [NIO-8084-EXEC-8] cpdcstudentdao.findbyListentity: <== Total: 2
Es gibt insgesamt: 3 Datenstücke, und die tatsächliche Rendite lautet: 2 zwei Daten!
Schauen wir uns nach Abschluss der Abfrage die Überwachungsschnittstelle von Druid an. Geben Sie in den Browser ein: http://127.0.0.1:8084/druid/index.html
Die Operation Records können deutlich gesehen werden!
Wenn Sie mehr über Druid erfahren möchten, können Sie die offizielle Dokumentation überprüfen!
Abschluss
Dieser Artikel war endlich fertig. Beim Schreiben des Codes habe ich viele Probleme gestoßen und dann langsam versuchte und Informationen gefunden, um ihn zu lösen. In diesem Artikel werden diese verwandten Verwendungen nur sehr kurz eingeführt und kann in den tatsächlichen Anwendungen komplizierter sein.
Referenzartikel: https://www.bysocket.com/?p=1712
Durid Offizielle Adresse: https://github.com/alibaba/druid
PageHelper Offizielle Adresse: https://github.com/pageHelper/mybatis-pageHelper
Ich habe das Projekt auf GitHub eingestellt: https://github.com/xuwujing/springboot. Sie können es auch lokal herunterladen: Klicken Sie hier
Zusammenfassen
Das obige ist der gesamte Inhalt dieses Artikels. Ich hoffe, dass der Inhalt dieses Artikels einen gewissen Referenzwert für das Studium oder die Arbeit eines jeden hat. Wenn Sie Fragen haben, können Sie eine Nachricht zur Kommunikation überlassen. Vielen Dank für Ihre Unterstützung bei Wulin.com.