Condivisione della tecnologia

JavaWeb (3: JDBC e MVC)

2024-07-12

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

JavaWeb (1: Conoscenze di base e costruzione dell'ambiente)icona-default.png?t=N7T8Italiano: https://blog.csdn.net/xpy2428507302/article/details/140365130?spm=1001.2014.3001.5501JavaWeb (2: Servlet e Jsp, ascoltatore e filtro)icona-default.png?t=N7T8Italiano: https://blog.csdn.net/xpy2428507302/article/details/140365159?spm=1001.2014.3001.5501

Sommario

10.JDBC

1. Panoramica

2.Scrivere il programma JDBC

3. Analisi dettagliata

(1) URL del database

(2) Classe di connessione

(3) Classe di istruzione

(4) Classe ResultSet

(5) Rilasciare risorse

4.Problema di iniezione SQL

5.Transazioni JDBC

6.Classe strumento JDBC

7. Pool di connessioni al database

(1. Panoramica

(2) Utilizzo

8.DBUtils

11. Architettura a tre livelli MVC

1. Introduzione

2. architettura a tre livelli mvc


10.JDBC

1. Panoramica

JDBC (Java DataBase Connectivity): tecnologia di connessione al database Java

JDBC è un sistema di gestione indipendente da un database specifico e un'interfaccia pubblica per l'accesso e le operazioni del database SQL universale.

Definisce una serie di standard per fornire un modo unificato per accedere a diversi database.

JDBC è un'API applicativa per l'accesso ai database, costituita da un insieme di classi e interfacce scritte in linguaggio Java.

Comprende due livelli:

① API orientata alle applicazioni che i programmatori possono chiamare.

② API orientata al database per consentire ai produttori di sviluppare driver di database.

Per connettere diversi database con JDBC, devi solo caricare diversi pacchetti di driver di database e non devi preoccuparti delle differenze nei linguaggi operativi del database.

2.Scrivere il programma JDBC

① Crea la tabella del database

  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;

② Importa le dipendenze del database

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

③ Utilizzo di JDBC:

Ⅰ Caricare il driver del database (il ponte tra il programma Java e il database)

Ⅱ Ottieni Connection, una connessione tra il programma Java e il database

Ⅲ Creare un oggetto Statement che invia SQL al database, generato da Connection

Ⅳ. Scrivi istruzioni SQL (scrivi SQL diversi a seconda dell'azienda)

Ⅴ Esegui SQL (se desideri ricevere il valore restituito, crea un oggetto ResultSet per salvare i risultati della query dopo l'esecuzione dell'istruzione)

Ⅵ. Chiudere la connessione

  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. Analisi dettagliata

(1) URL del database

jdbc:mysql://localhost:3306/xxx

JDBBC-ITprotocollo
il mio sqlsottoprotocollo
host locale:3306porta ospite
xxxBanca dati

Come scrivere gli indirizzi URL del database di uso comune:

  • Scrittura Oracle: jdbc:oracle:thin:@localhost:1521:xxx
  • Nome del database SqlServer: jdbc:microsoft:sqlserver://localhost:1433; DatabaseName=xxx
  • Metodo di scrittura MySql: jdbc:mysql://localhost:3306/xxx

(2) Classe di connessione

La connessione viene utilizzata per rappresentare i collegamenti al database e la raccolta è l'oggetto più importante nella programmazione del database.

Tutte le interazioni tra il client e il database vengono completate tramite l'oggetto connessione., metodi comuni di questo oggetto:

creaIstruzione()Crea un oggetto istruzione che invia SQL al database
preparareIstruzione(sql)Crea un oggetto PrepareSatement che invia SQL precompilato al database
setAutoCommit(booleano autoCommit)Imposta se le transazioni vengono impegnate automaticamente
commettere()Invia transazione sul collegamento
ripristinare()Transazione di rollback su questo collegamento

(3) Classe di istruzione

L'oggetto Statement viene utilizzato per inviare istruzioni SQL al database. Metodi comuni dell'oggetto Statement:

eseguiQuery(Stringa sql)Utilizzato per inviare istruzioni di query ai dati e restituire un set di risultati ResultSet
eseguiUpdate(Stringa sql)Utilizzato per inviare istruzioni di inserimento, aggiornamento o eliminazione al database e restituire il numero di righe aggiornate nel database
eseguire(Stringa sql)Utilizzato per inviare istruzioni SQL arbitrarie al database
addBatch(Stringa sql)Inserisci più istruzioni SQL in un batch
eseguiBatch()Invia un batch di istruzioni SQL al database per l'esecuzione

Avviso:

Per migliorare l'efficienza operativa, il metodo di esecuzione generalmente non viene utilizzato direttamente.

Usateli invece di conseguenza: eseguireQuery per le query; eseguireUpdate per aggiunte, eliminazioni e modifiche.

(4) Classe ResultSet

ResultSet viene utilizzato per rappresentare i risultati dell'esecuzione delle istruzioni SQL.

Set di risultati Quando si incapsula il set di risultati dell'esecuzione, utilizzaresimile al tavoloIl modo

L'oggetto ResultSet mantiene un cursore che punta alle righe di dati della tabella.Inizialmente, il cursore si trova prima della prima riga

La chiamata al metodo ResultSet.next() può far sì che il cursore punti su una riga di dati specifica e chiamare il metodo getxxx per ottenere i dati della riga.

① Ottieni qualsiasi tipo di dato

getObject(int indice)

Ottieni l'oggetto Object in base al numero di colonne specificato

getObject(stringa nomecolonna)Ottieni l'oggetto Object in base al nome dell'attributo specificato

② Ottieni dati del tipo specificato, come ottenere il tipo String

getString(int indice)

Ottieni un oggetto String in base al numero specificato di colonne

getString(Stringa nomecolonna)Ottieni l'oggetto String in base al nome dell'attributo specificato

③ Come scorrere il set di risultati: 

Prossimo()passare alla riga successiva
Precedente()passare alla riga precedente
assoluto(int riga)Passa alla riga specificata
prima diPrimo()Sposta la parte anteriore di resultSet
dopoUltimo()Passa alla fine del resultSet

(5) Rilasciare risorse

Al termine dell'esecuzione del programma Jdbc, ricordarsi di rilasciare gli oggetti creati dal programma per interagire con il database durante il processo in esecuzione.

Questi oggetti sono solitamente oggetti ResultSet, Statement e Connection.

Soprattutto l'oggetto Connection, è una risorsa molto rara e deve essere rilasciata immediatamente dopo l'uso.

Se la connessione non può essere chiusa tempestivamente e correttamente, può facilmente causare tempi di inattività del sistema.

Per garantire che il codice di rilascio della risorsa possa essere eseguito, è necessario inserire anche il codice di rilascio della risorsa nell'istruzione final.

4.Problema di iniezione SQL

Ci sono due problemi quando si utilizza Statement per lo sviluppo:

① Le stringhe devono essere giuntate frequentemente e il tasso di errore è elevato

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

② Esiste il rischio potenziale di SQL injection

SQL injection: inserimento di istruzioni SQL illegali nei dati immessi dall'utente, utilizzando tecniche intelligenti per unire le stringhe, causando un cortocircuito SQL, rubando così i dati del database.

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

risultato dell'operazione:

Avviso:

La priorità di e nell'istruzione SQL è maggiore di o, quindi l'SQL eseguito equivale a selezionare * dagli utenti dove '1' = '1';


Soluzione:Utilizzare PreparedStatement, una sottoclasse di Statement, che fornisce la funzione di segnaposto SQL.

Non è necessario unire le stringhe e i dati inseriti dall'utente verranno completamente rilevati, rendendo il tutto più sicuro.

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

risultato dell'operazione:

5.Transazioni JDBC

Una transazione si riferisce a un insieme logico di operazioni, tutte riuscite o tutte fallite (principio ACID).

Quando il programma Jdbc ottiene un oggetto Connection dal database, per impostazione predefinita l'oggetto Connection invierà automaticamente una transazione al database.

Se desideri disattivare questo metodo di invio predefinito e consentire l'esecuzione di più SQL in un'unica transazione, puoi utilizzare la seguente istruzione della transazione di controllo JDBC.

Connessione.setAutoCommit(false);

avviare la transazione
Connessione.rollback();Transazione di ripristino
Connessione.commit();impegnare la transazione

① Crea la tabella dei conti

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

② Simulare lo scenario aziendale una volta che il trasferimento avrà esito positivo

  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.Classe strumento JDBC

In diverse richieste, è necessario connettersi al database e rilasciare risorse ogni volta e verrà scritto molto codice ripetuto.

Incapsulare le operazioni di preparazione e rilascio della connessione al database in una classe di strumenti e chiamarla direttamente quando la si utilizza per evitare di scrivere codice ripetuto.

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

Chiama il caso, ad esempio: aggiungi utente

  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. Pool di connessioni al database

(1. Panoramica

Processo di sviluppo JDBC:

Ⅰ Caricare il driver del database (deve essere caricato solo una volta)

Ⅱ Stabilire la connessione al database (Connessione)

Ⅲ Creare un oggetto Statement che invia SQL al database, generato da Connection

Ⅳ Scrivere istruzioni SQL

Ⅴ Esegui SQL (Query--&gt;L'oggettoResultSet riceve il set di risultati)

Ⅵ Chiudi la connessione e rilascia le risorse


L'oggetto di connessione al database viene ottenuto tramite DriverManager Ogni volta che viene ottenuto, è necessario rivolgersi al database per ottenere una connessione e verificare il nome utente e la password.

Gli utenti devono ottenere un collegamento dal database per ogni richiesta e la creazione di una connessione al database solitamente consuma risorse relativamente grandi e richiede molto tempo.

Ogni volta che viene eseguita l'istruzione SQL, la connessione viene interrotta, il che causerà uno spreco di risorse e le risorse di connessione al database non verranno riutilizzate correttamente.

Supponendo che il sito Web riceva 100.000 visite al giorno, il server del database deve creare 100.000 connessioni, il che rappresenta un enorme spreco di risorse del database e può facilmente causare un overflow della memoria del server del database e l'espansione della macchina.

Soluzione: pool di connessioni al database

L'idea di base del pool di connessioni al database:

Stabilire un pool buffer per il database e inserire in anticipo un certo numero di oggetti di connessione nel pool buffer.

Quando si desidera ottenere una connessione al database, è sufficiente prendere un oggetto dal pool di buffer.

Dopo l'uso, reinserirlo nel buffer pool per la richiesta successiva, ottenendo il riutilizzo delle risorse senza la necessità di ripetute creazioni.

Quando non sono presenti oggetti di connessione inattivi nel pool di connessioni del database, le nuove richieste entreranno nella coda di attesa e attenderanno che altri thread rilascino la connessione.

(2) Utilizzo

Il pool di connessioni al database di JDBC viene completato utilizzando l'interfaccia javax.sql.DataSource è l'interfaccia fornita ufficialmente da Java.

Quando lo utilizzano, gli sviluppatori non hanno bisogno di implementare l'interfaccia da soli e possono utilizzare strumenti di terze parti.

C3P0 è un'implementazione di terze parti comunemente utilizzata Nello sviluppo effettivo, C3P0 può essere utilizzato direttamente per completare l'operazione del pool di connessioni al database.

Passaggi per l'utilizzo:

① Importa le dipendenze in pom.xml

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

② Scrivi il codice

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

Avviso:

Connessione ottenuta tramite metodo tradizionale: com.mysql.cj.jdbc.ConnectionImpl@3c153a1

Connessione ottenuta da C3P0: com.mchange.v2.c3p0.impl.NewProxyConnection@6156496

COSÌ,Sebbene tutti chiamino il metodo close, le classi di implementazione sono diverse, quindi anche la riscrittura del metodo è diversa. Si tratta di polimorfismo dell'interfaccia.

Il metodo di chiusura in C3P0 non distrugge direttamente la risorsa di connessione, ma restituisce la connessione al pool di connessioni del database.


Il metodo sopra descritto per impostare i parametri per il pool di connessioni al database viene scritto direttamente nel programma Java.

Questo è adottatohardcodeIl modo,Ogni volta che cambi la configurazioneÈ necessario ricompilare, genera nuovi file di classe, l'efficienza è troppo bassa

Nello sviluppo effettivo, le informazioni di configurazione di C3P0 sono definite in un file xml e il programma Java deve solo caricare il file di configurazione per completare l'operazione di inizializzazione del pool di connessioni al database.

Successivamente basterà modificare la configurazione e modificare la configurazione in xml senza ricompilare.

Passaggi per l'utilizzo:

① Nella directory delle risorse, crea un nuovo file denominato c3p0-config.xml

② Compila le informazioni di configurazione in 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>

③Scrivere un programma 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. }

Avviso:

① Il parametro nel metodo di costruzione ComboPooledDataSource è il valore dell'attributo name del tag Named-config configurato in c3p0-config.xml.

② A questo punto, la classe dello strumento JDBC può essere modificata come:

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

Nel codice precedente, le operazioni di riempimento dei segnaposto con i dati e di estrazione dei dati dal set di risultati sono troppo macchinose.

Se ci sono 100 attributi nella tabella Studente, allora dovremo scrivere 100 righe nel ciclo while per recuperare i dati e potrebbero essere necessarie anche molte righe per riempire i segnaposto.


Soluzione:DBUtils può aiutare gli sviluppatori a completare l'incapsulamento dei dati (mappatura dei set di risultati su oggetti Java).

Passaggi per l'utilizzo:

① Importa le dipendenze in pom.xml

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

② Scrivi il codice

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

A questo punto, riempire i segnaposto ed estrarre il set di risultati può essere eseguito con due righe di codice.

dettaglio:

① Il metodo di query deve passare 4 parametri:

Oggetto di connessione

Istruzione SQL

L'oggetto della classe di implementazione dell'interfaccia ResultSetHandler (il tipo dell'oggetto convertito deve essere: Student.class)

Parametri per popolare i segnaposto

② L'interfaccia ResultSetHandler viene utilizzata per elaborare i set di risultati. Può convertire i set di risultati interrogati in oggetti Java. Vengono fornite le seguenti quattro classi di implementazione.

Gestore di fagioliMappare il set di risultati in un oggetto Java (come l'oggetto Student)
GestoreElencoBean Mappare il set di risultati in una raccolta List (come: List<Student > )
Gestore di mappe

Mappare il set di risultati in un oggetto di raccolta Map

(es: mappa<String,Object> chiave: nome attributo; valore: valore attributo)

GestoreMapList Mappare il set di risultati in una raccolta MapList (ad esempio: List <Map<<String,Object> &gt;)

③ Il parametro per compilare il segnaposto è un parametro variabile, quindi è possibile passare qualsiasi numero di parametri per soddisfare le diverse esigenze degli utenti.

④ La classe oggetto convertita (classe Studente) deve avere un costruttore senza parametri, altrimenti il ​​programma segnalerà un errore.

motivo:Il livello inferiore trova questa classe tramite Student.class e poi tramitemeccanismo di riflessioneTrova il costruttore senza parametri di questa classe e crea il suo oggetto.

⑤ Il nome dell'attributo nella classe deve essere esattamente uguale al nome del campo nella tabella del database.

Perché dopo la creazione dell'oggetto, quando si assegnano valori alle proprietà dell'oggetto in base al set di risultati, la ricerca e l'assegnazione vengono eseguite in base al nome.

11. Architettura a tre livelli MVC

Cos'è MVC?

  • Modello: modello (servizio, dao, entità)
  • Visualizza: Visualizza (jsp, html, client dell'app)
  • Controller: controller (servlet, hander, azione)

Dopo che la richiesta è entrata nell'applicazione JavaWeb, il Controller riceve la richiesta, esegue l'elaborazione della logica aziendale e infine restituisce il risultato all'utente (Visualizzazione + Modello).

1. Introduzione

Nei primi anni, quando si utilizzavano le applicazioni web, gli utenti accedevano direttamente al livello di controllo e il livello di controllo poteva gestire direttamente il database:

servlet--CRUD (aggiungi, elimina, modifica)--&gt;database

Nel codice servlet: elaborazione di richieste, risposte, salti di visualizzazione, elaborazione JDBC, elaborazione del codice aziendale ed elaborazione del codice logico

Svantaggi:Il programma è molto gonfio e non favorisce la manutenzione

soluzione: Non c'è niente che l'aggiunta di un altro livello non possa risolvere, se c'è, aggiungi un altro livello!

2. architettura a tre livelli mvc

Modello

  • Elaborazione aziendale: logica aziendale (Servizio)
  • Livello di persistenza dei dati: CRUD (oggetto di persistenza dei dati DAO)

visualizzazione

  • mostra dati
  • Fornire un collegamento per avviare una richiesta Servlet (a, form, img...)

Controllore (servlet)

  • Ricevi la richiesta dell'utente: (req, parametri di richiesta, informazioni sulla sessione)
  • Passarlo al livello aziendale per elaborare il codice corrispondente
  • Controlla il salto della vista

Prendiamo come esempio l'accesso dell'utente e dell'amministratore:

Livello di controllo:

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

Livello di servizio:

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

Livello Dao/Repository:

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