Partage de technologie

JavaWeb (3 : JDBC et MVC)

2024-07-12

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

JavaWeb (1 : Connaissances de base et construction de l'environnement)icône-par-défaut.png?t=N7T8https://blog.csdn.net/xpy2428507302/article/details/140365130?spm=1001.2014.3001.5501JavaWeb (2 : Servlet et Jsp, écouteur et filtre)icône-par-défaut.png?t=N7T8https://blog.csdn.net/xpy2428507302/article/details/140365159?spm=1001.2014.3001.5501

Table des matières

10.JDBC

1. Vue d'ensemble

2.Écrire le programme JDBC

3. Analyse détaillée

(1) URL de la base de données

(2) Classe de connexion

(3) Classe de déclaration

(4) Classe ResultSet

(5) Libérer des ressources

4.Problème d'injection SQL

5.Transactions JDBC

6.Classe d'outils JDBC

7. Pool de connexions à la base de données

(1. Vue d'ensemble

(2) Utilisation

8.DBUtils

11. Architecture MVC à trois niveaux

1. Introduction

2. Architecture mvc à trois niveaux


10.JDBC

1. Vue d'ensemble

JDBC (Java DataBase Connectivity) : technologie de connexion à une base de données Java

JDBC est un système de gestion indépendant d'une base de données spécifique et une interface publique pour l'accès et les opérations universels aux bases de données SQL.

Définit un ensemble de normes pour fournir un moyen unifié d'accéder à différentes bases de données.

JDBC est une API d'application pour l'accès aux bases de données, composée d'un ensemble de classes et d'interfaces écrites en langage Java.

Comprend deux niveaux :

① API orientée application que les programmeurs peuvent appeler.

② API orientée base de données permettant aux fabricants de développer des pilotes de base de données.

Pour connecter différentes bases de données avec JDBC, il vous suffit de charger différents packages de pilotes de base de données et vous n'avez pas à vous soucier des différences entre les langages de fonctionnement des bases de données.

2.Écrire le programme JDBC

① Créer une table de base de données

  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;

② Importer les dépendances de la base de données

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

③ Utilisation de JDBC :

Ⅰ. Chargez le pilote de base de données (le pont entre le programme Java et la base de données)

Ⅱ. Obtenir une connexion, une connexion entre le programme Java et la base de données

Ⅲ Créez un objet Statement qui envoie du SQL à la base de données, généré par Connection.

Ⅳ Écrire des instructions SQL (écrire différents SQL selon l'entreprise)

Ⅴ. Exécutez SQL (si vous souhaitez recevoir la valeur de retour, créez un objet ResultSet pour enregistrer les résultats de la requête après l'exécution de l'instruction)

Ⅵ. Fermez la connexion

  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. Analyse détaillée

(1) URL de la base de données

jdbc:mysql://localhost:3306/xxx

jdbcprotocole
mysqlsous-protocole
localhost:3306port hôte
xxxbase de données

Comment écrire les adresses URL de bases de données couramment utilisées :

  • Écriture Oracle : jdbc:oracle:thin :@localhost:1521:xxx
  • Nom de la base de données SQL Server : jdbc : microsoft : sqlserver : ://localhost : 1433 ; DatabaseName = xxx
  • Méthode d'écriture MySql : jdbc:mysql://localhost:3306/xxx

(2) Classe de connexion

La connexion est utilisée pour représenter les liens de base de données et la collection est l'objet le plus important dans la programmation de base de données.

Toutes les interactions entre le client et la base de données sont effectuées via l'objet de connexion., méthodes courantes de cet objet :

createStatement()Créer un objet d'instruction qui envoie SQL à la base de données
préparerStatement(sql)Créez un objet PrepareSationment qui envoie du SQL précompilé à la base de données
setAutoCommit(booléen autoCommit)Définir si les transactions sont automatiquement validées
commettre()Soumettre la transaction sur le lien
retour en arriere()Transaction d'annulation sur ce lien

(3) Classe de déclaration

L'objet Statement est utilisé pour envoyer des instructions SQL à la base de données Méthodes courantes de l'objet Statement :

exécuterQuery(Chaîne sql)Utilisé pour envoyer des instructions de requête aux données et renvoyer un jeu de résultats ResultSet
executeUpdate(Chaîne sql)Utilisé pour envoyer des instructions d'insertion, de mise à jour ou de suppression à la base de données et renvoyer le nombre de lignes mises à jour dans la base de données
exécuter(Chaîne sql)Utilisé pour envoyer des instructions SQL arbitraires à la base de données
addBatch(Chaîne sql)Mettez plusieurs instructions SQL dans un lot
exécuterBatch()Envoyer un lot d'instructions SQL à la base de données pour exécution

Avis:

Pour améliorer l'efficacité opérationnelle, la méthode d'exécution n'est généralement pas utilisée directement.

Utilisez-les plutôt en conséquence : executeQuery pour les requêtes ;executeUpdate pour les ajouts, les suppressions et les modifications.

(4) Classe ResultSet

ResultSet est utilisé pour représenter les résultats d'exécution des instructions SQL.

Ensemble de résultats Lors de l'encapsulation du jeu de résultats d'exécution, utilisezsemblable au tableauLe chemin

L'objet ResultSet maintient un curseur pointant vers les lignes de données du tableau.Initialement, le curseur est avant la première ligne

L'appel de la méthode ResultSet.next() peut faire pointer le curseur sur une ligne de données spécifique et appeler la méthode getxxx pour obtenir les données de la ligne.

① Obtenez tout type de données

getObject(int index)

Récupère l'objet Object en fonction du nombre de colonnes spécifié

getObject(chaîne nom_colonne)Récupère l'objet Object en fonction du nom d'attribut spécifié

② Obtenir des données du type spécifié, comme obtenir le type String

getString(int index)

Obtenez un objet String en fonction du nombre de colonnes spécifié

getString(Chaîne nom_colonne)Récupère l'objet String en fonction du nom d'attribut spécifié

③ Comment faire défiler l'ensemble de résultats : 

suivant()passer à la ligne suivante
Précédent()passer à la ligne précédente
absolu(int ligne)Déplacer vers la ligne spécifiée
avantPremier()Déplacer le devant de resultSet
aprèsDernier()Aller à la fin de resultSet

(5) Libérer des ressources

Une fois l'exécution du programme Jdbc terminée, n'oubliez pas de libérer les objets créés par le programme pour interagir avec la base de données pendant le processus en cours.

Ces objets sont généralement des objets ResultSet, Statement et Connection.

Surtout l'objet Connection, c'est une ressource très rare et doit être libérée immédiatement après utilisation.

Si la connexion ne peut pas être fermée rapidement et correctement, cela peut facilement entraîner un temps d'arrêt du système.

Pour garantir que le code de version de ressource peut s'exécuter, le code de version de ressource doit également être placé dans l'instructionfinal.

4.Problème d'injection SQL

Il y a deux problèmes lors de l'utilisation de Statement pour le développement :

① Les chaînes doivent être épissées fréquemment et le taux d'erreur est élevé

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

② Il existe un risque potentiel d'injection SQL

Injection SQL : injection d'instructions SQL illégales dans les données saisies par l'utilisateur, en utilisant des techniques intelligentes pour épisser les chaînes, provoquant un court-circuit SQL, volant ainsi les données de la base de données.

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

résultat de l'opération :

Avis:

La priorité de et dans l'instruction SQL est supérieure à ou, donc le SQL exécuté équivaut à sélectionner * parmi les utilisateurs où '1' = '1' ;


Solution:Utilisez PreparedStatement, une sous-classe de Statement, qui fournit la fonction d'espace réservé SQL.

Il n'est pas nécessaire d'épisser les chaînes et les données saisies par l'utilisateur seront entièrement détectées, ce qui les rendra plus sûres.

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

résultat de l'opération :

5.Transactions JDBC

Une transaction fait référence à un ensemble logique d’opérations, toutes réussies ou toutes infructueuses (principe ACID).

Lorsque le programme Jdbc obtient un objet Connection de la base de données, par défaut, l'objet Connection soumettra automatiquement une transaction à la base de données.

Si vous souhaitez désactiver cette méthode de soumission par défaut et autoriser l'exécution de plusieurs SQL en une seule transaction, vous pouvez utiliser l'instruction de transaction de contrôle JDBC suivante.

Connexion.setAutoCommit(false);

commencer la transaction
Connexion.rollback();Transaction d'annulation
Connexion.commit();valider une transaction

① Créer un tableau de comptes

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

② Simulez le scénario commercial lorsque le transfert est réussi

  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 d'outils JDBC

Dans différentes requêtes, vous devez vous connecter à la base de données et libérer des ressources à chaque fois, et beaucoup de code répété sera écrit.

Encapsulez les opérations de préparation et de libération de la connexion à la base de données dans une classe d'outils et appelez-la directement lorsque vous l'utilisez pour éviter d'écrire du code répété.

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

Cas d'appel, tel que : ajouter un utilisateur

  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 de connexions à la base de données

(1. Vue d'ensemble

Processus de développement JDBC :

Ⅰ. Chargez le pilote de base de données (ne doit être chargé qu'une seule fois)

Ⅱ. Établir une connexion à la base de données (Connexion)

Ⅲ Créez un objet Statement qui envoie du SQL à la base de données, généré par Connection.

Ⅳ. Écrire des instructions SQL

Ⅴ. Exécuter SQL (l'objet Query--&gt;ResultSet reçoit le jeu de résultats)

Ⅵ. Fermez la connexion et libérez les ressources.


L'objet de connexion à la base de données est obtenu via DriverManager. Chaque fois qu'il est obtenu, vous devez vous adresser à la base de données pour obtenir une connexion et vérifier le nom d'utilisateur et le mot de passe.

Les utilisateurs doivent obtenir un lien de la base de données pour chaque requête, et la création d'une connexion à la base de données consomme généralement des ressources relativement importantes et prend beaucoup de temps à créer.

Chaque fois que l'instruction SQL est exécutée, la connexion est déconnectée, ce qui entraînera un gaspillage de ressources et les ressources de connexion à la base de données ne seront pas bien réutilisées.

En supposant que le site Web reçoive 100 000 visites par jour, le serveur de base de données doit créer 100 000 connexions, ce qui représente un énorme gaspillage de ressources de base de données et peut facilement provoquer un débordement de mémoire du serveur de base de données et une expansion de la machine.

Solution : Pool de connexions à la base de données

L'idée de base du pool de connexions à la base de données :

Établissez un pool de mémoire tampon pour la base de données et placez à l'avance un certain nombre d'objets de connexion dans le pool de mémoire tampon.

Lorsque vous souhaitez obtenir une connexion à une base de données, il vous suffit de récupérer un objet du pool de mémoire tampon.

Après utilisation, remettez-le dans le pool de mémoire tampon pour la prochaine requête, obtenant ainsi une réutilisation des ressources sans avoir besoin d'une création répétée.

Lorsqu'il n'y a aucun objet de connexion inactif dans le pool de connexions à la base de données, les nouvelles requêtes entreront dans la file d'attente et attendront que d'autres threads libèrent la connexion.

(2) Utilisation

Le pool de connexions à la base de données de JDBC est complété à l'aide de l'interface javax.sql.DataSource est l'interface officiellement fournie par Java.

Lorsqu'ils l'utilisent, les développeurs n'ont pas besoin d'implémenter eux-mêmes l'interface et peuvent utiliser des outils tiers.

C3P0 est une implémentation tierce couramment utilisée. Dans le développement réel, C3P0 peut être utilisé directement pour terminer le fonctionnement du pool de connexions à la base de données.

Étapes d'utilisation :

① Importer les dépendances dans pom.xml

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

② Écrire le code

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

Avis:

Connexion obtenue par méthode traditionnelle : com.mysql.cj.jdbc.ConnectionImpl@3c153a1

Connexion obtenue par C3P0 : com.mchange.v2.c3p0.impl.NewProxyConnection@6156496

donc,Bien qu'ils appellent tous la méthode close, les classes d'implémentation sont différentes, donc la réécriture de la méthode est également différente. Il s'agit du polymorphisme d'interface.

La méthode close dans C3P0 ne détruit pas directement la ressource de connexion, mais renvoie la connexion au pool de connexions de base de données.


La méthode ci-dessus de définition des paramètres du pool de connexions à la base de données est écrite directement dans le programme Java.

Ceci est adoptécoder en durLe chemin,Chaque fois que vous modifiez la configurationIl faut recompiler, génère de nouveaux fichiers de classe, l'efficacité est trop faible

Dans le développement réel, les informations de configuration de C3P0 sont définies dans un fichier XML et le programme Java n'a besoin que de charger le fichier de configuration pour terminer l'opération d'initialisation du pool de connexions à la base de données.

Par la suite, il vous suffit de modifier la configuration et de modifier la configuration en XML sans recompiler.

Étapes d'utilisation :

① Dans le répertoire des ressources, créez un nouveau fichier nommé c3p0-config.xml

② Remplissez les informations de configuration dans 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>

③Écrire un programme 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. }

Avis:

① Le paramètre dans la méthode de construction ComboPooledDataSource est la valeur de l'attribut name de la balise nommée-config configurée dans c3p0-config.xml.

② À l'heure actuelle, la classe d'outils JDBC peut être modifiée comme suit :

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

Dans le code ci-dessus, les opérations de remplissage des espaces réservés avec des données et d'extraction des données de l'ensemble de résultats sont trop lourdes.

S'il y a 100 attributs dans la table Student, nous devrons alors écrire 100 lignes dans la boucle while pour récupérer les données, et cela peut également prendre plusieurs lignes pour remplir les espaces réservés.


Solution:DBUtils peut aider les développeurs à réaliser l'encapsulation des données (mappage des jeux de résultats vers des objets Java).

Étapes d'utilisation :

① Importer les dépendances dans pom.xml

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

② Écrire le code

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

À ce stade, le remplissage des espaces réservés et la suppression du jeu de résultats peuvent être effectués avec deux lignes de code.

détail:

① La méthode de requête doit transmettre 4 paramètres :

Objet de connexion

Instruction SQL

L'objet de classe d'implémentation de l'interface ResultSetHandler (le type de l'objet converti doit être : Student.class)

Paramètres pour remplir les espaces réservés

② L'interface ResultSetHandler est utilisée pour traiter les ensembles de résultats. Elle peut convertir les ensembles de résultats interrogés en objets Java. Les quatre classes d'implémentation suivantes sont fournies.

Gestionnaire de haricotsMappez le jeu de résultats dans un objet Java (tel qu'un objet Student)
Gestionnaire de liste de haricots Mappez l'ensemble de résultats dans une collection List (telle que : List<Student > )
Gestionnaire de cartes

Mappez le jeu de résultats dans un objet de collection Map

(c'est-à-dire : carte<String,Object> clé : nom de l'attribut ; valeur : valeur de l'attribut)

Gestionnaire de liste de cartes Mappez l'ensemble de résultats dans une collection MapList (c'est-à-dire : List <Map<<String,Object> &gt;)

③ Le paramètre à remplir dans l'espace réservé est un paramètre variable, donc n'importe quel nombre de paramètres peut être transmis pour répondre aux différents besoins des utilisateurs.

④ La classe d'objet convertie (classe Student) doit avoir un constructeur sans paramètre, sinon le programme signalera une erreur.

raison:La couche inférieure trouve cette classe via Student.class, puis viamécanisme de réflexionRecherchez le constructeur sans paramètre de cette classe et créez son objet.

⑤ Le nom de l'attribut dans la classe doit être exactement le même que le nom du champ dans la table de la base de données.

Parce qu'après la création de l'objet, lors de l'attribution de valeurs aux propriétés de l'objet en fonction du jeu de résultats, la recherche et l'affectation sont effectuées en fonction du nom.

11. Architecture MVC à trois niveaux

Qu’est-ce que MVC ?

  • Modèle : modèle (service, dao, entité)
  • Vue : Vue (jsp, html, client d'application)
  • Contrôleur : Contrôleur (Servlet, Hander, Action)

Une fois la demande entrée dans l'application JavaWeb, le contrôleur reçoit la demande, effectue le traitement de la logique métier et renvoie enfin le résultat à l'utilisateur (Vue + Modèle).

1. Introduction

Dans les premières années, lors de l'exploitation d'applications Web, les utilisateurs accédaient directement à la couche de contrôle, et la couche de contrôle pouvait directement exploiter la base de données :

servlet--CRUD (ajouter, supprimer, modifier)--&gt;base de données

Dans le code du servlet : traitement des requêtes, des réponses, des sauts de vue, traitement JDBC, traitement du code métier et traitement du code logique

Désavantages:Le programme est très volumineux et peu propice à la maintenance

solution: Il n'y a rien que l'ajout d'un autre calque ne puisse résoudre, si c'est le cas, ajoutez un autre calque !

2. Architecture mvc à trois niveaux

Modèle

  • Traitement métier : logique métier (Service)
  • Couche de persistance des données : CRUD (objet de persistance des données DAO)

voir

  • afficher les données
  • Fournir un lien pour lancer une requête Servlet (a, form, img...)

Contrôleur (Servlet)

  • Recevoir la demande de l'utilisateur : (requête, paramètres de demande, informations de session)
  • Remettez-le à la couche métier pour traiter le code correspondant
  • Contrôler le saut de vue

Prenons l'exemple de la connexion utilisateur et administrateur :

Couche contrôleur :

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

Couche de services :

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

Couche Dao/Référentiel :

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