Technologieaustausch

JavaWeb (3: JDBC und MVC)

2024-07-12

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

JavaWeb (1: Grundkenntnisse und Umgebungskonstruktion)icon-default.png?t=N7T8https://blog.csdn.net/xpy2428507302/article/details/140365130?spm=1001.2014.3001.5501JavaWeb (2: Servlet und Jsp, Listener und Filter)icon-default.png?t=N7T8https://blog.csdn.net/xpy2428507302/article/details/140365159?spm=1001.2014.3001.5501

Inhaltsverzeichnis

10. JDBC

1. Übersicht

2.Schreiben Sie ein JDBC-Programm

3. Detaillierte Analyse

(1) Datenbank-URL

(2) Verbindungsklasse

(3) Anweisungsklasse

(4) ResultSet-Klasse

(5) Ressourcen freigeben

4.SQL-Injection-Problem

5.JDBC-Transaktionen

6.JDBC-Toolklasse

7. Datenbankverbindungspool

(1. Übersicht

(2) Verwendung

8.DBUtils

11. Dreistufige MVC-Architektur

1. Einleitung

2. Dreistufige MVC-Architektur


10. JDBC

1. Übersicht

JDBC (Java DataBase Connectivity): Java-Datenbankverbindungstechnologie

JDBC ist ein von einer bestimmten Datenbank unabhängiges Verwaltungssystem und eine öffentliche Schnittstelle für den universellen Zugriff und Betrieb von SQL-Datenbanken.

Definiert eine Reihe von Standards, um eine einheitliche Möglichkeit für den Zugriff auf verschiedene Datenbanken bereitzustellen.

JDBC ist eine Anwendungs-API für den Datenbankzugriff, die aus einer Reihe von Klassen und Schnittstellen besteht, die in der Java-Sprache geschrieben sind.

Beinhaltet zwei Ebenen:

① Anwendungsorientierte API zum Aufrufen durch Programmierer.

② Datenbankorientierte API für Hersteller zur Entwicklung von Datenbanktreibern.

Um verschiedene Datenbanken mit JDBC zu verbinden, müssen Sie lediglich verschiedene Datenbanktreiberpakete laden und müssen sich keine Gedanken über die Unterschiede in den Datenbankbetriebssprachen machen.

2.Schreiben Sie ein JDBC-Programm

① Datenbanktabelle erstellen

  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;

② Datenbankabhängigkeiten importieren

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

③ Verwendung von JDBC:

Ⅰ Laden Sie den Datenbanktreiber (die Brücke zwischen dem Java-Programm und der Datenbank).

Ⅱ. Verbindung herstellen, eine Verbindung zwischen dem Java-Programm und der Datenbank

Ⅲ. Erstellen Sie ein Statement-Objekt, das von Connection generierte SQL-Anweisungen an die Datenbank sendet

Ⅳ Schreiben Sie SQL-Anweisungen (schreiben Sie je nach Unternehmen unterschiedliche SQLs).

Ⅴ Führen Sie SQL aus (wenn Sie den Rückgabewert erhalten möchten, erstellen Sie ein ResultSet-Objekt, um die Abfrageergebnisse zu speichern, nachdem die Anweisung ausgeführt wurde)

Ⅵ Schließen Sie die Verbindung

  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. Detaillierte Analyse

(1) Datenbank-URL

jdbc:mysql://localhost:3306/xxx

jdbcProtokoll
MySQLUnterprotokoll
lokaler Host: 3306host:port
xxxDatenbank

So schreiben Sie häufig verwendete Datenbank-URL-Adressen:

  • Oracle-Schreiben: jdbc:oracle:thin:@localhost:1521:xxx
  • SqlServer-Name: jdbc:microsoft:sqlserver://localhost:1433; DatabaseName=xxx
  • MySQL-Schreibmethode: jdbc:mysql://localhost:3306/xxx

(2) Verbindungsklasse

Die Verbindung wird zur Darstellung von Datenbankverknüpfungen verwendet, und die Sammlung ist das wichtigste Objekt in der Datenbankprogrammierung.

Alle Interaktionen zwischen dem Client und der Datenbank werden über das Verbindungsobjekt abgeschlossen., gängige Methoden dieses Objekts:

Anweisung erstellen()Erstellen Sie ein Anweisungsobjekt, das SQL an die Datenbank sendet
bereiten SieStatement(sql) vorErstellen Sie ein PrepareSatement-Objekt, das vorkompiliertes SQL an die Datenbank sendet
setAutoCommit(boolean autoCommit)Legen Sie fest, ob Transaktionen automatisch festgeschrieben werden
begehen()Senden Sie die Transaktion über den Link
Rollback()Rollback-Transaktion über diesen Link

(3) Anweisungsklasse

Das Statement-Objekt wird zum Senden von SQL-Anweisungen an die Datenbank verwendet. Allgemeine Methoden des Statement-Objekts:

Abfrage ausführen(String sql)Wird verwendet, um Abfrageanweisungen an Daten zu senden und eine ResultSet-Ergebnismenge zurückzugeben
führeUpdate aus (String sql)Wird verwendet, um Einfügungs-, Aktualisierungs- oder Löschanweisungen an die Datenbank zu senden und die Anzahl der in der Datenbank aktualisierten Zeilen zurückzugeben
ausführen(String sql)Wird verwendet, um beliebige SQL-Anweisungen an die Datenbank zu senden
addBatch(String sql)Fügen Sie mehrere SQL-Anweisungen in einen Stapel ein
führeBatch() ausSenden Sie einen Stapel SQL-Anweisungen zur Ausführung an die Datenbank

Beachten:

Um die Betriebseffizienz zu verbessern, wird die Ausführungsmethode im Allgemeinen nicht direkt verwendet.

Verwenden Sie sie stattdessen entsprechend: „executeQuery“ für Abfragen; „executeUpdate“ für Hinzufügungen, Löschungen und Änderungen.

(4) ResultSet-Klasse

ResultSet wird verwendet, um die Ausführungsergebnisse von SQL-Anweisungen darzustellen.

Ergebnismenge Verwenden Sie beim Kapseln des Ausführungsergebnissatzesähnlich wie bei TischDer Weg

Das ResultSet-Objekt verwaltet einen Cursor, der auf die Tabellendatenzeilen zeigt.Der Cursor befindet sich zunächst vor der ersten Zeile

Durch Aufrufen der Methode ResultSet.next () kann der Cursor auf eine bestimmte Datenzeile zeigen und die Methode getxxx aufrufen, um die Daten dieser Zeile abzurufen.

① Holen Sie sich jede Art von Daten

getObject(int-Index)

Rufen Sie das Objektobjekt entsprechend der angegebenen Anzahl von Spalten ab

getObject(Zeichenfolge Spaltenname)Rufen Sie das Objektobjekt gemäß dem angegebenen Attributnamen ab

② Rufen Sie Daten des angegebenen Typs ab, z. B. den String-Typ

getString(int index)

Rufen Sie ein String-Objekt basierend auf der angegebenen Anzahl von Spalten ab

getString(String Spaltenname)Rufen Sie das String-Objekt gemäß dem angegebenen Attributnamen ab

③ So scrollen Sie durch die Ergebnismenge: 

nächste()Zur nächsten Zeile wechseln
Vorherige()Zur vorherigen Zeile wechseln
absolut (int Zeile)Zur angegebenen Zeile wechseln
vorErstes()Verschieben Sie die Vorderseite von resultSet
nachLetztem()Zum Ende von resultSet wechseln

(5) Ressourcen freigeben

Denken Sie nach Abschluss der Ausführung des JDBC-Programms daran, die vom Programm erstellten Objekte für die Interaktion mit der Datenbank während des laufenden Prozesses freizugeben.

Diese Objekte sind normalerweise ResultSet-, Statement- und Connection-Objekte.

Insbesondere das Connection-Objekt ist eine sehr seltene Ressource und muss sofort nach der Verwendung freigegeben werden.

Wenn die Verbindung nicht zeitnah und korrekt geschlossen werden kann, kann es leicht zu Systemausfällen kommen.

Um sicherzustellen, dass der Ressourcenfreigabecode ausgeführt werden kann, muss der Ressourcenfreigabecode auch in der final-Anweisung platziert werden.

4.SQL-Injection-Problem

Bei der Verwendung von Statement für die Entwicklung gibt es zwei Probleme:

① String-Strings müssen häufig gespleißt werden und die Fehlerrate ist hoch

  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);

