技術共有

JavaWeb (3: JDBC および MVC)

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

JavaWeb (1:基礎知識と環境構築)アイコンデフォルト.png?t=N7T8https://blog.csdn.net/xpy2428507302/article/details/140365130?spm=1001.2014.3001.5501JavaWeb (2: サーブレットと Jsp、リスナーとフィルター)アイコンデフォルト.png?t=N7T8https://blog.csdn.net/xpy2428507302/article/details/140365159?spm=1001.2014.3001.5501

目次

10.JDBC

1。概要

2.JDBCプログラムを書く

3. 詳細な分析

(1) データベースURL

(2) 接続クラス

(3) ステートメントクラス

(4) ResultSetクラス

(5) リソースの解放

4.SQLインジェクションの問題

5.JDBCトランザクション

6.JDBCツールクラス

7. データベース接続プール

(1。概要

(2) 用途

8.DBユーティリティ

11. MVC 3 層アーキテクチャ

1 はじめに

2. mvc 3 層アーキテクチャ


10.JDBC

1。概要

JDBC (Java DataBase Connectivity): Java データベース接続技術

JDBC は、特定のデータベースから独立した管理システムであり、ユニバーサル SQL データベースへのアクセスと操作のためのパブリック インターフェイスです。

さまざまなデータベースにアクセスするための統一された方法を提供する一連の標準を定義します。

JDBC はデータベース アクセス用のアプリケーション API であり、Java 言語で記述されたクラスとインターフェイスのセットで構成されます。

2 つのレベルが含まれます:

① プログラマーが呼び出すアプリケーション指向の API。

② メーカーがデータベースドライバーを開発するためのデータベース指向 API。

異なるデータベースを JDBC に接続するには、異なるデータベース ドライバー パッケージをロードするだけでよく、データベースのオペレーティング言語の違いを気にする必要はありません。

2.JDBCプログラムを書く

①データベーステーブルの作成

  1. CREATE TABLE users(
  2. id INT PRIMARY KEY,
  3. `name` VARCHAR(40),
  4. `password` VARCHAR(40),
  5. email VARCHAR(60),
  6. birthday DATE
  7. );
  8. INSERT INTO users(id,`name`,`password`,email,birthday)
  9. VALUES(1,'张三','123456','[email protected]','2000-01-01');
  10. INSERT INTO users(id,`name`,`password`,email,birthday)
  11. VALUES(2,'李四','123456','[email protected]','2000-01-01');
  12. INSERT INTO users(id,`name`,`password`,email,birthday)
  13. VALUES(3,'王五','123456','[email protected]','2000-01-01');
  14. SELECT * FROM users;

② データベースの依存関係をインポートする

  1. <!--mysql的驱动-->
  2. <dependency>
  3. <groupId>mysql</groupId>
  4. <artifactId>mysql-connector-java</artifactId>
  5. <version>8.0.31</version>
  6. </dependency>

③JDBCの利用:

Ⅰ. データベースドライバ(Javaプログラムとデータベース間の橋渡し)をロードします。

Ⅱ. Javaプログラムとデータベース間の接続を取得します。

Ⅲ. Connection によって生成された SQL をデータベースに送信する Statement オブジェクトを作成します。

Ⅳ. SQL文を書く(業務に応じてSQLを書き分ける)

Ⅴ. SQL を実行します (戻り値を受け取りたい場合は、Statement の実行後にクエリ結果を保存する ResultSet オブジェクトを作成します)。

Ⅵ.接続を閉じます。

  1. public class JDBCTest {
  2. public static void main(String[] args) throws Exception {
  3. //配置信息
  4. //要连接的数据库URL(解决中文乱码问题:useUnicode=true&characterEncoding=utf8&useSSL=true)
  5. String url = "jdbc:mysql://localhost:3306/jdbc?useUnicode=true&characterEncoding=utf8&useSSL=true";
  6. //连接的数据库时使用的用户名
  7. String username = "root";
  8. //连接的数据库时使用的密码
  9. String password = "123456";
  10. Connection conn = null;
  11. Statement st = null;
  12. ResultSet rs = null;
  13. try {
  14. //1.加载驱动
  15. Class.forName("com.mysql.cj.jdbc.Driver");
  16. //2.获取与数据库的链接
  17. conn = DriverManager.getConnection(url, username, password);
  18. //3.获取用于向数据库发送sql语句的statement对象
  19. st = conn.createStatement();
  20. String sql = "select id,name,password,email,birthday from users";
  21. //4.向数据库发sql,并获取代表结果集的resultset对象
  22. //查:executeQuery 增删改:executeUpdate
  23. rs = st.executeQuery(sql);
  24. //5.取出结果集的数据
  25. while (rs.next()) {
  26. System.out.println("id=" + rs.getInt("id"));
  27. System.out.println("name=" + rs.getString("name"));
  28. System.out.println("password=" + rs.getString("password"));
  29. System.out.println("email=" + rs.getString("email"));
  30. System.out.println("birthday=" + rs.getDate("birthday"));
  31. }
  32. } catch (ClassNotFoundException e) {
  33. e.printStackTrace();
  34. } catch (SQLException e) {
  35. e.printStackTrace();
  36. } finally {
  37. try {
  38. //6.关闭链接,释放资源(先开后关)
  39. rs.close();
  40. st.close();
  41. conn.close();
  42. } catch (SQLException e) {
  43. e.printStackTrace();
  44. }
  45. }
  46. }
  47. }

3. 詳細な分析

(1) データベースURL

jdbc:mysql://localhost:3306/xxx

jdbcプロトコル
マイスクサブプロトコル
ローカルホスト:3306ホスト:ポート
xxxデータベース

一般的に使用されるデータベース URL アドレスの書き方:

  • Oracle の書き込み: jdbc:oracle:thin:@ローカルホスト:1521:xxx
  • SqlServer の書き方:jdbc:microsoft:sqlserver://localhost:1433; DatabaseName=xxx
  • MySqlの記述方法:jdbc:mysql://localhost:3306/xxx

(2) 接続クラス

Connection はデータベース リンクを表すために使用され、Collection はデータベース プログラミングで最も重要なオブジェクトです。

クライアントとデータベース間のすべての対話は、接続オブジェクトを通じて完了します。、このオブジェクトの一般的なメソッド:

ステートメントの作成()SQLをデータベースに送信するステートメントオブジェクトを作成します。
準備ステートメント(SQL)プリコンパイルされた SQL をデータベースに送信する PrepareSatement オブジェクトを作成します。
setAutoCommit(ブール値 autoCommit)トランザクションを自動的にコミットするかどうかを設定します
専念()リンク上でトランザクションを送信
ロールバック()このリンク上のトランザクションをロールバックします

(3) ステートメントクラス

Statement オブジェクトは、SQL ステートメントをデータベースに送信するために使用されます。 Statement オブジェクトの一般的なメソッドは次のとおりです。

クエリを実行します(文字列sql)クエリ ステートメントをデータに送信し、ResultSet 結果セットを返すために使用されます。
更新を実行します(文字列sql)挿入、更新、または削除ステートメントをデータベースに送信し、データベース内で更新された行数を返すために使用されます。
実行(文字列SQL)任意の SQL ステートメントをデータベースに送信するために使用されます。
addBatch(文字列sql)複数の SQL ステートメントをバッチに入れる
バッチ実行()SQL ステートメントのバッチをデータベースに送信して実行します

知らせ:

操作効率を向上させるため、通常、execute メソッドは直接使用されません。

代わりに、クエリの場合はexecuteQueryを、追加、削除、および変更の場合はexecuteUpdateを使用してください。

(4) ResultSetクラス

ResultSet は、SQL ステートメントの実行結果を表すために使用されます。

結果セット 実行結果セットをカプセル化する場合は、次を使用します。テーブルに似たもの

ResultSet オブジェクトは、テーブル データ行を指すカーソルを保持します。最初、カーソルは最初の行の前にあります

ResultSet.next() メソッドを呼び出すと、カーソルが特定のデータ行をポイントし、getxxx メソッドを呼び出してその行のデータを取得できます。

① あらゆるデータを取得

getObject(int インデックス)

指定された列数に応じてObjectオブジェクトを取得します

getObject(文字列列名)指定された属性名に従って Object オブジェクトを取得します

② String型の取得など、指定した型のデータを取得する

getString(int インデックス)

指定された列数に基づいて String オブジェクトを取得します

getString(文字列列名)指定された属性名に従って String オブジェクトを取得します

③ 結果セットをスクロールする方法: 

次()次の行に移動
前の()前の行に移動
絶対(int 行)指定した行に移動
beforeFirst()resultSet の前に移動
afterLast()resultSet の末尾に移動

(5) リソースの解放

Jdbc プログラムの実行が終了したら、実行プロセス中にデータベースと対話するためにプログラムによって作成されたオブジェクトを忘れずに解放してください。

これらのオブジェクトは通常、ResultSet、Statement、および Connection オブジェクトです。

特に Connection オブジェクトは非常にまれなリソースであるため、使用後はすぐに解放する必要があります。

接続を迅速かつ正確に閉じることができない場合、システムのダウンタイムが容易に発生する可能性があります。

リソース解放コードを確実に実行できるようにするには、リソース解放コードをfinally ステートメントにも配置する必要があります。

4.SQLインジェクションの問題

開発に Statement を使用する場合、次の 2 つの問題があります。

①弦を頻繁に繋ぎ合わせる必要があり、エラー率が高い

  1. String username = "zhangsan";
  2. String password = "123";
  3. String sql = "select * from users where name='"+username+"' and password='"+password+"'";
  4. ResultSet rs = st.executeQuery(sql);

② SQLインジェクションの潜在的なリスクがある

SQL インジェクション: 文字列を結合する巧妙な手法を使用して、ユーザーが入力したデータに不正な SQL ステートメントを挿入し、SQL ショートサーキットを引き起こし、データベース データを盗みます。

  1. public class SQLTest {
  2. public static void main(String[] args) throws Exception {
  3. // 正常登陆sql:select * from users where name='张三' and password ='123456'
  4. //login("张三","123456");
  5. // SQL 注入:select * from users where name='' and password ='123456' or '1'='1'
  6. login("", "123456' or '1'='1");
  7. }
  8. public static void login(String username, String password) throws Exception {
  9. String url = "jdbc:mysql://localhost:3306/jdbc?useUnicode=true&characterEncoding=utf8&useSSL=true";
  10. String dbUsername = "root";
  11. String dbPassword = "123456";
  12. //1.加载驱动
  13. Class.forName("com.mysql.cj.jdbc.Driver");
  14. //2.获取与数据库的链接
  15. Connection conn = DriverManager.getConnection(url, dbUsername, dbPassword);
  16. //3.获取用于向数据库发送sql语句的statement
  17. Statement st = conn.createStatement();
  18. String sql = "select * from users where name='" + username + "' and password='" + password + "'";
  19. System.out.println(sql);
  20. //4.向数据库发sql,并获取代表结果集的rs
  21. ResultSet rs = st.executeQuery(sql);
  22. if (rs.next()) {
  23. System.out.println("登录成功");
  24. } else {
  25. System.out.println("登录失败");
  26. }
  27. //6.关闭链接,释放资源
  28. rs.close();
  29. st.close();
  30. conn.close();
  31. }
  32. }

操作結果:

知らせ:

SQL ステートメント内の and の優先順位は or より大きいため、実行される SQL は select * from users where '1' = '1'; と同等になります。


解決:SQL プレースホルダーの機能を提供する Statement のサブクラスである PreparedStatement を使用します。

文字列をつなぎ合わせる必要がなく、ユーザーが入力したデータは完全に検出されるため、より安全になります。

  1. public class PSTest {
  2. public static void main(String[] args) throws Exception {
  3. // 正常登陆sql:select * from users where name='张三' and password ='123456'
  4. //login("张三","123456");
  5. // SQL 注入:select * from users where name='' and password ='123456' or '1'='1'
  6. login("", "123456' or '1'='1");
  7. }
  8. public static void login(String username, String password) throws Exception {
  9. String url = "jdbc:mysql://localhost:3306/jdbc?useUnicode=true&characterEncoding=utf8&useSSL=true";
  10. String dbUsername = "root";
  11. String dbPassword = "123456";
  12. Class.forName("com.mysql.cj.jdbc.Driver");
  13. Connection conn = DriverManager.getConnection(url, dbUsername, dbPassword);
  14. //获取用于向数据库发送预编译sql语句的prepareStatement
  15. String sql = "select * from users where name = ? and password = ?";
  16. System.out.println(sql);
  17. PreparedStatement ps = conn.prepareStatement(sql);
  18. //给占位符 ? 填充数据
  19. ps.setString(1, username);
  20. ps.setString(2, password);
  21. ResultSet rs = ps.executeQuery();
  22. if (rs.next()) {
  23. System.out.println("登录成功");
  24. } else {
  25. System.out.println("登录失败");
  26. }
  27. rs.close();
  28. ps.close();
  29. conn.close();
  30. }
  31. }

操作結果:

5.JDBCトランザクション

トランザクションは、すべてが成功したか、すべてが失敗したかの論理的な一連の操作を指します (ACID 原則)。

Jdbc プログラムがデータベースから Connection オブジェクトを取得すると、デフォルトでは、Connection オブジェクトはデータベースにトランザクションを自動的に送信します。

このデフォルトの送信方法をオフにして、1 つのトランザクションで複数の SQL を実行できるようにする場合は、次の JDBC 制御トランザクション ステートメントを使用できます。

接続.setAutoCommit(false);

トランザクションを開始する
接続.ロールバック();トランザクションのロールバック
接続をコミットします。トランザクションをコミットする

①アカウントテーブルの作成

  1. /*创建账户表*/
  2. CREATE TABLE account(
  3. id INT PRIMARY KEY AUTO_INCREMENT,
  4. NAME VARCHAR(40),
  5. money DECIMAL(9,2)
  6. );
  7. /*插入测试数据*/
  8. insert into account(name,money) values('A',1000);
  9. insert into account(name,money) values('B',1000);
  10. insert into account(name,money) values('C',1000);

② 移管成功時のビジネスシナリオをシミュレーションする

  1. //失败后让数据库自动回滚事务
  2. public class Demo {
  3. public static void main(String[] args) {
  4. String url = "jdbc:mysql://localhost:3306/jdbc?useUnicode=true&characterEncoding=utf8&useSSL=true";
  5. String username = "root";
  6. String password = "123456";
  7. Connection conn = null;
  8. try {
  9. Class.forName("com.mysql.cj.jdbc.Driver");
  10. conn = DriverManager.getConnection(url, username, password);
  11. //通知数据库开启事务,false表示开启
  12. conn.setAutoCommit(false);
  13. String sql1 = "update account set money=money-100 where name = 'A' ";
  14. conn.prepareStatement(sql1).executeUpdate();
  15. //模拟执行完SQL1之后程序出现了异常而导致后面的SQL无法正常执行,事务也无法正常提交
  16. int x = 1/0;
  17. String sql2 = "update account set money=money+100 where name = 'B' ";
  18. conn.prepareStatement(sql2)executeUpdate();
  19. //sql1 和 sql2都顺利执行,就提交事务
  20. conn.commit();
  21. System.out.println("成功!!!");
  22. } catch (Exception e) {
  23. //出现异常,通知数据库回滚事务
  24. conn.rollback();
  25. e.printStackTrace();
  26. } finally {
  27. conn.close();
  28. }
  29. }
  30. }

6.JDBCツールクラス

さまざまなリクエストでは、毎回データベースに接続してリソースを解放する必要があり、繰り返しコードが大量に記述されます。

データベース接続の準備と解放の操作をツール クラスにカプセル化し、使用時に直接呼び出して、繰り返しコードを記述することを避けます。

  1. public class JdbcUtil {
  2. private static Connection connection;
  3. private static String url = "jdbc:mysql://localhost:3306/jdbc?useUnicode=true&characterEncoding=utf8&useSSL=true";
  4. private static String username = "root";
  5. private static String password = "123456";
  6. //驱动(类)只需要加载一次,放静态代码块即可
  7. static {
  8. try {
  9. //加载数据库驱动
  10. Class.forName("com.mysql.cj.jdbc.Driver");
  11. } catch (Exception e) {
  12. e.printStackTrace();
  13. }
  14. }
  15. // 获取数据库连接对象
  16. public static Connection getConnection() throws SQLException {
  17. return DriverManager.getConnection(url, username, password);
  18. }
  19. // 释放资源(利用多态:Statement 和 PreparedStatement 都可以传进来)
  20. public static void release(Connection conn, Statement st, ResultSet rs) {
  21. try {
  22. if (rs != null) {
  23. rs.close();
  24. }
  25. if (st != null) {
  26. st.close();
  27. }
  28. if (conn != null) {
  29. conn.close();
  30. }
  31. } catch (SQLException e) {
  32. e.printStackTrace();
  33. }
  34. }
  35. }

コールケース(例: ユーザーの追加)

  1. public void add(String name, String password) {
  2. Connection conn = null;
  3. PreparedStatement ps = null;
  4. try {
  5. conn = JdbcUtil.getConnection();
  6. String sql = "insert into users(name,password) values(?,?)";
  7. ps = conn.prepareStatement(sql);
  8. ps.setString(1, name);
  9. ps.setString(2, password);
  10. ps.executeUpdate();
  11. } catch (SQLException e) {
  12. e.printStackTrace();
  13. } finally {
  14. JdbcUtil.release(conn, ps, null);
  15. }
  16. }

7. データベース接続プール

(1。概要

JDBC 開発プロセス:

Ⅰ. データベースドライバーをロードします(ロードは一度だけ必要です)

Ⅱ. データベース接続を確立する(接続)

Ⅲ. Connection によって生成された SQL をデータベースに送信する Statement オブジェクトを作成します。

Ⅳ. SQL文の書き方

Ⅴ. SQL を実行します (クエリ -&gt; ResultSet オブジェクトが結果セットを受け取ります)

Ⅵ. 接続を閉じてリソースを解放します。


データベース接続オブジェクトは DriverManager を通じて取得されます。取得するたびに、データベースに接続を申請し、ユーザー名とパスワードを確認する必要があります。

ユーザーはリクエストごとにデータベースからリンクを取得する必要があり、データベースへの接続の作成には通常、比較的大きなリソースが消費され、作成に時間がかかります。

SQL ステートメントが実行されるたびに接続が切断されるため、リソースの無駄が発生し、データベース接続リソースが適切に再利用されません。

Web サイトが 1 日に 100,000 件のアクセスを受けると仮定すると、データベース サーバーは 100,000 件の接続を作成する必要があります。これはデータベース リソースの膨大な無駄であり、データベース サーバーのメモリ オーバーフローやマシンの拡張が容易に発生する可能性があります。

解決策: データベース接続プール

データベース接続プールの基本的な考え方:

データベース用のバッファプールを確立し、あらかじめ一定数の接続オブジェクトをバッファプールに入れておきます。

データベース接続を取得する場合は、バッファー プールからオブジェクトを取得するだけで済みます。

使用後は、次のリクエストで使用できるようにバッファー プールに戻すことで、繰り返し作成することなくリソースを再利用できます。

データベース接続プールにアイドル状態の接続オブジェクトがない場合、新しいリクエストは待機キューに入り、他のスレッドが接続を解放するのを待ちます。

(2) 用途

JDBC のデータベース接続プールは、javax.sql.DataSource インターフェイスを使用して完成します。DataSource は、Java によって正式に提供されるインターフェイスです。

これを使用する場合、開発者は自分でインターフェイスを実装する必要はなく、サードパーティのツールを使用できます。

C3P0 は一般的に使用されるサードパーティの実装であり、実際の開発では、C3P0 を直接使用してデータベース接続プールの操作を完了できます。

使用手順:

①pom.xmlに依存関係をインポートする

  1. <dependency>
  2. <groupId>com.mchange</groupId>
  3. <artifactId>c3p0</artifactId>
  4. <version>0.9.5.2</version>
  5. </dependency>

②コードを書く

  1. public class DataSourceTest {
  2. public static void main(String[] args) {
  3. try {
  4. //创建C3P0数据库连接池
  5. ComboPooledDataSource dataSource=new ComboPooledDataSource();
  6. dataSource.setDriverClass("com.mysql.cj.jdbc.Driver");
  7. dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/jdbc?useUnicode=true&characterEncoding=utf8");
  8. dataSource.setUser("root");
  9. dataSource.setPassword("123456");
  10. //设置初始化连接个数
  11. dataSource.setInitialPoolSize(5);
  12. //设置最大连接个数(连接池中不够,可以继续申请,申请后最终的上限)
  13. dataSource.setMaxPoolSize(20);
  14. //当连接对象不够时,再次申请的连接对象个数
  15. dataSource.setAcquireIncrement(5);
  16. //设置最小连接数(当连接池中剩余2个连接对象时,就去申请 --> 提前做准备)
  17. dataSource.setMinPoolSize(2);
  18. Connection conn=dataSource.getConnection();
  19. //SQL操作...
  20. //将连接还回到数据库连接池中
  21. conn.close();
  22. } catch (PropertyVetoException e) {
  23. e.printStackTrace();
  24. } catch (SQLException e) {
  25. e.printStackTrace();
  26. }
  27. }
  28. }

知らせ:

従来の方法で取得した接続: com.mysql.cj.jdbc.ConnectionImpl@3c153a1

C3P0 によって取得された接続: com.mchange.v2.c3p0.impl.NewProxyConnection@6156496

それで、同じ close メソッドを呼び出しますが、実装クラスが異なるため、メソッドの書き換えも異なります。これがインターフェイスのポリモーフィズムです。

C3P0 の close メソッドは、接続リソースを直接破棄しませんが、接続をデータベース接続プールに返します。


データベース接続プールのパラメータを設定する上記の方法は、Java プログラムに直接記述されます。

これが採用されるハードコード道、設定を変更するたびに再コンパイルが必要、新しいクラス ファイルを生成します。効率が低すぎます。

実際の開発では、C3P0 の構成情報は XML ファイルで定義されており、Java プログラムは構成ファイルをロードするだけでデータベース接続プールの初期化操作を完了します。

その後、構成を変更するだけで、再コンパイルせずに XML 内の構成を変更できます。

使用手順:

① リソースディレクトリに、c3p0-config.xml という名前の新しいファイルを作成します。

② c3p0-config.xml に設定情報を入力します。

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <c3p0-config>
  3. <!--配置连接池mysql-->
  4. <named-config name="C3P0Test">
  5. <!-- 指定连接数据源的基本属性 -->
  6. <property name="driverClass">com.mysql.cj.jdbc.Driver</property>
  7. <property name="jdbcUrl">jdbc:mysql://localhost:3306/jdbc?useUnicode=true&amp;characterEncoding=utf8</property>
  8. <property name="user">root</property>
  9. <property name="password">123456</property>
  10. <!-- 设置初始化连接个数 -->
  11. <property name="initialPoolSize">5</property>
  12. <!-- 设置最大连接个数(连接池中不够,可以继续申请,申请后最终的上限) -->
  13. <property name="maxPoolSize">20</property>
  14. <!-- 当连接对象不够时,再次申请的连接对象个数 -->
  15. <property name="acquireIncrement">5</property>
  16. <!-- 设置最小连接数(当连接池中剩余2个连接对象时,就去申请 -> 提前做准备) -->
  17. <property name="minPoolSize">2</property>
  18. </named-config>
  19. </c3p0-config>

③Javaプログラムを書く

  1. public class DataSourceTest {
  2. public static void main(String[] args) {
  3. try {
  4. //创建C3P0数据库连接池
  5. ComboPooledDataSource dataSource=new ComboPooledDataSource("C3P0Test");
  6. Connection conn=dataSource.getConnection();
  7. System.out.println(conn);
  8. //将连接还回到数据库连接池中
  9. conn.close();
  10. }catch (SQLException e) {
  11. e.printStackTrace();
  12. }
  13. }
  14. }

知らせ:

① ComboPooledDataSource構築メソッドのパラメータは、c3p0-config.xmlに設定されているnamed-configタグのname属性値です。

② このとき、JDBC ツールクラスは次のように変更できます。

  1. public class JdbcUtil {
  2. private static DataSource dataSource;
  3. static {
  4. dataSource = new ComboPooledDataSource("C3P0Test");
  5. }
  6. // 获取数据库连接对象
  7. public static Connection getConnection() throws SQLException {
  8. Connection conn = null;
  9. try {
  10. conn = dataSource.getConnection();
  11. } catch (SQLException e) {
  12. e.printStackTrace();
  13. }
  14. return conn;
  15. }
  16. // 释放资源(利用多态:Statement 和 PreparedStatement 都可以传进来)
  17. public static void release(Connection conn, Statement st, ResultSet rs) {
  18. try {
  19. if (rs != null) {
  20. rs.close();
  21. }
  22. if (st != null) {
  23. st.close();
  24. }
  25. if (conn != null) {
  26. conn.close();
  27. }
  28. } catch (SQLException e) {
  29. e.printStackTrace();
  30. }
  31. }
  32. }

8.DBユーティリティ

  1. public static Student findById(Integer idx) {
  2. Connection conn = null;
  3. PreparedStatement st = null;
  4. ResultSet rs = null;
  5. Student stu = null;
  6. try {
  7. conn = JdbcUtil.getConnection();
  8. String sql = "select * from student where id = ?";
  9. PreparedStatement ps = conn.prepareStatement(sql);
  10. //给占位符 ? 填充数据
  11. ps.setInt(1, idx);
  12. rs = ps.executeQuery();
  13. //取出结果集的数据
  14. while (rs.next()) {
  15. Integer id = rs.getInt(1);
  16. String name = rs.getString(2);
  17. Double score = rs.getDouble(3);
  18. Date birthday = rs.getDate(4);
  19. stu = new Student(id, name, score, birthday);
  20. }
  21. } catch (SQLException e) {
  22. e.printStackTrace();
  23. } finally {
  24. try {
  25. //关闭链接,释放资源
  26. rs.close();
  27. st.close();
  28. conn.close();
  29. } catch (SQLException e) {
  30. e.printStackTrace();
  31. }
  32. }
  33. return stu;
  34. }

上記のコードでは、プレースホルダーにデータを埋めたり、結果セットからデータを抽出したりする操作が非常に面倒です。

Student テーブルに 100 個の属性がある場合、データを取得するために while ループに 100 行を書き込む必要があり、プレースホルダーを埋めるために多くの行が必要になる場合もあります。


解決:DBUtils は、開発者がデータのカプセル化 (Java オブジェクトへの結果セットのマッピング) を完了するのに役立ちます。

使用手順:

①pom.xmlに依存関係をインポートする

  1. <dependency>
  2. <groupId>commons-dbutils</groupId>
  3. <artifactId>commons-dbutils</artifactId>
  4. <version>1.6</version>
  5. </dependency>

②コードを書く

  1. public static Student findById(Integer idx) {
  2. Connection conn = null;
  3. Student stu = null;
  4. try {
  5. conn = JdbcUtil.getConnection();
  6. String sql = "select * from student where id = ?";
  7. //使用DBUtils
  8. QueryRunner qr = new QueryRunner();
  9. stu = qr.query(conn, sql, new BeanHandler<>(Student.class), idx);
  10. } catch (SQLException e) {
  11. e.printStackTrace();
  12. } finally {
  13. try {
  14. //关闭链接,释放资源
  15. conn.close();
  16. } catch (SQLException e) {
  17. e.printStackTrace();
  18. }
  19. }
  20. return stu;
  21. }

この時点で、プレースホルダーの入力と結果セットの取り出しは 2 行のコードで実行できます。

詳細:

① クエリ メソッドは 4 つのパラメータを渡す必要があります。

接続オブジェクト

SQL文

ResultSetHandler インターフェースの実装クラス オブジェクト (変換されたオブジェクトの型は Student.class である必要があります)

プレースホルダーを設定するためのパラメーター

② ResultSetHandler インターフェースは、クエリされた結果セットを Java オブジェクトに変換するために使用されます。 以下の 4 つの実装クラスが提供されます。

BeanHandler結果セットを Java オブジェクト (Student オブジェクトなど) にマッピングします。
BeanListHandler結果セットを List コレクションにマップします (例: List<Student > )
マップハンドラ

結果セットを Map コレクション オブジェクトにマップします。

(つまり: 地図<String,Object>キー: 属性名; 値: 属性値)

マップリストハンドラ結果セットを MapList コレクションにマップします (つまり、List <Map<<String,Object> &gt;)

③ プレースホルダーに入力するパラメータは可変パラメータであるため、ユーザーのさまざまなニーズを満たすために任意の数のパラメータを渡すことができます。

④ 変換されたオブジェクトクラス (Student クラス) はパラメータのないコンストラクタを持たなければなりません。そうでない場合、プログラムはエラーを報告します。

理由:最下位層は Student.class を通じてこのクラスを見つけ、さらに Student.class を通じてこのクラスを見つけます。反射機構このクラスのパラメータなしのコンストラクターを見つけて、そのオブジェクトを作成します。

⑤ クラス内の属性の名前は、データベース テーブル内のフィールドの名前とまったく同じである必要があります。

オブジェクトの作成後、結果セットに基づいてオブジェクトのプロパティに値を割り当てるときに、名前に基づいて検索と割り当てが実行されるためです。

11. MVC 3 層アーキテクチャ

MVCとは何ですか?

  • モデル: モデル (サービス、dao、エンティティ)
  • ビュー: ビュー (jsp、html、アプリクライアント)
  • コントローラー: コントローラー (サーブレット、ハンドダー、アクション)

リクエストが JavaWeb アプリケーションに入ると、コントローラーはリクエストを受け取り、ビジネス ロジック処理を実行し、最終的に結果をユーザーに返します (ビュー + モデル)。

1 はじめに

初期の頃、Web アプリケーションを操作する場合、ユーザーは制御層に直接アクセスし、制御層はデータベースを直接操作できました。

サーブレット -- CRUD (追加、削除、変更) --&gt; データベース

サーブレット コード内: リクエスト、レスポンス、ビュー ジャンプの処理、JDBC 処理、ビジネス コード処理、ロジック コード処理

短所:プログラムが非常に肥大化しており、メンテナンスが困難です

解決: 別のレイヤーを追加しても解決できないことはありません。解決できる場合は、別のレイヤーを追加してください。

2. mvc 3 層アーキテクチャ

モデル

  • 業務処理:ビジネスロジック(サービス)
  • データ永続層: CRUD (DAO データ永続オブジェクト)

ビュー

  • 表示データ
  • サーブレットリクエストを開始するためのリンクを提供します (a、form、img...)

コントローラ(サーブレット)

  • ユーザーのリクエストを受信します: (req、リクエストパラメータ、セッション情報)
  • それをビジネス層に渡して、対応するコードを処理します
  • コントロールビュージャンプ

ユーザーと管理者のログインを例に挙げます。

コントローラー層:

  1. @WebServlet("/login")
  2. public class LoginServlet extends HttpServlet {
  3. private LoginService loginService = new LoginServiceImpl();
  4. /* 处理登录的业务逻辑*/
  5. @Override
  6. protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  7. String username = req.getParameter("username");
  8. String password = req.getParameter("password");
  9. String type = req.getParameter("type");
  10. Object object = loginService.login(username,password,type);
  11. if(object != null){
  12. HttpSession session = req.getSession();
  13. switch (type){
  14. case "reader":
  15. Reader reader = (Reader) object;
  16. session.setAttribute("reader",reader);
  17. //跳转到用户的首页
  18. resp.sendRedirect("/book?page=1");
  19. break;
  20. case "admin":
  21. Admin admin = (Admin) object;
  22. session.setAttribute("admin",admin);
  23. //跳转到管理员的首页
  24. resp.sendRedirect("/admin?method=findAllBorrow&page=1");
  25. break;
  26. }
  27. }else{
  28. resp.sendRedirect("login.jsp");
  29. }
  30. }
  31. }

サービス層:

  1. public interface LoginService {
  2. //利用多态,动态返回不同类型的对象
  3. public Object login(String username,String password,String type);
  4. }
  1. public class LoginServiceImpl implements LoginService {
  2. private ReaderRepository readerRepository = new ReaderRepositoryImpl();
  3. private AdminRepository adminRepository = new AdminRepositoryImpl();
  4. @Override
  5. public Object login(String username, String password,String type) {
  6. Object object = null;
  7. //业务逻辑处理:根据type的值,来选择调用不同的登录方法,去查找不同的表
  8. switch (type){
  9. case "reader":
  10. object = readerRepository.login(username,password);
  11. break;
  12. case "admin":
  13. object = adminRepository.login(username, password);
  14. break;
  15. }
  16. return object;
  17. }
  18. }

Dao/リポジトリ層:

  1. public interface AdminRepository {
  2. public Admin login(String username,String password);
  3. }
  1. public interface ReaderRepository {
  2. public Reader login(String username,String password);
  3. }
  1. public class AdminRepositoryImpl implements AdminRepository {
  2. //管理员的登录方法(和数据库交互)
  3. @Override
  4. public Admin login(String username, String password) {
  5. Connection connection = JDBCTools.getConnection();
  6. String sql = "select * from bookadmin where username = ? and password = ?";
  7. PreparedStatement statement = null;
  8. ResultSet resultSet = null;
  9. Admin admin = null;
  10. try {
  11. statement = connection.prepareStatement(sql);
  12. statement.setString(1,username);
  13. statement.setString(2,password);
  14. resultSet = statement.executeQuery();
  15. if(resultSet.next()){
  16. admin = new Admin(resultSet.getInt(1),resultSet.getString(2),resultSet.getString(3));
  17. }
  18. } catch (SQLException e) {
  19. e.printStackTrace();
  20. } finally {
  21. JDBCTools.release(connection,statement,resultSet);
  22. }
  23. return admin;
  24. }
  25. }
  1. public class ReaderRepositoryImpl implements ReaderRepository {
  2. //用户的登录方法(和数据库交互)
  3. @Override
  4. public Reader login(String username, String password) {
  5. Connection connection = JDBCTools.getConnection();
  6. String sql = "select * from reader where username = ? and password = ?";
  7. PreparedStatement statement = null;
  8. ResultSet resultSet = null;
  9. Reader reader = null;
  10. try {
  11. statement = connection.prepareStatement(sql);
  12. statement.setString(1,username);
  13. statement.setString(2,password);
  14. resultSet = statement.executeQuery();
  15. if(resultSet.next()){
  16. reader = new Reader(resultSet.getInt(1),resultSet.getString(2),resultSet.getString(3),resultSet.getString(4),resultSet.getString(5),resultSet.getString(6),resultSet.getString(7));
  17. }
  18. } catch (SQLException e) {
  19. e.printStackTrace();
  20. } finally {
  21. JDBCTools.release(connection,statement,resultSet);
  22. }
  23. return reader;
  24. }
  25. }