記事ディレクトリ
1. データベース接続プールの必要性
(1). 従来のデータベース接続モードの手順
- メイン プログラムで接続を作成する
- SQL 操作を実行する
- データベース接続を閉じる
(2). 従来のデータベース接続モードの問題点
-
時間の無駄: 接続するたびにログインを確認し、接続をメモリにロードします。
-
大規模なデータベースにアクセスできない: データベースの訪問者が多すぎると、多くのシステム リソースが消費され、サーバーがクラッシュします。
-
メモリ リークの問題があります: 各接続を切断する必要があります. 切断しないとプログラムが終了し, 作成された接続オブジェクトがメモリに残って閉じることができず, Java メモリ リークが発生します.
メモリ リーク: 作成されたオブジェクトを再利用できないことを意味します
2. データベース接続プール技術
(1). データ接続プールの考え方:
一定数の接続オブジェクトを格納するために事前にメモリ内にバッファー プールを作成し、必要に応じてそれを呼び出し、最後にバッファー プールに戻します。
(2). データベース接続プールのタスク:
データベース接続を管理および解放し、ユーザーがオブジェクトを作成せずにプール内の接続オブジェクトを使用できるようにします。
(3). データベース接続プールのサイズ:
初期化時の数: データベース接続の最小数によって設定されます。
最大数: データベース接続の最大数によって決まります。
接続数が最大接続数を超えると、超過した接続は接続オブジェクトが解放されるのを待機しなくなります。
(4). 動作原理:
(5). データベース接続プールの利点:
-
リソースの再利用:
接続プール内のオブジェクトは必要に応じて取り出され、接続プールによって再利用される必要はありません -
応答速度の高速化:
接続オブジェクトが事前にプールに予約され、初期化が完了し、呼び出しが直接行われます。 -
データベース共有メカニズム
複数のユーザーが同じデータベースにアクセスする場合、アプリケーション層での構成により、リソースの独占を回避できます。 -
メモリ リークの回避:
接続オブジェクトの統合管理、接続オブジェクトのタイム スライスの設定、およびタイムアウト発生時の強制リサイクル。
3. さまざまなオープン ソース データベース接続プール
JDBC データベース接続プールは、javax.sql.DataSource で表されます。情報元通常、サーバーによって提供されるインターフェイスです。
一般的なオープン ソース データベース接続プール:
DBCP: C3P0 より高速ですがバグがあります
c3p0: 遅いが、比較的安定している
Proxool: オープン ソースの接続プール、接続プールを監視する機能がありますが、安定性は C3P0 よりも劣ります
BoneCP: 高速、オープンソース
Druid: Ali が提供する接続プールは高速 (BoneCP ほどではありません) で、安定性が高く、接続プールを監視する機能があります。
4. 最も主流のデータベース接続プール Druid を学ぶ
Druidは Ali が提供するデータベース接続プールで、DBCP、C3P0、Proxool の利点を組み合わせたデータベース接続プールと言われ、現在中国で最も使用されているデータベース接続プール技術です。
(1). 接続を作成する (構成ファイル方式)
- 頼る
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.31</version>
</dependency>
<dependency>
<!--mysql版本对应-->
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.21</version>
</dependency>
- 構成ファイル
構成ファイルを作成しdruid.properties
、構成情報を入力します
driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/test
username=root
password=12345678
initialSize=10
maxActive=20
- 詳細な設定パラメータ
構成 | デフォルト | 例証する |
---|---|---|
名前 | このプロパティを構成することの重要性は、複数のデータ ソースがある場合、監視中にそれらを名前で区別できることです。構成されていない場合、名前は次の形式で生成されます: "DataSource-" + System.identityHashCode(this) | |
URL | データベースに接続するための URL は、データベースごとに異なります。例: mysql: jdbc:mysql://10.20.153.104:3306/druid2 oracle: jdbc:oracle:thin:@10.20.149.85:1521:ocnauto | |
ユーザー名 | データベースに接続するためのユーザー名 | |
パスワード | データベースに接続するためのパスワード。パスワードを構成ファイルに直接書きたくない場合は、ConfigFilter を使用できます。詳細はこちら: https://github.com/alibaba/druid/wiki/Using ConfigFilter | |
driverClassName | URL の自動識別に応じて、この項目を構成するかどうかを指定できます.druid を構成しない場合は、URL に基づいて dbType を自動的に識別し、対応する driverClassName を選択します (推奨構成) | |
初期サイズ | 0 | 初期化中に確立された物理接続の数。初期化は、init メソッドが明示的に呼び出されたとき、または getConnection が最初に呼び出されたときに発生します。 |
最大アクティブ | 8 | 接続プールの最大数 |
最大アイドル | 8 | もう使用されておらず、構成は効果がありません |
最小アイドル | 接続プールの最小数 | |
最大待機 | 接続を取得する際の最大待機時間 (ミリ秒)。maxWait が構成された後、フェア ロックがデフォルトで有効になり、同時実行効率が低下します。必要に応じて、useUnfairLock プロパティを true に構成することにより、不公平なロックを使用できます。 | |
プール準備済みステートメント | 間違い | PreparedStatement、つまり PSCache をキャッシュするかどうか。PSCache は、Oracle などのカーソルをサポートするデータベースのパフォーマンスを大幅に向上させます。mysql の下で閉じることをお勧めします。 |
maxOpenPreparedStatements | -1 | PSCache を有効にするには、0 より大きい値に設定する必要があります。0 より大きい場合、poolPreparedStatements が自動的にトリガーされ、true に変更されます。Druid では、Oracle の下で PSCache がメモリを占有しすぎるという問題はありません.この値は、たとえば 100 など、より大きな値に設定できます. |
検証クエリ | 接続が有効かどうかを確認するために使用される SQL には、クエリ ステートメントが必要です。validationQuery が null の場合、testOnBorrow、testOnReturn、および testWhileIdle は機能しません。 | |
testOnBorrow | 真実 | 接続申請時にvalidationQueryを実行し、接続が有効かどうかを確認する設定を行うとパフォーマンスが低下します。 |
testOnReturn | 間違い | 接続を返すときに、接続が有効かどうかを確認するために validationQuery を実行します。この構成を行うと、パフォーマンスが低下します。 |
testWhileIdle | 間違い | true に設定することをお勧めします。これは、パフォーマンスに影響を与えず、セキュリティを確保します。接続申請時に確認 アイドル時間が timeBetweenEvictionRunsMillis よりも長い場合、validationQuery を実行して接続が有効かどうかを確認します。 |
timeBetweenEvictionRunsMillis | 2つの意味があります: 1) Destroy スレッドが接続間隔を検出します 2) testWhileIdle の判断基準、詳細については testWhileIdle 属性の説明を参照してください | |
numTestsPerEvictionRun | 使用されなくなりました。DruidDataSource は 1 つの EvictionRun のみをサポートします | |
minEvictableIdleTimeMills | ||
connectionInitSqls | 物理接続の初期化時に実行されるSQL | |
例外ソーター | データベースが回復不能な例外をスローしたときに dbType に従って自動的に識別し、接続を破棄します | |
フィルター | 属性タイプは文字列であり、拡張プラグインはエイリアスを介して構成されます. 一般的に使用されるプラグインには次のものがあります: 統計を監視するためのフィルター: 統計ログ用のフィルター: SQL インジェクションを防止するための log4j フィルター: wall | |
プロキシフィルター | type は List.filters と proxyFilters が同時に設定されている場合は、置換関係ではなく組み合わせ関係です。 |
- カプセル化接続ツール クラス
public class DruidUtils {
private static DataSource source;
/**
* 静态代码块中加载配置文件,随着类的加载而执行
*/
static {
try {
//创建properties对象,用来封装从文件中获取的流数据
Properties pros = new Properties();
//采用类加载方式获取文件的内容,并封装成流
InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("Druid.properties");
//将流传入到pros对象中
pros.load(is);
//利用工厂类创建数据库连接池
source = DruidDataSourceFactory.createDataSource(pros);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获取连接 直接使用数据库连接池对象条用getConnection()方法
* @return
* @throws Exception
*/
public static Connection getConnection() throws Exception {
return source.getConnection();
}
/**
* 关闭连接
* @param connection
* @param stm
* @param rs
*/
public static void close(Connection connection, Statement stm, ResultSet rs) {
try {
if (connection != null) connection.close();
} catch (Exception throwable) {
throwable.printStackTrace();
}
try {
if (stm != null) stm.close();
} catch (Exception throwable) {
throwable.printStackTrace();
}
try {
if (rs != null) rs.close();
} catch (Exception throwable) {
throwable.printStackTrace();
}
}
/**
* 重载close方法
*/
public static void close(Connection conn, Statement stm) {
close(conn, stm, null);
}
}
- テストクラス
/**
* 测试连接是否成功
* @throws Exception
*/
@Test
public void getDruidConnection() throws Exception {
Connection conn = DruidUtils.getConnection();
System.out.println(conn);
}
返り値は以下の通り、接続成功
単純な CRUD
- エンティティ クラス
public class ApplicationDO {
/**
* 主键ID
*/
private Long id;
/**
* 申请类型 0 未知,1 license,2 soultion,3 both
*/
private Integer applyType;
/**
* teamwork项目名称
*/
private String teamworkProjectName;
/**
* 项目名称
*/
private String projectName;
/**
* 客户名称
*/
private String customName;
/**
* 获取主键ID
*
* @return id - 主键ID
*/
public Long getId() {
return id;
}
/**
* 设置主键ID
*
* @param id 主键ID
*/
public void setId(Long id) {
this.id = id;
}
/**
* 获取申请类型 0 未知,1 license,2 soultion,3 both
*
* @return apply_type - 申请类型 0 未知,1 license,2 soultion,3 both
*/
public Integer getApplyType() {
return applyType;
}
/**
* 设置申请类型 0 未知,1 license,2 soultion,3 both
*
* @param applyType 申请类型 0 未知,1 license,2 soultion,3 both
*/
public void setApplyType(Integer applyType) {
this.applyType = applyType;
}
/**
* 获取teamwork项目名称
*
* @return teamwork_project_name - teamwork项目名称
*/
public String getTeamworkProjectName() {
return teamworkProjectName;
}
/**
* 设置teamwork项目名称
*
* @param teamworkProjectName teamwork项目名称
*/
public void setTeamworkProjectName(String teamworkProjectName) {
this.teamworkProjectName = teamworkProjectName;
}
/**
* 获取项目名称
*
* @return project_name - 项目名称
*/
public String getProjectName() {
return projectName;
}
/**
* 设置项目名称
*
* @param projectName 项目名称
*/
public void setProjectName(String projectName) {
this.projectName = projectName;
}
/**
* 获取客户名称
*
* @return custom_name - 客户名称
*/
public String getCustomName() {
return customName;
}
/**
* 设置客户名称
*
* @param customName 客户名称
*/
public void setCustomName(String customName) {
this.customName = customName;
}
@Override
public String toString() {
return "ApplicationDO{" +
"id=" + id +
", applyType=" + applyType +
", teamworkProjectName='" + teamworkProjectName + '\'' +
", projectName='" + projectName + '\'' +
", customName='" + customName + '\'' +
'}';
}
}
- クラッド
/**
* 查询
* @throws Exception
*/
@Test
public void Select() throws Exception {
//因为获取连接创建的是静态方法 直接使用类名.方法名调取 获得连接即可
Connection conn= DruidUtils.getConnection();
String sql="SELECT * FROM licenx_application";
//获取执行者对象
PreparedStatement pstmt = conn.prepareStatement(sql);
ResultSet rs = pstmt.executeQuery();
ApplicationDO applicationDO;
List<ApplicationDO> applicationDOs=new ArrayList<>();
//遍历ResultSet结果集 存入List
while (rs.next()){
applicationDO=new ApplicationDO();
applicationDO.setId(rs.getLong("id"));
applicationDO.setCustomName(rs.getString("custom_name"));
applicationDO.setApplyType(rs.getInt("apply_type"));
applicationDO.setTeamworkProjectName(rs.getString("teamwork_project_name"));
applicationDO.setProjectName(rs.getString("project_name"));
applicationDOs.add(applicationDO);
}
//输出list查看
for (ApplicationDO applicationDO1 : applicationDOs) {
System.out.println(applicationDO1);
}
DruidUtils.close(conn,pstmt,rs);
}
/**
* 插入
* @throws Exception
*/
@Test
public void insert() throws Exception {
//获取数据库连接
Connection conn=DruidUtils.getConnection();
String sql="insert into licenx_application (custom_name, apply_type, teamwork_project_name, project_name) values (?, ?, ?, ?)";
//获取执行者对象
PreparedStatement pstmt = conn.prepareStatement(sql);
//设置sql语句中的?值
pstmt.setObject(1,"测试");
pstmt.setObject(2,1);
pstmt.setObject(3,"测试");
pstmt.setObject(4,"测试");
int i = pstmt.executeUpdate();
if (i>0){
System.out.println("添加成功");
}
//释放资源
DruidUtils.close(conn,pstmt);
}
/**
* 删除
* @throws Exception
*/
@Test
public void delete() throws Exception {
//获取连接
Connection conn=DruidUtils.getConnection();
String sql="DELETE from licenx_application where id=?";
//获取执行者对象
PreparedStatement pstmt = conn.prepareStatement(sql);
//设置sql中的?值
pstmt.setObject(1,251757598242504708l);
int i = pstmt.executeUpdate();
if (i>0) System.out.println("删除成功");
//关闭资源
DruidUtils.close(conn,pstmt);
}
/**
* 更新
* @throws Exception
*/
@Test
public void update() throws Exception {
//获取连接
Connection conn=DruidUtils.getConnection();
String sql="UPDATE licenx_application SET teamwork_project_name=? WHERE id=?";
//获取执行者对象
PreparedStatement pstmt = conn.prepareStatement(sql);
//设置sql中?的值
pstmt.setObject(1,"99999");
pstmt.setObject(2,251757598242504706l);
int i = pstmt.executeUpdate();
if (i>0) System.out.println("修改成功");
//释放资源
DruidUtils.close(conn,pstmt);
}