② Es besteht die potenzielle Gefahr einer SQL-Injection

SQL-Injection: Einfügen illegaler SQL-Anweisungen in die vom Benutzer eingegebenen Daten unter Verwendung cleverer Techniken zum Zusammenfügen von Zeichenfolgen, was zu einem SQL-Kurzschluss führt und dadurch Datenbankdaten stiehlt.

  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. }

Operationsergebnis:

Beachten:

Die Priorität von und in der SQL-Anweisung ist größer als oder, daher entspricht die ausgeführte SQL der Auswahl * von Benutzern, wobei '1' = '1';


Lösung:Verwenden Sie PreparedStatement, eine Unterklasse von Statement, die die Funktion eines SQL-Platzhalters bereitstellt.

Es ist nicht erforderlich, Zeichenfolgen zu verbinden, und die vom Benutzer eingegebenen Daten werden vollständig erkannt, was die Sicherheit erhöht.

  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. }

Operationsergebnis:

5.JDBC-Transaktionen

Eine Transaktion bezieht sich auf eine logische Reihe von Vorgängen, die entweder alle erfolgreich oder alle erfolglos sind (ACID-Prinzip).

Wenn das JDBC-Programm ein Verbindungsobjekt aus der Datenbank erhält, sendet das Verbindungsobjekt standardmäßig automatisch eine Transaktion an die Datenbank.

