JDBC知识点详解

一:JDBC 概述

    一、简介

       1. JDBC(Java DataBase Connection,Java 数据库连接)Java语言中用来规范客户端程序如何来访问数据库的应用程序接口,提供了诸如查询和更新数据库中数据的方法。

       2. JDBC 是一个标准 SQL(Structured Query Language,结构化查询语言)数据库访问接口,可以为多种关系数据库提供统一访问。也提供一种基准,据此可以构建更高级的工具和接口。

       3. JDK(Java Development Kit,Java 开发工具包)软件捆绑包括 JDBC 和 JDBC-ODBC(Open DataBase Connection,开放式数据库连接)桥。

          

    二、API

     

二:JDBC 开发步骤

    一、配置依赖 jar 包

       1. 下载

           1. MySQL

              

           2. Oracle

              

       2. 配置

           1. 导入 jar 包

              

           2. Maven 配置

 1 <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
 2 <dependency>
 3     <groupId>mysql</groupId>
 4     <artifactId>mysql-connector-java</artifactId>
 5     <version>8.0.20</version>
 6 </dependency>
 7 
 8 <!-- https://mvnrepository.com/artifact/com.oracle.database.jdbc/ojdbc10 -->
 9 <dependency>
10     <groupId>com.oracle.database.jdbc</groupId>
11     <artifactId>ojdbc10</artifactId>
12     <version>19.3.0.0</version>
13 </dependency>

    二、注册驱动

 1 public class DriverTest {
 2     /**
 3      * 通过反射机制,加载数据库驱动,类初始化的时候执行静态代码块
 4      *   优点1:此方式由于参数为字符串,因此很容易修改,移植性强。
 5      *   优点2:不依赖特定的Driver库,很容易改造成从配置文件读取JDBC配置,从而可以在运行时动态更换数据库连接驱动。
 6      */
 7     static {
 8         try {
 9             /**
10              * MySQL:8.0版本,5.0版本为:com.mysql.jdbc.Driver
11              */
12             Class.forName("com.mysql.cj.jdbc.Driver");
13             
14             /**
15              * Oracle
16              */
17             Class.forName("oracle.jdbc.driver.OracleDriver");
18         } catch (ClassNotFoundException e) {
19             // TODO Auto-generated catch block
20             e.printStackTrace();
21         } // 8版本
22     }
23 }

    三、获取连接

 1 public class ConnectionTest {
 2     /**
 3      * MySQL
 4      */
 5     public Connection getMysqlConnection() {
 6         // 本地连接URL:jdbc:mysql:///student?useUnicode=true&characterEnocding=utf-8&useSSL=false&serverTimezone=UTC
 7         // 远程连接URL:jdbc:mysql://<host>:<port>/<database>?useUnicode=true&characterEnocding=utf-8&useSSL=false&serverTimezone=UTC
 8         final String url = "jdbc:mysql:///student?useUnicode=true&characterEnocding=utf-8&useSSL=false&serverTimezone=UTC";
 9         // 连接用户名
10         final String user = "root";
11         // 连接密码
12         final String password = "000000";
13         Connection conn = null;
14         if (conn == null) {
15             try {
16                 conn = DriverManager.getConnection(url, user, password);
17             } catch (SQLException e) {
18                 // TODO Auto-generated catch block
19                 System.err.println("Oracle数据库连接出错");
20                 e.printStackTrace();
21             }
22         }
23         return conn;
24     }
25 
26     /**
27      * Oracle
28      */
29     public Connection getOracleConnection() {
30         /**
31          * 1. 使用thin连接:jdbc:oracle:thin:@<host>:<port>:<database>
32          *    优点:thin完全由Java代码编写,与平台无关,不需要Oracle客户端。
33          *    缺点:thin性能一般,达不到如OCI方式的企业级的要求,一般适合一台主机连接。
34          * 
35          * 2. 使用oci连接:jdbc:oracle:oci:@<host>:<port>:<database>
36          *    优点:用OCI连接数据库是企业级的做法,适应于单个数据库和集群数据库,性能优越,尤其是连接池功能大大提高了应用程序的性能和并发量。
37          *    缺点:若想使用OCI必须要安装Oracle客户端。
38          */
39         final String url = "jdbc:oracle:thin:@127.0.0.1:1521:orcl";
40         // 连接用户名
41         final String user = "scott";
42         // 连接密码
43         final String password = "tiger";
44         // 连接对象
45         Connection conn = null;
46         if (conn == null) {
47             try {
48                 conn = DriverManager.getConnection(url, user, password);
49             } catch (SQLException e) {
50                 // TODO Auto-generated catch block
51                 System.err.println("Oracle数据库连接出错");
52                 e.printStackTrace();
53             }
54         }
55         return conn;
56     }
57 }

    四、创建Statement、PreparedStatement、CallableStatement接口,执行SQL语句

 1 package pers.mj.test;
 2 
 3 import java.sql.CallableStatement;
 4 import java.sql.Connection;
 5 import java.sql.PreparedStatement;
 6 import java.sql.ResultSet;
 7 import java.sql.SQLException;
 8 import java.sql.Statement;
 9 import java.sql.Types;
