序文
この記事は、主にSpringが物事をどのようにサポートするか、そしてSpringがMyBatisを組み合わせたときにデータベースのThings機能を単に実装する方法を記録します。以下ではあまり言いません。詳細な紹介を一緒に見てみましょう。
CASE1:2つのテーブルの状況をサポートします
最初に2つのテーブル、1つのユーザーテーブルと1つのストーリーテーブルを準備します。構造は次のとおりです。
テーブル「ユーザー」( `id` int(11)unsigned not null auto_increment、` name 'varchar(20)null default' 'comment' username '、 `pwd` varchar(26)noll default' 'コメント'パスワード '、` isdeleted` tinyint(1)notl defult' '' 0 '0' varchar(13) NOT NULL DEFAULT '0', PRIMARY KEY (`id`), KEY `name` (`name`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;CREATE TABLE `story` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `userId` int(20) unsigned NOT NULL DEFAULT '0' COMMENT 'author' userID', `name` varchar(20) NOT NULL DEFAULT 「コメント」著者」、「タイトル `varchar(26)nolt null default ''コメントパスワード '、「ストーリーコメント「ストーリーコンテンツ」、「isdeleted」tinyint(1)noll default' 0 ''、` redured` varchar(13)notl default '0' '、 `updated` varchar(13)not null'`( `id`( `ユーザー) Engine = InnoDBデフォルトcharset = utf8mb4;
私たちの状況は、ユーザーが名前を変更する場合、両方のテーブルの名前を一緒に変更する必要があり、不整合が許可されていないことです。
ケース2:シングルテーブルのサポート
送金、1人のユーザーがお金を削減し、別のユーザーがお金を増やします
テーブル「Money`」( `id` int(11)unsigned not null auto_increment、` name` varchar(20)null default '' comment 'username'、 `money` int(26)not not not '' 'comment' money '、` isdeleted`tinyint(1)not null default' '' not '' 0 ' varchar(13)null default '0'、プライマリキー( `id`)、key` name`( `name`))エンジン= innodbデフォルトcharset = utf8mb4;
上記のケースと比較して、これは簡単です。次の例は、主にこれに基づいて説明されています。 case1に関しては、拡張する必要があります。
まず、対応するDAOおよびエンティティを実装します
@Datapublic Class MoneyEntityはSerializable {private static final long serialversionuid = -7074788842783160025l; private int id;プライベート文字列名;プライベートインクマネー;プライベートINT ISDELETED; private int created; private int updated;}パブリックインターフェイスMoneydao {Moneyentity QueryMoney(@param( "id")int userid); //お金を追加すると、ネガティブな場合、それはお金を減らすことを意味しますint incrementmoney(@param( "id")int userid、@param( "addmoney")int addmoney);}対応するマッパーファイルはです
<?xml version = "1.0" encoding = "utf-8"?> <!doctype mapper public " - // mybatis.org//dtd mapper 3.0 // en" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace = "com.git.hui.demo.mybatis.mapper.moneydao"> <sql id = "Moneyentity"> id、 `name`、` money`、 `sedured`、` created`、 `updated` </sql> <select id =" querymoney " resultType = "com.git.hui.demo.mybatis.entity.moneyentity"> select <inclid = "mayleantity"/> from money from money where id =#{id} </select> <update id = "incrementmoney"MyBatis接続データソースの対応する構成
<bean> <プロパティ名= "locations"> <value> classpath*:jdbc.properties </value> </property> </bean> <bean id = "datasource" init-method = "init-method =" close "> <property name =" driverclassname "value =" $ {dright} name = "username" value = "$ {username}"/> <property name = "password" value = "$ {password}"/> <property name = "filters" value = "stat"/> <property name = "maxactive" value = "20"/> <プロパティ名= "イニシャルサイズ"値= "1" Value = "1"/> <プロパティ名= "timeevictionrunsmillis" value = "60000"/> <プロパティ名= "minevictableidletimemillis" value = "300000"/> <プロパティ名= "validationquery" value = "select 'x'" "/> <プロパティname =" testhale "/"/> <"/> <"/> <"fall"/> <"/>> <property name"/> <"/> name = "testonborrow" value = "false"/> <プロパティ名= "testonreturn" value = "false"/> <プロパティ名= "poolpreparedStatements" value = "true"/> <プロパティ名= "maxpoolpreparedStatementperconnectionsizesize" value = "50/> </bean> < ref = "dataSource"/> <! - Mapperファイルを指定 - > <プロパティ名= "MapperLocations" value = "classpath*:mapper/*。xml"/> </bean> <! - 指定スキャンdao-> <bean>オンラインクエリを通じて、春のものを管理する4つの方法があります。デモンストレーションは、各メソッドを再生する方法を1つずつ示し、実際のプロジェクトで選択する方法を確認します
TransactionTemplateを介して複数のdb操作の管理を実現するプログラミングシング管理
a。実装
次に、転送ケースを次のように実装できます
@RepositoryPublic Class Codedemo1 {@Autowired Private Moneydao Moneydao; @autowired private transactionTemplate TransactionTemplate; / ** *転送 * * @param inuserid * @param outuserid * @param paymoney * @param status 0は通常の転送を示します。 status){transactionTemplate.execute(new TransactionCallbackwithoutresult(){protected void dointransactionwithoutresult(transactionStatus transactionStatus){MoneyEntity Entity = MoneyDao.QueryMoney(Outuserid); MoneyDao.IncrementMoney(autuserid、outuserid、Status); } //以下はすべて関連するテストケースプライベートボイドテストケース(最終int inuserid、final int outuserid、final int status){if(status == 1){throw new IllegalargumentException( "Transfer Exception !!!"); } else if(status == 2){addmoney(inserid); {thread.sleep(3000); } catch(arturnedexception e){e.printstacktrace(); }} else if(status == 3){addmoney(outuserid); {thread.sleep(3000); } catch(arturnedexception e){e.printstacktrace(); }}} public void addMoney(final int userid){system.out.printf( "internally addmoney:" + system.currenttimemillis());新しいスレッド(new runnable(){public void run(){moneydao.incrementmoney(userid、200); system.out.println( "sub modify success! }}主に上記の変換方法を見てください。物事のカプセル化は、TransactionTemplateを通じて内部的に実現されます。内部的には3つのdB操作、1つのクエリと2つの更新があります。特定の分析については後で説明します。
上記のコードは比較的簡単です。注意する必要がある唯一のことは、TransactionTemplate Beanの定義方法です。 XMLファイルと以前のファイルを貼り付けない場合は、キーコードを貼り付けてください。 1つはDataSourceに基づいて作成されたTransactionManagerで、もう1つはTransactionManagerに基づいて作成されたTransactionTemplateです。
<! - プログラミングシングス - > <bean id = "transactionmanager"> <プロパティ名= "dataSource" ref = "dataSource"/> </bean> <bean id = "transactionTemplate"> <プロパティ名= "transactionManager" ref = "transactionmanager"/>> </bean> </bean>
b。テストケース
通常のデモンストレーションの状況、デモンストレーションには例外がなく、並行性の状況は考慮されていません
@runwith(springjunit4classrunner.class)@contextconfiguration({"classpath*:spring/service.xml"、 "classpath*:test-datasource1.xml"})public class codedemo1test {@autowied private codemo1 codedemo1; @autowired private moneydao moneydao; @test public void testtransfor(){system.out.println( "--------------------"); System.out.println( "id:1 money =" + moneydao.querymoney(1).getmoney()); system.out.println( "id:2 money =" + moneydao.querymoney(2).getmoney()); codedemo1.transfor(1、2、10、0); System.out.println( "--------------------"); System.out.println( "id:1 money =" + moneydao.querymoney(1).getmoney()); system.out.println( "id:2 money =" + moneydao.querymoney(2).getmoney()); }}出力は次のとおりです。両方のアカウントのお金に問題はありません
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -
ID:1 Money = 10000
ID:2マネー= 50000
転送が完了しました!今:1526130394266
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -
ID:1 Money = 10010
ID:2マネー= 49990
異常は、譲渡プロセス中に発生します。特に、譲渡人がお金を差し引き、受信者がお金を受け取っていない場合、つまりケースのステータスが1である場合。
// @testpublic void testtransforexception(){system.out.println( "---------------------"); System.out.println( "id:1 money =" + moneydao.querymoney(1).getmoney()); system.out.println( "id:2 money =" + moneydao.querymoney(2).getmoney()); try {codedemo1.transfor(1、2、10、1); } catch(Exception e){e.printstacktrace(); } system.out.println( "-----------------------"); System.out.println( "id:1 money =" + moneydao.querymoney(1).getmoney()); System.out.println( "id:2 money =" + moneydao.querymoney(2).getMoney();};}この点で、譲渡人からお金を返し、次のように出力したいと考えています。どちらも変更されていないことがわかりました。
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -
ID:1 Money = 10010
ID:2マネー= 49990
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -
ID:1 Money = 10010
java.lang.illegalargumentexception:転送例外!!!
... //例外情報を省略します
ID:2マネー= 49990
ステータスが2の場合、譲渡人のお金が差し引かれ、受取人のお金が受け取られていないことを意味します。誰かが200人を受取人に移しました。現時点では、MySQLのロックメカニズムによると、他の人の転送をすぐに受信する必要があります(受取人のアカウントがロックされていないため)、金額に問題はないはずです。
出力の結果は次のとおりです。
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -
ID:1 Money = 10010
ID:2マネー= 49990
右側の##はメモです:譲渡プロセス中に、お金はすぐに預けられ、内部にお金が追加されていません:1526130827480
成功を修正してください!今:1526130827500
##送金はお金を節約した後に完了します!今:1526130830488
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -
ID:1お金= 10220
ID:2マネー= 49980
ステータスが3の場合、それは譲渡人のお金が差し引かれ、受取人のお金が受け取られておらず、誰かが譲渡人に200人を転送したことを意味します。現時点では、転送者の記録と書き込みロックが追加されているため、+200の成功の前に転送を提出するのを待つことしかできません。もちろん、最終金額も同じでなければなりません。
出力の結果は次のとおりです
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -
ID:1お金= 10220
ID:2マネー= 49980
右側の##はメモです:私は内部的にお金を節約しましたが、すぐに成功しませんでした
##転送が完了するまで、すぐに成功します。 2つのタイムスタンプにお金を追加することに注意してください:1526131101046
転送が完了しました!今:1526131104051
成功を修正してください!今:1526131104053
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -
ID:1お金= 10230
ID:2マネー= 50170
c。まとめ
これまでのところ、プログラミングは例で実証されています。上記のプロセスから、SQLに関連するものを書くのと同じ感覚を人々に与えます。
トランザクションを開始します。
- これは、TransactionTemplate#実行メソッド内のロジックです
- つまり、物事を管理する必要があるSQLのセット専念;
次の3つは宣言的なもの管理であり、それぞれの管理クラスをTransactionProxyFactoryBeanに追加する必要があるため、あまり使用されていません。
a。実装
トランザクションテンプレートを殺害し、前のSQLロジックと比較して内部SQLロジックを削除することに加えて、基本的に違いはないことがわかりました。
Public Class Factorybeandemo2 {@autowired private Moneydao Moneydao; / ** *転送 * * @param inuserid * @param outuserid * @param paymoney * @param status 0は通常の転送を示します。1は例外が内部にスローされたことを示します。ステータス){MoneyEntity Entity = MoneyDao.QueryMoney(Outuserid); if(entity.getMoney()> paymoney){//お金を転送できます// first moneydao.incrementmoney(outuserid、-paymoney); Testcase(Inuserid、outuserid、status); // moneydao.incrementMoney(inserid、paymoney)にお金を追加します。 system.out.println( "転送が完了しました! }} private void testcase(final int inuserid、final int outuserid、final int status){if(status == 1){throw new IllegalargumentException( "Transfer Exception !!!"); } else if(status == 2){addmoney(inserid); {thread.sleep(3000); } catch(arturnedexception e){e.printstacktrace(); }} else if(status == 3){addmoney(outuserid); {thread.sleep(3000); } catch(arturnedexception e){e.printstacktrace(); }}} public void addMoney(final int userid){system.out.println( "internal add money:" + system.currenttimemillis());新しいスレッド(new runnable(){public void run(){moneydao.incrementmoney(userid、200); system.out.println( "sub modify success! }}重要なポイントは、TransactionProxyBeanFactoryを構成する必要があることです。 BeanFactoryは、自分で豆を作成する手段であることを知っています。関連するXML構成は次のとおりです
<! - プログラミングシングス - > <bean id = "transactionmanager"> <プロパティ名= "dataSource" ref = "dataSource"/> </bean> <bean id = "factorybeandemo2"/> <! - ビジネスレイヤーのプロキシを構成 - > <bean id = "factorybeandemoproxy"> < ref = "factorybeandemo2" /> <! - トランザクションマネージャーを挿入 - > <プロパティ名= "transactionManager" ref = "transactionManager" /> <! - トランザクションのプロパティを挿入 - > <プロパティ名= "トランザクションアットトリビューズ"> <props> <! - propフォーマット: *トランザクションの提案 *読み取り:トランザクションをロールバックする例外* +例外:どの例外がトランザクションをロールバックしないか - > <! - このキーはターゲットクラスのメソッドに対応します - > <prop key = "transfor"> propagation_required </prop> <! - <prop key = "prop key =" transfer " <! - <prop key = "transfer"> propagation_required、+java.lang.arithmeticexception </prop> - > </props> </property> </bean>
上記の構成を通じて、TransactionProxyFactoryBeanがFactoryBeanDemo2のプロキシクラスを作成することを大まかに理解できます。このプロキシクラスは、内部の良いものに関連するロジックをカプセル化します。これは、以前のプログラミングの単純な一般的な抽象化と見なすことができます。
b。テスト
テストコードは基本的に以前と同じです。唯一の違いは、Factorybeandemo2を直接使用するのではなく、上記のBeanFactoryによって生成されたBeanを使用する必要があることです。
通常のデモケース:
@runwith(springjunit4classrunner.class)@contextconfiguration({"classpath*:spring/service.xml"、 "classpath*:test-datasource2.xml"})public classbeandemo1test {@Resource(name = "factorybeandemoproxy")fictorybeandemoproxy ") @autowired private moneydao moneydao; @test public void testtransfor(){system.out.println( "----------------------"); System.out.println( "id:1 money =" + moneydao.querymoney(1).getmoney()); system.out.println( "id:2 money =" + moneydao.querymoney(2).getmoney()); Factorybeandemo2.transfor(1、2、10、0); system.out.println( "-------------------------"); System.out.println( "id:1 money =" + moneydao.querymoney(1).getmoney()); system.out.println( "id:2 money =" + moneydao.querymoney(2).getmoney()); }}出力
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -
ID:1 Money = 10000
ID:2マネー= 50000
転送が完了しました!今:1526132058886
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -
ID:1 Money = 10010
ID:2マネー= 49990
ステータスが1であり、内部例外が当てはまらない場合、お金に問題がないことを願っています。
@testpublic void testtransforexception(){system.out.println( "-----------------------"); System.out.println( "id:1 money =" + moneydao.querymoney(1).getmoney()); system.out.println( "id:2 money =" + moneydao.querymoney(2).getmoney()); try {factorybeandemo2.transfor(1、2、10、1); } catch(Exception e){System.out.println(e.getMessage());; } system.out.println( "-----------------------"); System.out.println( "id:1 money =" + moneydao.querymoney(1).getmoney()); System.out.println( "id:2 money =" + moneydao.querymoney(2).getMoney();};}出力はです
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -
ID:1 Money = 10010
ID:2マネー= 49990
異常を転送!!!
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -
ID:1 Money = 10010
ID:2マネー= 49990
ステータスが2の場合、分析結果は上記と同じである必要があり、出力は次のとおりです。
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -
ID:1 Money = 10010
ID:2マネー= 49950
内部お金:1526133325376
成功を修正してください!今:1526133325387
転送が完了しました!今:1526133328381
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -
ID:1お金= 10220
ID:2マネー= 49940
ステータスが3の場合、出力
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -
ID:1お金= 10220
ID:2マネー= 49940
内部お金:1526133373466
転送が完了しました!今:1526133376476
成功を修正してください!今:1526133376480
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -
ID:1お金= 10230
ID:2マネー= 50130
c。まとめ
TransactionProxyFactoryBeanのアイデアは、プロキシモードを使用して物事管理を実装し、プロキシクラスを生成し、ターゲットメソッドをインターセプトし、SQL操作のセットを物にカプセル化することです。ハードコードと比較して、それは非侵襲的であり、柔軟な構成方法をサポートします。
短所も明らかであり、それぞれを構成する必要があり、これは非常に複雑です
春には、IOCとAOPの2つの主要な特性があります。このようなことについては、AOPを使用してそれを行うことはできますか?
オンにする必要がある方法、傍受、実行前に物事を開始し、実行後に物事を送信し、例外が発生したときにロールバックします。
この観点から、それは非常に有望であると感じており、次の2つの姿勢がこのように演奏されるため、アスペクトの依存が必要です。
<Dependency> GroupId> org.aspectj </groupId> <ArtifActid> astifactid> aspectjweaver </artifactid> <バージョン> 1.8.7 </version> </dependency>
a。実装
Javaクラスは2番目のタイプとまったく同じで、XMLのみが変更されます
<! - 最初に名前空間を追加 - > xmlns:tx = "http://www.springframework.org/schema/tx" xmlns:aop = "http://www.springframework.org/schema/aop" http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/tx/spron ID = "TXADVICE" Transaction-Manager = "TransactionManager"> <TX:属性> < </tx:属性> </tx:アドバイス> <! - 構成セクション - > <aop:config> <! - configuration point-cut-> <aop:aop:aop:pointcut expression = "execution(* com.git.hui.demo.mybatis.repository.transaction.xmldemo3。*(..) advise-ref = "txadvice" pointcut-ref = "pointcut1"/> </aop:config>
上記の構成を観察し、2番目の方法について考えてください。アイデアはほぼ同じですが、この方法は明らかに一般的です。セクションと切断のポイントを通じて、多数の構成を減らすことができます。
b。テスト
@runwith(springjunit4classrunner.class)@contextconfiguration({"classpath*:spring/service.xml"、 "classpath*:test-datasource3.xml"})public class xmlbeantest {@autowired private xmldemo3 xmldemo; @autowired private moneydao moneydao; @test public void testtransfor(){system.out.println( "--------------------"); System.out.println( "id:1 money =" + moneydao.querymoney(1).getmoney()); system.out.println( "id:2 money =" + moneydao.querymoney(2).getmoney()); xmldemo.transfor(1、2、10、0); System.out.println( "----------------------"); System.out.println( "id:1 money =" + moneydao.querymoney(1).getmoney()); system.out.println( "id:2 money =" + moneydao.querymoney(2).getmoney()); }}このテストは一般的な執筆方法と違いはなく、2番目の工場の注入方法よりも簡単です
通常の出力
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -
ID:1 Money = 10000
ID:2マネー= 50000
転送が完了しました!今:1526135301273
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -
ID:1 Money = 10010
ID:2マネー= 49990
ステータス= 1例外が発生した場合、出力はです
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -
ID:1 Money = 10010
ID:2マネー= 49990
異常を転送!!!
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -
ID:1 Money = 10010
ID:2マネー= 49990
ステータス= 2転送プロセス中にお金を節約するシナリオは、出力が以前の期待と一致しています。
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -
ID:1 Money = 10010
ID:2マネー= 49990
内部お金:1526135438403
成功を修正してください!今:1526135438421
転送が完了しました!今:1526135441410
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -
ID:1お金= 10220
ID:2マネー= 49980
ステータス= 3の出力は、以前の期待と一致しています
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -
ID:1お金= 10220
ID:2マネー= 49980
内部お金:1526135464341
転送が完了しました!今:1526135467349
成功を修正してください!今:1526135467352
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -
ID:1お金= 10230
ID:2マネー= 50170
これは、XMLを排除し、アノテーションを使用してそれを行うことです。これは、以前のXMLの構成を@TransactionAl Annotationに置き換えることです。
a。実装
@RepositoryPublic Class Annodemo4 {@autowired private Moneydao Moneydao; /** * * * @param inuserid * @param outuserid * @param paymoney * @paramステータス0は通常の転送を示します。 readonly:read-only* rollbackfor:どの例外が発生したかノルオールバックフォース:ロールバックではない例外*ロールバックのロールバックは例外クラス名に従ってロールバック*/ @transactional(伝播=伝播=分離=分離=分離=分離=分離= default、readonly = fals = fals = fals)変換(最終int) MoneyEntity Entity = MoneyDao.QueryMoney(Outuserid); if(entity.getMoney()> paymoney){//お金を転送できます// first moneydao.incrementmoney(outuserid、-paymoney); Testcase(Inuserid、outuserid、status); // moneydao.incrementMoney(inserid、paymoney)にお金を追加します。 system.out.println( "転送が完了しました! }} private void testcase(final int inuserid、final int outuserid、final int status){if(status == 1){throw new IllegalargumentException( "Transfer Exception !!!"); } else if(status == 2){addmoney(inserid); {thread.sleep(3000); } catch(arturnedexception e){e.printstacktrace(); }} else if(status == 3){addmoney(outuserid); {thread.sleep(3000); } catch(arturnedexception e){e.printstacktrace(); }}} private void addmoney(final int userid){system.out.println( "Inside add money:" + system.currenttimemillis());新しいスレッド(new runnable(){public void run(){moneydao.incrementmoney(userid、200); system.out.println( "sub modify success! }}したがって、物事の注釈を有効にするためにXMLで構成する必要があります
<! - プログラミングシングス - > <bean id = "transactionManager"> <プロパティ名= "dataSource" ref = "dataSource"/> </bean> <tx:annotation-driven clonsaction-manager = "transactionmanager"/>
これにより明確になります。実際のプロジェクトでは、XMLおよび注釈方法も最も一般的に使用されるシナリオです。
b。テストケース
これは3番目のテストケースとまったく同じであり、出力の結果は同じであり、直接省略されています
上記は、春に物事を使用する4つの方法について語っています。その中で、ハードコーディングされた方法は最良の理解である可能性があります。これは、SQLのものを対応するJavaコードに使用する方法を直接翻訳するのと同等です。また、工場の方法は、特別な状況を扱い、それぞれのものをプロキシクラスで扱うことと同等です。後者の2つの原則は、Thing通知(AOP)を使用してほぼ実装され、接線ポイントと関連情報を定義します。
プログラミング:
transactionTemplate#executeメソッドを実行しますプロキシBeanFactory:
XML設定:
注釈方法:
tx:annotation-driven transaction-manager="transactionManager"/>書類
春のトランザクション管理の4つの方法
ソースコード
上記は、この記事のコンテンツ全体です。この記事の内容には、すべての人の研究や仕事に特定の参照値があることを願っています。ご質問がある場合は、メッセージを残してコミュニケーションをとることができます。 wulin.comへのご支援ありがとうございます。