Wenn Sie diese Standardübermittlungsmethode deaktivieren und die Ausführung mehrerer SQLs in einer Transaktion zulassen möchten, können Sie die folgende JDBC-Steuertransaktionsanweisung verwenden.

Verbindung.setAutoCommit(false);

Transaktion starten
Verbindung.rollback();Rollback-Transaktion
Verbindung.commit();Transaktion festschreiben

① Kontotabelle erstellen

  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);

② Simulieren Sie das Geschäftsszenario, wenn die Übertragung erfolgreich ist

  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-Toolklasse

Bei unterschiedlichen Anforderungen müssen Sie jedes Mal eine Verbindung zur Datenbank herstellen und Ressourcen freigeben, und es wird viel wiederholter Code geschrieben.

Kapseln Sie die Vorbereitungs- und Freigabevorgänge für die Datenbankverbindung in einer Toolklasse und rufen Sie sie direkt auf, wenn Sie sie verwenden, um das Schreiben wiederholten Codes zu vermeiden.

  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. }

Anruffall, z. B.: Benutzer hinzufügen

  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. Datenbankverbindungspool

(1. Übersicht

JDBC-Entwicklungsprozess:

Ⅰ Laden Sie den Datenbanktreiber (muss nur einmal geladen werden)

Ⅱ Datenbankverbindung herstellen (Verbindung)

Ⅲ. Erstellen Sie ein Statement-Objekt, das von Connection generierte SQL-Anweisungen an die Datenbank sendet

Ⅳ. SQL-Anweisungen schreiben

Ⅴ. SQL ausführen (Query--&gt;ResultSet-Objekt empfängt Ergebnismenge)

Ⅵ. Schließen Sie die Verbindung und geben Sie Ressourcen frei


Das Datenbankverbindungsobjekt wird über DriverManager abgerufen. Bei jedem Abruf müssen Sie eine Verbindung zur Datenbank herstellen und den Benutzernamen und das Kennwort überprüfen.

Benutzer müssen für jede Anfrage einen Link von der Datenbank erhalten, und das Erstellen einer Verbindung zur Datenbank verbraucht normalerweise relativ große Ressourcen und dauert lange.

Jedes Mal, wenn die SQL-Anweisung ausgeführt wird, wird die Verbindung getrennt, was zu einer Verschwendung von Ressourcen führt und die Datenbankverbindungsressourcen nicht ordnungsgemäß wiederverwendet werden.

Unter der Annahme, dass die Website 100.000 Besuche pro Tag erhält, muss der Datenbankserver 100.000 Verbindungen herstellen, was eine enorme Verschwendung von Datenbankressourcen darstellt und leicht zu einem Speicherüberlauf des Datenbankservers und einer Maschinenerweiterung führen kann.

Lösung: Datenbankverbindungspool

Die Grundidee des Datenbankverbindungspools:

Richten Sie einen Pufferpool für die Datenbank ein und stellen Sie im Voraus eine bestimmte Anzahl von Verbindungsobjekten in den Pufferpool.

Wenn Sie eine Datenbankverbindung herstellen möchten, müssen Sie nur ein Objekt aus dem Pufferpool entnehmen.

Legen Sie es nach der Verwendung für die nächste Anforderung wieder in den Pufferpool zurück, um eine Wiederverwendung der Ressourcen zu erreichen, ohne dass eine wiederholte Erstellung erforderlich ist.

Wenn im Datenbankverbindungspool keine inaktiven Verbindungsobjekte vorhanden sind, werden neue Anforderungen in die Warteschlange gestellt und warten darauf, dass andere Threads die Verbindung freigeben.

(2) Verwendung

Der Datenbankverbindungspool von JDBC wird über die Schnittstelle javax.sql.DataSource vervollständigt. DataSource ist die offiziell von Java bereitgestellte Schnittstelle.

Bei der Nutzung müssen Entwickler die Schnittstelle nicht selbst implementieren und können Tools von Drittanbietern nutzen.

C3P0 ist eine häufig verwendete Implementierung von Drittanbietern. In der tatsächlichen Entwicklung kann C3P0 direkt verwendet werden, um den Betrieb des Datenbankverbindungspools abzuschließen.

Schritte zur Nutzung:

① Abhängigkeiten in pom.xml importieren

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

② Code schreiben

  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. }