10 
11 public class MysqJDBClTest {
12     /**
13      * Statement 的作用:用于执行静态 SQL 语句并返回它所生成结果的对象,完成对数据库的增删改查。
14      * Statement 的优点:语法简单,对于只执行一次的 SQL 语句,使用 Statement 比 PreparedStatement 对象的开销更小。
15      * Statement 的缺点:每次执行时相似SQL都会进行编译  ,采用硬编码效率低,安全性较差,字符串拼接方式的 SQL 语句是非常繁琐的,中间有很多的单引号和双引号的混用,极易出错。
16      * Statement 的适用场景:普通的不带参的查询SQL 
17      */
18     public void testStatement() {
19         try {
20             // 获取数据库连接
21             Connection conn = DBUtil.getMysqlConnection();
22             // 创建 Statement 对象
23             Statement st = conn.createStatement();
24             // 定义SQL语句
25             String sql = "insert into student(stu_id, stu_name) values(20200626, " + " '张三')"; // 字符串拼接麻烦
26             /**
27              * 演示SQL注入问题:如果此时传递给字段的值为:or 1 = 1,那么不论如何条件都成立,导致结果都成功,这就是SQL注入
28              */
29             String SQL = "select * from student where stu_name="+"'张三' and stu_id= "+"'or 1 = 1' ";
30             // 执行SQL语句
31             if (st.execute(sql)) {
32                 System.out.println("信息插入成功");
33             }
34         } catch (SQLException e) {
35             System.err.println("插入语句执行失败");
36             e.printStackTrace();
37         }
38     }
39     
40     /**
41      * PreparedStatement 的作用:用于执行动态 SQL 语句并返回它所生成结果的对象,完成对数据库的增删改查。继承Statement
42      * PreparedStatement 的优点:相似SQL只编译一次,减少编译次数,代码的可读性和可维护性更高,提高了安全性(阻止了SQL注入)。
43      * PreparedStatement 的缺点:执行非相似SQL语句时,速度较慢。
44      * PreparedStatement 的适用场景:支持可变参数的SQL 
45      */
46     public void testPreparedStatement() {
47         try {
48             // 获取数据库连接
49             Connection conn = DBUtil.getMysqlConnection();
50             // 定义SQL语句
51             String sql = "insert into student(stu_id, stu_name) values(?, ?)"; 
52             /**
53              * 解决SQL注入问题:PreparedStatement不是将参数简单拼凑成sql,而是做了一些预处理,将参数转换为string,两端加单引号,将参数内的一些特殊字符(换行,单双引号,斜杠等)做转义处理,这样就很大限度的避免了sql注入。
54              */
55             String SQL = "select * from student where stu_name=? and stu_id= ? ";
56             // 预编译SQL语句,防止SQL注入
57             PreparedStatement ps = conn.prepareStatement(sql);
58             // 给占位符(?)赋值:索引从1开始,数据类型需要相对应
59             ps.setInt(1, 20180627);
60             ps.setString(2, "李四");
61             // 执行SQL语句
62             if (ps.execute()) {
63                 System.out.println("信息插入成功");
64             }
65         } catch (SQLException e) {
66             System.err.println("插入语句执行失败");
67             e.printStackTrace();
68         }
69     }
70     
71     /**
72      * CallableStatement 的作用:实现了存储过程函数调用的方法以及对于输出的处理。继承PreparedStatement
73      * CallableStatement 的使用场景:支持调用存储过程,提供了对输出和输入/输出参数(INOUT)的支持
74      */
75     public void testCallableStatement(){
76         try {
77             // 获取数据库连接
78             Connection conn = DBUtil.getMysqlConnection();
79             // 定义SQL语句
80             String sql = "call p3(?,?)";
81             // 预编译SQL
82             CallableStatement cs = conn.prepareCall(sql);
83             // 给站位符赋值
84             cs.setString(1, "王五"); 
85             cs.registerOutParameter(2, Types.INTEGER);  // 注册一个输入参数
86             // 执行SQL
87             cs.execute();
88             // 获取结果集对象
89             ResultSet resultSet = cs.getResultSet();
90             while (resultSet.next()) {
91                 System.out.println(resultSet.getString("name")+" ");                
92             }
93             // 获取输出参数
94             System.out.println(cs.getInt(2));
95         } catch (Exception e) {
96             // TODO: handle exception
97         }
98     }
99 }

    五、execute、executeQuery和executeUpdate详解

  1 public class ExecuteTest {
  2     // 数据库连接对象
  3     Connection conn = null;
  4     // 预处理对象
  5     PreparedStatement ps = null;
  6     // 结果集对象
  7     ResultSet rs = null;
  8     
  9     /**
 10      * executeQuery()
 11      *   作用:只能执行DQL(SELECT语句),是使用最多的查询语句。
 12      *   返回值:单个结果及对象(ResultSet)
 13      */
 14     public void testExecuteQuery() {
 15         try {
 16             // 获取数据库连接
 17             conn = DBUtil.getMysqlConnection();
 18             // 定义SQL语句
 19             String sql = "SELECT stu_id,stu_name FROM student WHERE stu_id=? ";
 20             // 预编译SQL
 21             ps = conn.prepareStatement(sql);
 22             // 给占位符赋值
 23             ps.setInt(1, 20200626);
 24             // 执行SQL语句
 25             rs = ps.executeQuery();
 26             while (rs.next()) {
 27                 System.out.println("学号:" + rs.getInt("stu_id") + " 姓名:" + rs.getString("stu_name"));
 28             }
 29         } catch (Exception e) {
 30             // TODO: handle exception
 31         }
 32     }
 33 
 34     /**
 35      * executeUpdate()
 36      *   作用:执行DML(除去SELECT语句)和DDL,修改表中零行或多行中的一列或多列。
 37      *   返回值:受影响的行数(整数)
 38      */
 39     @Test
 40     public void testExecuteUpdate() {
 41         try {
 42             // 获取数据库连接
 43             conn = DBUtil.getMysqlConnection();
 44             // 定义SQL语句
 45             String sql = "UPDATE student SET stu_name=? WHERE stu_id=?";
 46             // 预编译SQL
 47             ps = conn.prepareStatement(sql);
 48             // 给占位符赋值
 49             ps.setString(1, "王五");
 50             ps.setInt(2, 20200626);
 51             // 执行SQL语句
 52             if (ps.executeUpdate() != 0) {
 53                 System.out.println("信息修改成功");
 54             }
 55         } catch (Exception e) {
 56             // TODO: handle exception
 57         }
 58     }
 59 
 60     /**
 61      * execute()
 62      *   作用:用于执行返回多个结果集、多个更新计数或二者组合的语句。例如:执行某个已存储过程或动态执行未知 SQL 字符串。
 63      *   返回值:多个ResultSet对象、多个更新计数或ResultSet对象与更新计数。
 64      *   使用
 65      *    多个结果集
 66      *      1. 执行完execute()方法后,使用CallableStatement对象调用getResultSet()方法获取第一个结果集,调用适当的getXXX方法获取值
 67      *      2. 如果存在第二个结果集,则必须调用getMoreResults()方法,然后再调用getResultSet()方法来获取结果集,依次类推。
 68      *    多个更新计数
 69      *      1. 执行完execute()方法后,则首先调用方法 getUpdateCount,然后调用 getMoreResults,并再次调用 getUpdateCount,依次类推。
 70      *   
 71      */
 72     public void testExecute() {
 73         List<List<Map<String, Object>>> resultList = new ArrayList<>();
 74         try {
 75             // 获取数据库连接
 76             conn = DBUtil.getMysqlConnection();
 77             // 定义SQL语句
 78             String sql = "sp_help 'test.student'";
 79             // 预编译SQL
 80             CallableStatement cs = conn.prepareCall(sql);
 81              // 外循环获取结果集的个数
 82             boolean oprFlg  = cs.execute(sql);
 83             while (oprFlg) {
 84                 List<Map<String, Object>> result = new ArrayList<>();
 85                 // 获取第一个结果集
 86                 rs = cs.getResultSet();
 87                 // 内循环获取每个结果集的记录
 88                 while (rs.next()) {
 89                     ResultSetMetaData rsmd = rs.getMetaData();
 90                     int columnCount = rsmd.getColumnCount();
 91                     Map<String, Object> map = new HashMap<String, Object>();
 92                     for (int i = 0; i < columnCount; i++) {
 93                         map.put(rsmd.getColumnName(i + 1).toLowerCase(), rs.getObject(i + 1));
 94                     }
 95                     result.add(map);
 96                 }
 97                 resultList.add(result);
 98                 // 获取更多的结果集
 99                 oprFlg = cs.getMoreResults();
100             }
101         } catch (Exception e) {
102             // TODO: handle exception
103         }
104     }

    六、处理结果集

      1. ResultSet:在线

 1 public class ResultSetTest {
 2     // 数据库连接对象
 3     Connection conn = null;
 4     // 预处理对象
 5     PreparedStatement ps = null;
 6     // 结果集对象
 7     ResultSet rs = null;
 8 
 9     /**
10      * 可滚动,可更新
11      *    ResultSet.TYPE_FORWARD_ONLY:该常量控制ResultSet记录指针只能向前移动(默认)。
12      *    ResultSet.TYPE_SCROLL_INSENSITIVE:该常量控制ResultSet记录指针可以自由移动(可滚动结果集),但底层数据的改变不会受ResultSet的内容。
13      *    ResultSet.TYPE_SCROLL_SENSITIVE:该常量控制ResultSet记录指针可以自由移动(可滚动结果集),并且底层数据的改变会影响ResultSet的内容。
14      *    ResultSet.CONCUR_READ_ONLY:该常量指示ResultSet是只读的并发模式(默认)。
15      *    ResultSet.CONCUR_UPDATABLE: 该常量指示ResultSet是可更新的并发默认。
16      *   
17      */
18     public void testScrollAndUpdate() {
19         try {
20             // 获取数据库连接
21             conn = DBUtil.getMysqlConnection();
22             // 定义SQL语句
23             String sql = "SELECT stu_id,stu_name FROM student";
24             // 预编译SQL
25             ps = conn.prepareStatement(sql, ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
26             // 执行SQL语句
27             rs = ps.executeQuery();
28             // 处理结果集
29             while (rs.next()) {
30                 System.out.println("学号:" + rs.getInt("stu_id") + " 姓名:" + rs.getString("stu_name"));
31             }
32         } catch (Exception e) {
33             // TODO: handle exception
34         }
35     }
36     
37     /**
38      * ResultSetMetaData:分析结果集,当不清楚该ResultSet里包含哪些数据列,以及每个数据列的数据类型,那么可以通过ResultSetMetaData来获取关于ResultSet的描述信息。
39      *   getMetaData():获取ResultSetMetaData对象。
40      *   int getColumnCount():返回该ResultSet的列数量。
41      *   String getColumnName(int columnIndex):返回对应列的名称。
42      *   int getColumnType(int columnIdex):返回对应列的数据类型。
43      */
44     public void testResultSetMetaData() {
45         try {
46             // 获取数据库连接
47             conn = DBUtil.getMysqlConnection();
48             // 定义SQL语句
49             String sql = "SELECT stu_id,stu_name FROM student";
50             // 预编译SQL
51             ps = conn.prepareStatement(sql);
52             // 执行SQL语句
53             rs = ps.executeQuery();
54             // 处理结果集
55             while (rs.next()) {
56                 // 获取结果集元数据对象
57                 ResultSetMetaData rsmd = rs.getMetaData();
58                 // 获取结果集列数
59                 int columnCount = rsmd.getColumnCount();
60                 for (int i = 0; i < columnCount; i++) {
61                     // 获取结果集对应列名称
62                     System.out.println(rsmd.getColumnName(i + 1));
63                     // 获取结果集对应数据类型
64                     System.out.println(rsmd.getColumnType(i + 1));
65                 }
66             }
67         } catch (Exception e) {
68             // TODO: handle exception
69         }
70     }
71 }

      2. RowSet:离线

  1 public class RowSetTest {
  2     // 数据库连接对象
  3     Connection conn = null;
  4     // 预处理对象
  5     PreparedStatement ps = null;
  6     // 结果集对象
  7     ResultSet rs = null;
  8 
  9     /**
 10      * RowSet:实现了ResultSet接口,并且有子接口CachedRowSet(离线查询)
 11      *   概念:离线查询:在本地搞一个结果集的副本,关闭结果集、数据库连接,使用本地的这个副本。
 12      *   作用:RowSet默认是可滚动,可更新,可序列化的结果集,并且作为对JavaBean使用,因此能方便地在网络上传输,用于同步两端的数据。
 13      *   优点:程序在创建RowSet时已把底层数据读取到了内存中,因此可以充分利用计算机的内存,从而减低数据库的负载,提高程序的性能。
 14      */
 15     @Test
 16     public void testRowSetOffline() {
 17         try {
 18             // 获取数据库连接
 19             conn = DBUtil.getMysqlConnection();
 20             // 定义SQL语句
 21             String sql = "SELECT stu_id,stu_name FROM student";
 22             // 预编译SQL
 23             ps = conn.prepareStatement(sql);
 24             // 执行SQL语句
 25             rs = ps.executeQuery();
 26             /**
 27              * 离线查询
 28              */
 29             // 通过RowSetProvider的静态方法创建RowSetFactory对象
 30             RowSetFactory rsf = RowSetProvider.newFactory();
 31             // 创建CachedRowSet对象
 32             CachedRowSet crs = rsf.createCachedRowSet(); 
 33             // 使用结果集填充CachedRowSet
 34             crs.populate(rs); //  使用给定的ResultSet装填RowSet,从ResultSet的第startRow条记录开始装填。
 35             /**
 36              * 关闭数据库资源
 37              */
 38             rs.close();
 39             ps.close();
 40             conn.close();
 41             // 离线处理结果集:CachedRowSet是ResultSet的孙接口,使用的方法都相同。
 42             while (crs.next()) {
 43                 System.out.println("学号:" + crs.getInt("stu_id") + " 姓名:" + crs.getString("stu_name"));
 44             }
 45         } catch (Exception e) {
 46             // TODO: handle exception
 47         }
 48     }
 49     
 50     /**
 51      * 分页:不推荐使用每个数据库特有的分页,追求跨数据库,代码可用性更高
 52      *   1. 使用游标实现
 53      *   2. 使用离线查询实现
 54      */
 55     public void pagination() {
 56         try {
 57             /**
 58              * 使用游标实现
 59              */
 60             // 获取数据库连接
 61             conn = DBUtil.getMysqlConnection();
 62             // 定义SQL语句
 63             String sql = "SELECT stu_id,stu_name FROM student";
 64             // 预编译SQL
 65             ps = conn.prepareStatement(sql, ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
 66             // 执行SQL语句
 67             rs = ps.executeQuery();
 68             // 定义分页
 69             int start = 0;  // 起始页
 70             int pageSize = 10;  // 分页大小
 71             // 定义游标
 72             rs.absolute(start);  // 游标指向起始位
 73             while (rs.next()) {
 74                 // ......
 75                 if (rs.getRow() == pageSize) { // getRow()是获取当前记录是结果集中的第几条记录,第一条就是1。也可以设置计数器来判断
 76                     break;
 77                 }
 78             }
 79             
 80             /**
 81              * 使用离线查询实现
 82              */
 83             // 创建RowSetFactory对象
 84             RowSetFactory rsf = RowSetProvider.newFactory();
 85             // 创建CachedRowSet对象
 86             CachedRowSet crs = rsf.createCachedRowSet(); 
 87             // 设置分页大小
 88             crs.setPageSize(10);
 89             // 使用结果集填充CachedRowSet
 90             crs.populate(rs);
 91             // 释放资源
 92             rs.close();
 93             ps.close();
 94             conn.close();
 95             while (crs.next()) {
 96                 // ......
 97             }
 98         } catch (Exception e) {
 99             // TODO: handle exception
100         }
101     }
102 }

   

    七、释放资源

 1 public class CloseTest {
 2     // 数据库连接对象
 3     Connection conn = null;
 4     // 预处理对象
 5     PreparedStatement ps = null;
 6     // 结果集对象
 7     ResultSet rs = null;
 8 
 9     /**
10      * 手动释放
11      */
12     public void handMovement() {
13         try {
14             // 获取数据库连接
15             conn = DBUtil.getMysqlConnection();
16             // 定义SQL语句
17             String sql = "SELECT stu_id,stu_name FROM student ";
18             // 预编译SQL
19             ps = conn.prepareStatement(sql);
20             // 执行SQL语句
21             rs = ps.executeQuery();
22             // 处理结果集
23             while (rs.next()) {
24                 System.out.println("学号:" + rs.getInt("stu_id") + " 姓名:" + rs.getString("stu_name"));
25             }
26         } catch (Exception e) {
27             // TODO: handle exception
28         } finally {
29             if (rs != null) {
30                 try {
31                     rs.close();
32                 } catch (SQLException e) {
33                     // TODO Auto-generated catch block
34                     e.printStackTrace();
35                 }
36             }
37             if (ps != null) {
38                 try {
39                     ps.close();
40                 } catch (SQLException e) {
41                     // TODO Auto-generated catch block
42                     e.printStackTrace();
43                 }
44             }
45             if (conn != null) {
46                 try {
47                     conn.close();
48                 } catch (SQLException e) {
49                     // TODO Auto-generated catch block
50                     e.printStackTrace();
51                 }
52             }
53         }
54     }
55 
56     /**
57      * 自动释放
58      */
59     public void autoregula() {
60         try(
61             // 获取数据库连接
62             conn = DBUtil.getMysqlConnection();
63             // 定义SQL语句
64             String sql = "SELECT stu_id,stu_name FROM student ";
65             // 预编译SQL
66             ps = conn.prepareStatement(sql);
67             // 执行SQL语句
68             rs = ps.executeQuery();
69             // 处理结果集
70             while (rs.next()) {
71                 System.out.println("学号:" + rs.getInt("stu_id") + " 姓名:" + rs.getString("stu_name"));
72             }    
73           ){
74         }catch (Exception e) {
75             // TODO: handle exception
76         }
77     }
78 }

三:封装

猜你喜欢

转载自www.cnblogs.com/mh20131118/p/13176479.html
今日推荐