必要とする:
Spring Boot で複数のデータベースが発生する可能性がある場合、これをどのように構成すればよいでしょうか?私の中心的な要件は、構成がどのように構成されていても、最初のデータベースのデフォルト構成とコードの記述方法に影響を与えてはならず、トランザクションの通常の実行が保証されることです。
コード:
1. 最初のデータベースを通常どおりに設定しましょう。
プロジェクトを作成する
関連する依存関係を選択します。ここでは、jdbcTemplate を使用してテストします。最初のデータベースは mysql を使用し、2 番目のデータベースは oracle を使用します。
2. データベース構成ファイルを変更する
私は yml 設定ファイルを使用することを好むため、application.properties の名前を application.yml に変更しました。ここでは、両方のデータベースが一度に完全に構成されます。
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/test
username: root
password: mysql
second:
datasource:
driver-class-name: oracle.jdbc.driver.OracleDriver
url: jdbc:oracle:thin:localhost:1521:orcl
username: system
password: manager
ここのスプリング: は最初のデータベース構成です。デフォルトでは、誰もがこのように構成します。これ以上言うことはありません。そして 2 番目: は自分で定義した 2 番目のデータベース構成です。インターネットで情報を検索するとき、複数のデータ ソースの多くの構成では url: を jdbc-url: に変更する必要がありますが、このように変更したくありません。最初のデータベースが 2 番目のデータベースの影響を受けないようにする必要があります。 。
3. jdbcTemplate をカプセル化します。
基本的に、誰もが jdbcTemplate を使用するときはそれをカプセル化しますが、直接使用する人はほとんどいません。また、主にページング クエリ関数を追加するために、ここでもカプセル化します。
package com.example.databasetest.tools;
import java.util.Map;
import java.util.HashMap;
import java.util.List;
import javax.sql.DataSource;
import org.springframework.jdbc.core.JdbcTemplate;
/**
* 功能:JdbcTemplate封装类<br>
* 作者:我是小木鱼(Lag)<br>
*/
public class JdbcTemplatePackage extends JdbcTemplate
{
/** 数据库类型(ORACLE,MYSQL,DB2)*/
private String databaseType = "MYSQL";
public JdbcTemplatePackage(){}
/**
* 功能:初始化需要注入数据源<br>
* 备注:非常重要<br>
*/
public JdbcTemplatePackage(DataSource dataSource){super(dataSource);}
/**
* 功能:执行SQL查询语句并将结果以Map对象返回。
* @param sql 查询语句
* @return 仅返回符合条件的第一条数据,无则返回空值。
*/
@Override
public Map<String,Object> queryForMap(String sql)
{
return this.queryForMap(sql,null);
}
/**
* 功能:执行SQL查询语句并将结果以Map对象返回。
* @param sql 查询语句
* @param args 查询参数
* @return 仅返回符合条件的第一条数据,无则返回空值。
*/
@Override
public Map<String,Object> queryForMap(String sql, Object[] args)
{
List<Map<String,Object>> list = this.queryForList(sql,args);
if(list.size() > 0)
{
return list.get(0);
}
else
{
return null;
}
}
/**
* 功能:执行SQL查询语句并将数据总数以int对象返回。
* @param sql 查询语句
* @return -1-语句出错,0-未查询到符合条件的数据,>0-已查询到符合条件的数据。
*/
public int queryForInt(String sql)
{
return this.queryForInt(sql,null);
}
/**
* 功能:执行SQL查询语句并将数据总数以int对象返回。
* @param sql 查询语句
* @param args 查询参数
* @return -1-语句出错,0-未查询到符合条件的数据,>0-已查询到符合条件的数据。
*/
public int queryForInt(String sql, Object[] args)
{
int rowCount = -1;
Map<String,Object> map = this.queryForMap(sql,args);
if(map != null)
{
//遍历取值
for (Map.Entry<String, Object> stringObjectEntry : map.entrySet())
{
rowCount = Integer.parseInt(stringObjectEntry.getValue().toString());
}
}
return rowCount;
}
/**
* 功能:分页执行SQL查询语句
* @param sql 查询语句
* @param pageNo 查询页码
* @param pageSize 每页行数
* @return Map对象{"rowCount" :总行数 ,"pageCount" :总页数 ,"queryData" :查询数据列表}
*/
public Map<String,Object> queryForPage(String sql, int pageNo, int pageSize)
{
return this.queryForPage(sql,null,pageNo,pageSize);
}
/**
* 功能:分页执行SQL查询语句
* @param sql 查询语句
* @param args 查询参数
* @param pageNo 查询页码
* @param pageSize 每页行数
* @return Map对象{"rowCount" :总行数 ,"pageCount" :总页数 ,"queryData" :查询数据列表}
*/
public Map<String,Object> queryForPage(String sql, Object[] args, int pageNo, int pageSize)
{
int rowCount; //总行数
int pageCount; //总页数
int rowBegin; //起始行
int rowEnd; //截止行
Map<String,Object> rtnMap = new HashMap<>();
//数据库类型不允许为空
if("".equals(this.databaseType)){throw new RuntimeException("数据库类型为空,请用setDatabaseType(...)函数添加数据库类型!");}
//得到总行数
rowCount = this.queryForInt("select count(*) from ("+sql+") tempTableForCount",args);
if(rowCount < 1) //没有数据直接返回
{
rtnMap.put("rowCount",rowCount);
rtnMap.put("pageCount",0);
rtnMap.put("queryData",null);
return rtnMap;
}
//得到总页数
if(rowCount % pageSize == 0)
{
pageCount = rowCount / pageSize;
}
else
{
pageCount = rowCount / pageSize + 1;
}
//检验当前页码
if(pageNo < 1)
{
pageNo = 1;
}
else if(pageNo > pageCount)
{
pageNo = pageCount;
}
//计算起始行与截止行
rowBegin = (pageNo - 1) * pageSize + 1;
rowEnd = pageNo * pageSize;
if(rowEnd > rowCount){rowEnd = rowCount;}
//重新生成SQL语句
if("ORACLE".equals(databaseType))
{
sql = "select * from (select rownum myrownum,a.* from ("+sql+") a where rownum <= "+ rowEnd +") tempTableForPage where myrownum >= "+ rowBegin; //虚拟顺序号rownum变成真实顺序号rownum
}
else if("MYSQL".equals(databaseType) || "MARIADB".equals(databaseType))
{
sql = "select * from ("+sql+") tempTableForPage limit "+ (rowBegin - 1) +","+ pageSize +"";
}
else if("DB2".equals(databaseType))
{
sql = "select * from (select rownumber() over() as myrownum,a.* from ("+sql+") a) tempTableForPage where myrownum between "+ rowBegin +" and "+ rowEnd;
}
else
{
throw new RuntimeException("这取得的数据库类型["+this.databaseType+"]我也找不到啊!");
}
rtnMap.put("rowCount",rowCount);
rtnMap.put("pageCount",pageCount);
rtnMap.put("queryData",this.queryForList(sql,args));
return rtnMap;
}
/**
* 功能:设置数据库类型<br>
* @param databaseType(ORACLE,MYSQL,DB2)
*/
public void setDatabaseType(String databaseType)
{
this.databaseType = databaseType;
}
}
JdbcTemplatePackage クラスを解析します。
1) パラメーターのないコンストラクターと、DataSource パラメーターを含むコンストラクター、および super(dataSource) が必要です。これを忘れずに記述してください。これについては、あまり言う必要はありません。誰もが知っています。
2) queryForPage(...) を呼び出してページング関数をクエリする場合、各データベースの構文が異なるため、どのデータベースが呼び出されるかを知る必要があります。そこで変数を追加しました
/** データベースの種類 (ORACLE、MYSQL、DB2) */ プライベート文字列データベースタイプ = "MYSQL";
デフォルトでは、参照は mysql データベースです。このパラメータ値は、最初と 2 番目のデータベース Bean をアセンブルするときに指定する必要があります。
3) JdbcTemplatePackage クラスは通常のクラスであり、 は @Component アノテーションが付いた Bean にアセンブルされません。
4. 最初のデータベースのBeanを生成します。
package com.example.databasetest.tools;
import org.springframework.stereotype.Component;
import javax.sql.DataSource;
@Component
public class MyJdbcTemplate extends JdbcTemplatePackage
{
public MyJdbcTemplate(DataSource dataSource)
{
super(dataSource);
super.setDatabaseType("MYSQL");
}
}
MyJdbcTemplate クラスの解析:
1) このクラスは JdbcTemplatePackage を継承しており、コンストラクターはデフォルトのデータ ソースを追加し、それを mysql データベースに設定します。
2) このクラスは @Component アノテーションを使用して Bean を生成します。ユーザーは通常、このタイプの Bean を呼び出してデータベースを操作できます。
3) 複数のデータベースがある場合は、後で追加するトランザクション マネージャー TransactionManager をこのクラスに追加します。
5. 最初のデータベースの Bean をテストします。
(1) まず、Spring Boot が MyJdbcTemplate Bean を自動的にアセンブルしたかどうかを確認してみましょう。
スタートアップ クラス DatabaseTestApplication を変更します。
package com.example.databasetest;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
@SpringBootApplication
public class DatabaseTestApplication
{
public static void main(String[] args)
{
// SpringApplication.run(DatabaseTestApplication.class, args);
//看看spring boot给我们都自动装配了哪些bean
ConfigurableApplicationContext run = SpringApplication.run(DatabaseTestApplication.class, args);
String[] names = run.getBeanDefinitionNames();
for(String name: names)
{
System.out.println("===Bean:"+name);
}
System.out.println("====================我是快乐的分割线===================");
}
}
このクラスを実行して結果を確認します
ほら、自動的に組み立てられてるよ(笑)!
(2) 最初のデータベースのテーブル t_001 のデータ
(3) テスト Web ページのカテゴリを追加します。
package com.example.databasetest.test;
import java.util.Map;
import java.util.List;
import com.example.databasetest.tools.MyJdbcTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class Test
{
@Autowired
MyJdbcTemplate myJT;
@RequestMapping("/db1")
public String say()
{
//分页查询t_001表数据,第2页,每页显示2条数据,该函数返回Map对象,其中的key包括总行数,总页数与具体的查询数据。
Map mapQry = this.myJT.queryForPage("select * from t_001", 2, 2);
List list = (List)mapQry.get("queryData"); //得到Map中的具体查询数据
return "我是第一数据库的测试方法:" + list.toString();
}
}
Test クラスを解析します。
1) @RestController このアノテーションは説明がなく、退屈です。
2) @Autowired MyJdbcTemplate myJT; MyJdbcTemplate Bean を使用します。 @Autowired は、そのタイプに基づいて Bean を検索します。 @Resourceとは異なります。
(4) 返された結果を確認します。
これは非常に成功し、5 つのデータのうち 2 つが返され、ページは実際にページ分割されました。
(5) トランザクションの追加
package com.example.databasetest.test;
import java.util.Map;
import java.util.List;
import com.example.databasetest.tools.MyJdbcTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class Test
{
@Autowired MyJdbcTemplate myJT;
@RequestMapping("/db1")
@Transactional
public String say()
{
//分页查询t_001表数据,第2页,每页显示2条数据,该函数返回Map对象,其中的key包括总行数,总页数与具体的查询数据。
Map mapQry = this.myJT.queryForPage("select * from t_001", 2, 2);
List list = (List)mapQry.get("queryData"); //得到Map中的具体查询数据
System.out.println("我是第一数据库的测试方法:"+list.toString());
//事务
this.myJT.update("insert into t_001 (c01,c02,c03) values ('999','国王',100)");
//故意抛出异常
int i = 1/0;
return "我是第一数据库的测试方法:" + list.toString();
}
}
ここでは、RuntimeException をロールバックするトランザクション アノテーション @Transactional を追加します。結果を確認するために意図的に例外をスローしました。
データは変更されていません。これで最初のデータベース Bean テストが完了しました。
6. 2番目のデータベースのBeanを作成します ここがポイントです。
package com.example.databasetest.tools;
import javax.sql.DataSource;
import javax.annotation.PostConstruct;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class MyJdbcTemplate2 extends JdbcTemplatePackage
{
@Value("${second.datasource.driver-class-name}")
private String driverClassName ;
@Value("${second.datasource.url}")
private String url ;
@Value("${second.datasource.username}")
private String userName ;
@Value("${second.datasource.password}")
private String password ;
@PostConstruct
public void init()
{
System.out.println("=====@PostConstruct===========================");
HikariConfig config = new HikariConfig();
config.setDriverClassName(driverClassName);
config.setJdbcUrl(url);
config.setUsername(userName);
config.setPassword(password);
DataSource ds = new HikariDataSource(config);
super.setDataSource(ds);
super.setDatabaseType("ORACLE");
}
}
MyJdbcTemplate2 クラスを解析します。
1) このクラスは JdbcTemplatePackage から継続しています。
2) @Value アノテーションを使用して、構成ファイル application.yml 内の 2 番目のデータベースのパラメーターを取得します。
3) これらのパラメータを使用して新しいデータ ソース DataSource を生成し、それを親クラスに割り当てる必要があります。しかし、非常に重要な質問があります! @ValueはデフォルトではBean生成後に有効となり、コンストラクタでは取得できない(全く使えないわけではなくパラメータとして使えば良いが、面倒で汎用的ではない)ため、 @PostConstruct アノテーションを使用する。これは Spring コンテナーの開始時に実行されます。したがって、2 番目のデータベース Bean が生成される前に、@PostConstruct アノテーションが付けられた関数が実行され、2 番目のデータベース ソースが生成されて親クラスに割り当てられ、デフォルトのデータ ソースが置き換えられます。
4) @Component アノテーションを使用して、このクラスを Bean として登録します。
Spring Boot を起動して、Bean がロードされるのを確認してみましょう。
ご覧のとおり、@PostConstruct が最初にロードされ、次に 2 つのデータベース MyJdbcTemplate と MyJdbcTemplate2 の Bean が生成されます。
7. 2 番目のデータベースの Bean をテストする
(1) 2 番目のデータテーブルを見てみましょう
(2) テスト Web ページを変更する
package com.example.databasetest.test;
import java.util.Map;
import java.util.List;
import com.example.databasetest.tools.MyJdbcTemplate;
import com.example.databasetest.tools.MyJdbcTemplate2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class Test
{
@Autowired
MyJdbcTemplate myJT;
@Autowired
MyJdbcTemplate2 myJT2;
@RequestMapping("/db1")
@Transactional
public String say()
{
//分页查询t_001表数据,第2页,每页显示2条数据,该函数返回Map对象,其中的key包括总行数,总页数与具体的查询数据。
Map mapQry = this.myJT.queryForPage("select * from t_001", 2, 2);
List list = (List)mapQry.get("queryData"); //得到Map中的具体查询数据
System.out.println("我是第一数据库的测试方法:"+list.toString());
//事务
this.myJT.update("insert into t_001 (c01,c02,c03) values ('999','国王',100)");
//故意抛出异常
//int i = 1/0;
return "我是第一数据库的测试方法:" + list.toString();
}
@RequestMapping("/db2")
public String say2()
{
//分页查询t_test表数据,第2页,每页显示2条数据,该函数返回Map对象,其中的key包括总行数,总页数与具体的查询数据。
Map mapQry = this.myJT2.queryForPage("select * from t_test", 2, 2);
List list = (List)mapQry.get("queryData"); //得到Map中的具体查询数据
System.out.println("我是第二数据库的测试方法:"+list.toString());
return "我是第二数据库的测试方法:" + list.toString();
}
}
Test クラスを解析します。
1) 2 番目のデータベースの @Autowired MyJdbcTemplate2 myJT2; Bean を追加します。
2) 新しい関数say2()を追加
@RequestMapping("/db2") public String Say2() { //ページングで t_test テーブル データをクエリします。2 ページ目では、各ページに 2 つのデータが表示されます。この関数は Map オブジェクトを返します。そのキーには行の総数、ページの総数、および特定のクエリ データが含まれます。 マップ mapQry = this.myJT2.queryForPage("select * from t_test", 2, 2); List list = (List)mapQry.get("queryData"); // マップ内の特定のクエリ データを取得します System.out.println("私は 2 番目のデータベースのテスト メソッドです:"+list.toString()); return "私は 2 番目のデータベースのテスト メソッドです:" + list.toString(); }
3) 最初のデータベース Bean テスト関数 Say() の例外をコメント アウトし、しばらくして 2 つのデータベースを一緒にテストしました。
//トランザクション this.myJT.update("t_001 (c01,c02,c03) の値 ('999','King',100) に挿入"); // 意図的に例外をスローする // int i = 1/0;
テストを開始する
まず最初のデータベースをテストします
例外をコメントしたため、データの挿入は成功しました。
2 番目のデータベース Bean を再度テストします
成功!
8. 最後の 2 つの項目は、データベース Bean を生成するトランザクションです。
注: この場合、システムには 2 つのトランザクション マネージャーがあるため、2 番目のデータベース Bean のトランザクション マネージャーを生成したいのですが、最初のデータベース Bean の @Transactional はどれを使用するかわからないため、@ Primary を使用する必要があります。それを伝えます。
MyJdbcTemplate クラスを変更し、デフォルトのトランザクション マネージャーを追加します。
package com.example.databasetest.tools;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Component;
import org.springframework.transaction.PlatformTransactionManager;
import javax.sql.DataSource;
@Component
public class MyJdbcTemplate extends JdbcTemplatePackage
{
public MyJdbcTemplate(DataSource dataSource)
{
super(dataSource);
super.setDatabaseType("MYSQL");
}
@Bean
@Primary
public PlatformTransactionManager oneManager(){
System.out.println("=====这是第1个数据库事务===");
return new DataSourceTransactionManager(this.getDataSource());
}
}
新しいトランザクション マネージャーがこのクラスに追加され、デフォルトとして設定されます。
次に、トランザクション マネージャーをクラス MyJdbcTemplate2 に追加します。
package com.example.databasetest.tools;
import javax.sql.DataSource;
import javax.annotation.PostConstruct;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Component;
import org.springframework.transaction.PlatformTransactionManager;
@Component
public class MyJdbcTemplate2 extends JdbcTemplatePackage
{
@Value("${second.datasource.driver-class-name}")
private String driverClassName ;
@Value("${second.datasource.url}")
private String url ;
@Value("${second.datasource.username}")
private String userName ;
@Value("${second.datasource.password}")
private String password ;
@PostConstruct
public void init()
{
System.out.println("=====@PostConstruct===========================");
HikariConfig config = new HikariConfig();
config.setDriverClassName(driverClassName);
config.setJdbcUrl(url);
config.setUsername(userName);
config.setPassword(password);
DataSource ds = new HikariDataSource(config);
super.setDataSource(ds);
super.setDatabaseType("ORACLE");
}
@Bean
public PlatformTransactionManager twoManager(){
System.out.println("=====这是第2个数据库事务===");
return new DataSourceTransactionManager(this.getDataSource());
}
}
これら 2 つのトランザクション マネージャーの Bean が生成されるかどうかを確認してみましょう。
Spring Bootを実行し、
Bean が正常に生成されたことがわかります。
9. 最後の項目、マルチデータベーストランザクションのテストの開始
(1) 最初のデータベース Bean トランザクションを最初にテストします
テスト Web ページの Say() 関数を次のように変更します。
@RequestMapping("/db1") @トランザクション public String Say() { //ページングで t_001 テーブルのデータをクエリします。2 ページ目では、各ページに 2 つのデータが表示されます。この関数は Map オブジェクトを返します。そのキーには、行の合計数、ページの合計数、および特定のページ数が含まれます。クエリデータ。 マップ mapQry = this.myJT.queryForPage("select * from t_001", 2, 2); List list = (List)mapQry.get("queryData"); // マップ内の特定のクエリ データを取得します System.out.println("私は最初のデータベース テスト メソッドです:"+list.toString()); // トランザクション //this.myJT.update("t_001 (c01,c02,c03) の値 ('999','King',100) に挿入"); } return "私は最初のデータベース テスト メソッドです:" + list.toString(); int i = 1/0; // 意図的に例外をスローする this.myJT.update("t_001 (c01,c02,c03) の値 ('000','beggar',0) に挿入");
実行結果:http://localhost:8080/db1
データはロールバックされましたが、データは正常に挿入されませんでした。
最初のデータベース Bean トランザクション テストは成功しました。
(2) 2 番目のデータベース Bean トランザクションを再度テストし、トランザクションのアノテーションが変更されていることを確認します。
テスト Web ページの Say2() 関数を次のように変更します。
@RequestMapping("/db2") @Transactional(transactionManager = "twoManager") public String Say2() { //ページングで t_test テーブル データをクエリします。2 ページ目では、各ページに 2 つのデータが表示されます。この関数は、行の合計数、ページの合計数、および特定のクエリ データをキーに含む Map オブジェクトを返します。 。 マップ mapQry = this.myJT2.queryForPage("select * from t_test", 2, 2); List list = (List)mapQry.get("queryData"); // マップ内の特定のクエリ データを取得します System.out.println("私は 2 番目のデータベースのテスト メソッドです:"+list.toString()); // トランザクション this.myJT2.update("t_test (id,name,age) 値 ('Z','ZZZ',100) に挿入"); // 意図的に例外をスローする int i = 1/0; return "私は 2 番目のデータベースのテスト メソッドです:" + list.toString(); }
トランザクション アノテーション @Transactional(transactionManager = "twoManager")
特別に指定された取引管理者
テストを開始します:http://localhost:8080/db2
ご覧のとおり、データは挿入されず、ロールバックされました。
これは、2 番目のデータベース Bean のトランザクションも正常にテストされたことを示しています。
テスト Web ページの完全なコードは次のとおりです。
package com.example.databasetest.test;
import java.util.Map;
import java.util.List;
import com.example.databasetest.tools.MyJdbcTemplate;
import com.example.databasetest.tools.MyJdbcTemplate2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class Test
{
@Autowired
MyJdbcTemplate myJT;
@Autowired
MyJdbcTemplate2 myJT2;
@RequestMapping("/db1")
@Transactional
public String say()
{
//分页查询t_001表数据,第2页,每页显示2条数据,该函数返回Map对象,其中的key包括总行数,总页数与具体的查询数据。
Map mapQry = this.myJT.queryForPage("select * from t_001", 2, 2);
List list = (List)mapQry.get("queryData"); //得到Map中的具体查询数据
System.out.println("我是第一数据库的测试方法:"+list.toString());
//事务
//this.myJT.update("insert into t_001 (c01,c02,c03) values ('999','国王',100)");
this.myJT.update("insert into t_001 (c01,c02,c03) values ('000','乞丐',0)");
//故意抛出异常
int i = 1/0;
return "我是第一数据库的测试方法:" + list.toString();
}
@RequestMapping("/db2")
@Transactional(transactionManager = "twoManager")
public String say2()
{
//分页查询t_test表数据,第2页,每页显示2条数据,该函数返回Map对象,其中的key包括总行数,总页数与具体的查询数据。
Map mapQry = this.myJT2.queryForPage("select * from t_test", 2, 2);
List list = (List)mapQry.get("queryData"); //得到Map中的具体查询数据
System.out.println("我是第二数据库的测试方法:"+list.toString());
//事务
this.myJT2.update("insert into t_test (id,name,age) values ('Z','ZZZ',100)");
//故意抛出异常
int i = 1/0;
return "我是第二数据库的测试方法:" + list.toString();
}
}
さて、手動でスローされたsay()とsay2()の例外をマスクして、再度実行します。
実行: http://localhost:8080/db1 与 http://localhost: 8080/db2
データ挿入が成功しました!