Beachten:

Verbindung wird über die herkömmliche Methode erhalten: com.mysql.cj.jdbc.ConnectionImpl@3c153a1

Von C3P0 erhaltene Verbindung: com.mchange.v2.c3p0.impl.NewProxyConnection@6156496

Also,Obwohl sie alle die Close-Methode aufrufen, sind die Implementierungsklassen unterschiedlich, sodass auch das Umschreiben der Methode unterschiedlich ist. Dies ist Schnittstellenpolymorphismus.

Die Methode close in C3P0 zerstört die Verbindungsressource nicht direkt, sondern gibt die Verbindung an den Datenbankverbindungspool zurück.


Die obige Methode zum Festlegen von Parametern für den Datenbankverbindungspool wird direkt in das Java-Programm geschrieben.

Dies wird übernommenHardcodeDer Weg,Jedes Mal, wenn Sie die Konfiguration ändernMuss neu kompiliert werden, neue Klassendateien generieren, die Effizienz ist zu gering

In der tatsächlichen Entwicklung werden die Konfigurationsinformationen von C3P0 in einer XML-Datei definiert, und das Java-Programm muss nur die Konfigurationsdatei laden, um den Initialisierungsvorgang des Datenbankverbindungspools abzuschließen.

Anschließend müssen Sie nur noch die Konfiguration ändern und die Konfiguration in XML ändern, ohne sie neu zu kompilieren.

Schritte zur Nutzung:

① Erstellen Sie im Ressourcenverzeichnis eine neue Datei mit dem Namen c3p0-config.xml

② Geben Sie die Konfigurationsinformationen in c3p0-config.xml ein:

  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>

③Schreiben Sie ein Java-Programm

  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. }

Beachten:

① Der Parameter in der Konstruktionsmethode ComboPooledDataSource ist der Namensattributwert des in c3p0-config.xml konfigurierten Tags „named-config“.

② Zu diesem Zeitpunkt kann die JDBC-Toolklasse wie folgt geändert werden:

  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.DBUtils

  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. }

Im obigen Code sind die Vorgänge zum Füllen der Platzhalter mit Daten und zum Extrahieren der Daten aus der Ergebnismenge zu umständlich.

Wenn die Student-Tabelle 100 Attribute enthält, müssen wir 100 Zeilen in die while-Schleife schreiben, um die Daten abzurufen, und möglicherweise sind auch viele Zeilen erforderlich, um die Platzhalter auszufüllen.


Lösung:DBUtils können Entwicklern dabei helfen, die Datenkapselung abzuschließen (Zuordnung von Ergebnismengen zu Java-Objekten).

Schritte zur Nutzung:

① Abhängigkeiten in pom.xml importieren

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

② Code schreiben

  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. }

An diesem Punkt kann das Füllen der Platzhalter und das Herausnehmen der Ergebnismenge mit zwei Codezeilen erfolgen.

Detail:

① Die Abfragemethode muss 4 Parameter übergeben:

Verbindungsobjekt

SQL-Anweisung

Das Implementierungsklassenobjekt der ResultSetHandler-Schnittstelle (der Typ des konvertierten Objekts muss sein: Student.class)

Parameter zum Füllen von Platzhaltern

② Die ResultSetHandler-Schnittstelle wird zur Verarbeitung von Ergebnismengen verwendet. Sie kann die abgefragten Ergebnismengen in Java-Objekte konvertieren.

BeanHandlerOrdnen Sie die Ergebnismenge einem Java-Objekt zu (z. B. einem Student-Objekt).
BeanListHandler Ordnen Sie die Ergebnismenge einer Listensammlung zu (z. B. List<Student > )
Kartenhandler

Ordnen Sie die Ergebnismenge einem Map-Sammlungsobjekt zu

(dh: Karte<String,Object> Schlüssel: Attributname; Wert: Attributwert)

MapListHandler Ordnen Sie die Ergebnismenge einer MapList-Sammlung zu (z. B. List <Map<<String,Object> &gt;)

③ Der Parameter zum Ausfüllen des Platzhalters ist ein variabler Parameter, sodass eine beliebige Anzahl von Parametern übergeben werden kann, um den unterschiedlichen Anforderungen der Benutzer gerecht zu werden.

④ Die konvertierte Objektklasse (Student-Klasse) muss über einen Konstruktor ohne Parameter verfügen, andernfalls meldet das Programm einen Fehler.

Grund:Die unterste Ebene findet diese Klasse über Student.class und dann überReflexionsmechanismusSuchen Sie den parameterlosen Konstruktor dieser Klasse und erstellen Sie sein Objekt.

⑤ Der Name des Attributs in der Klasse muss genau mit dem Namen des Felds in der Datenbanktabelle übereinstimmen.

Denn nachdem das Objekt erstellt wurde, werden beim Zuweisen von Werten zu den Objekteigenschaften basierend auf der Ergebnismenge die Suche und Zuweisung basierend auf dem Namen durchgeführt.

11. Dreistufige MVC-Architektur

Was ist MVC?

  • Modell: Modell (Dienst, Dao, Entität)
  • Ansicht: Ansicht (JSP, HTML, App-Client)
  • Controller: Controller (Servlet, Hander, Aktion)

Nachdem die Anforderung in die JavaWeb-Anwendung gelangt ist, empfängt der Controller die Anforderung, führt eine Geschäftslogikverarbeitung durch und gibt schließlich das Ergebnis an den Benutzer zurück (Ansicht + Modell).

1. Einleitung

In den Anfangsjahren griffen Benutzer beim Betrieb von Webanwendungen direkt auf die Steuerungsschicht zu, und die Steuerungsschicht konnte die Datenbank direkt betreiben:

Servlet--CRUD (Hinzufügen, Löschen, Ändern)--&gt;Datenbank

Im Servlet-Code: Verarbeitung von Anforderungen, Antworten, Ansichtssprüngen, JDBC-Verarbeitung, Geschäftscodeverarbeitung und Logikcodeverarbeitung

Nachteile:Das Programm ist sehr aufgebläht und nicht wartungsfreundlich

Lösung: Es gibt nichts, was das Hinzufügen einer weiteren Ebene nicht lösen kann. Wenn ja, fügen Sie eine weitere Ebene hinzu!

2. Dreistufige MVC-Architektur

Modell

  • Geschäftsverarbeitung: Geschäftslogik (Service)
  • Datenpersistenzschicht: CRUD (DAO-Datenpersistenzobjekt)

Sicht

  • Anzeigedaten
  • Geben Sie einen Link zum Initiieren einer Servlet-Anfrage an (a, form, img...)

Controller (Servlet)

  • Benutzeranforderung empfangen: (Anforderung, Anforderungsparameter, Sitzungsinformationen)
  • Übergeben Sie es an die Business-Schicht, um den entsprechenden Code zu verarbeiten
  • Ansichtssprung steuern

Nehmen Sie als Beispiel die Benutzer- und Administratoranmeldung:

Controller-Ebene:

  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. }

Serviceschicht:

  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/Repository-Ebene:

  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. }