• 首页
  • About
  • Note
  • 联系
一键查岗
2021年1月4日 作者:Qu

JDBC部分知识梳理

JDBC部分知识梳理
2021年1月4日 作者:Qu

首先JDBC是SUN公司写好的一套接口(interface)

Java程序员面向接口编写程序,是这个接口的实现者.由于每个数据库管理系统(DBMS)底层的实现原理不同,每个数据库厂家是这套接口的调用者,所以每个厂家面向写这套接口的实现,也就是驱动

驱动: 所有数据库驱动都是以jar包的形式存在的jar包中有很多.class文件,就是对JDBC的实现

在开发前应在官网下载DBMS对应的驱动,配置环境变量……

JDBC编程分为六部:

第一步:注册驱动(告诉java程序,即将要连接的是哪个品牌的数据库)

1.DriverManager.registerDriver(new com.mysql.jdbc.Driver());

分析: DriverManager (Java Platform SE 8 ) 是一个类 java.sql.DriverManager

JDK介绍为: 用于管理一组JDBC驱动程序的基本服务。 方法: registerDriver(Driver driver) 静态方法 返回值:void 注册给定的驱动程序,要传入的的这个Driver 是一个驱动程序

来康康Driver这个东西:

Driver (Java Platform SE 8 ) public interface Driver 是一个接口啊

public interface Driver 每个驱动程序类必须实现的接口。 Java SQL框架允许多个数据库驱动程序。 每个驱动程序都应该提供一个实现Driver接口的类。 DriverManager将尝试加载尽可能多的驱动程序,然后对于任何给定的连接请求,它会依次要求每个驱动程序尝试连接到目标URL。 强烈建议每个Driver类应该是小型且独立的,以便可以加载和查询Driver类,而不需要大量的支持代码。 当加载一个Driver类时,它应该创建一个自己的实例,并用DriverManager注册它。 这意味着用户可以通过调用以下方式加载和注册驱动程序:

1
Class.forName("foo.bah.Driver")
所以在mysql中我们传入的Driver是: Driver driver=new com.mysql.jdbc.Driver(); 接着我们将这个Driver传入DriverManager.registerDriver()方法中: DriverManager.regidterDriver(driver); 两行代码合在一起: DriverManager.registerDriver(new_com.mysql.jdbc.Driver());

2.Class.forName(“com.mysql.jdbc.Driver”); 通过 Class.forName( “foo.bah.Driver” ); 类加载,触发静态代码块中的注册驱动代码(驱动中已经写好了注册代码,触发就会执行)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.mysql.jdbc;

import java.sql.DriverManager;
import java.sql.SQLException;

public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    public Driver() throws SQLException {
    }
//静态代码块
    static {
        try {
            DriverManager.registerDriver(new Driver());
        } catch (SQLException var1) {
            throw new RuntimeException("Can't register driver!");
        }
    }
}

第二步:”获取连接(表示JVM的进程和数据库进程之间的通道打开(这数据进程之间的通信,是重量级的,使用完之后要关闭通道))

Connection conn= DriverManager.getConnection(String url,String user,String password);

Interface Connection Collection是一个接口! API介绍:与特定数据库的连接(会话)。 执行SQL语句并在连接的上下文中返回结果。 Connection对象的数据库能够提供描述其表,其支持的SQL语法,其存储过程,此连接的功能等的信息。 该信息是用getMetaData方法获得的。 注意:配置Connection时,JDBC应用程序应使用适当的Connection方法,例如setAutoCommit或setTransactionIsolation 。 当有JDBC方法可用时,应用程序不应直接调用SQL命令来更改连接的配置。 默认情况下, Connection对象处于自动提交模式,这意味着它在执行每个语句后自动提交更改。 如果自动提交模式已禁用,必须显式调用方法commit才能提交更改; 否则数据库更改将不会被保存。

怎么获得Connection对象:调用DriverManager的静态方法:getConnection(String url,String user,String password); 方法返回值:Connection (你说巧不巧)

conn=DriverManager.getConnection(“jdbc:mysql://localhost:3306/studydatabase”,”studydatabase”,”password”);值得注意的是,参数一般采用反射机制的方式

1
2
3
4
5
6
ResourceBundle boundle=ResourceBundle.getBundle("TestJDBC/src/setting/jdbc");
配置文件:
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://192.168.1.12:3306/studydatabase
user=root
password=qu200011

第三步:获取数据库操作对象(专门执行sql语句的对象)第四步:执行sql语句(DQL,DML….)

首先是不需要处理查询结果集的:

怎么获得Statement对象呢?—–Connection对象.createStatement()–方法返回值:Statement:创建一个 Statement对象,用于将SQL语句发送到数据库。

1
Statment stmt=conn.createStatement();

看看这个玩意:

Interface Statement 是一个接口 public interface Statement extends Wrapper, AutoCloseable— 数据库操作对象;

用于执行静态SQL语句并返回其生成的结果的对象。 默认情况下,每个Statement对象只能有一个ResultSet对象同时打开。 因此,如果一个ResultSet对象的读取与另一个对象的读取交错,则ResultSet对象必须由不同的Statement对象生成。 在所有执行方法Statement接口隐式关闭当前ResultSet声明的对象,如果一个开放的存在。

方法1:executeUpdate(String sql) 返回值:int
执行给定的SQL语句,这可能是 INSERT , UPDATE ,或 DELETE语句,或者不返回任何内容,如SQL DDL语句的SQL语句。

1
2
3
String sql="delete from DEPT where DEPTNO=50";
            //返回修改的行数
            int count=stmt.executeUpdate(sql);

方法2:

executeQuery(String sql) 返回值:ResultSet
执行给定的SQL语句,该语句返回单个 ResultSet对象。

1
2
String sql="select empno,ename,sal from EMP";
            ResultSet rs=stmt.executeQuery(sql);

1
Statement stmt=conn.createStatement();

第五步:处理查询结果集(只有第四部是select语句的时候,才有这第五步的处理查询结果集)

如上,使用了Statement对象.executeQuery(sql) 方法,返回值为:ResultSet rs

public interface ResultSet 是一个接口
extends Wrapper, AutoCloseable表示数据库结果集的数据表,通常通过执行查询数据库的语句生成。
ResultSet对象保持一个光标指向其当前的数据行。 最初,光标位于第一行之前。 next方法将光标移动到下一行,并且由于在ResultSet对象中没有更多行时返回false ,因此可以在while循环中使用循环来遍历结果集。 默认的ResultSet对象不可更新,并且只有一个向前移动的光标。 因此,您只能从第一行到最后一行迭代一次。 可以生成可滚动和/或可更新的ResultSet对象。 以下代码片段(其中con是有效的Connection对象)说明了如何使可滚动且对其他人更新不敏感的结果集,这是可更新的。 有关其他选项,请参阅ResultSet字段。

光标默认在第一个结果的前面……总的来说,可以通过while循环来遍历结果集

1
2
3
4
5
6
7
8
9
10
String sql="select * from EMP ORDER BY SAL "+order+"";
            rs=stmt.executeQuery(sql);
            while(rs.next()){
                String name=rs.getString("ENAME");
                String jobname=rs.getString("JOB");
                //如果想接收double类型的返回值:
                //(同样还有getInt)
                Double sal=rs.getDouble("sal");
                System.out.println(name+"->"+jobname+"工资:"+sal);
            }

第六步:释放资源

释放资源没别的,为了保证资源一定释放,将其放入finally语句块关闭资源,并且要遵循从小到大依次关闭 ,分别对其try…catch ,一定要分开,要保证其中一个产生异常,其它的资源能正常释放

从小到大依次关闭: ResultSet对象.close()—> Statement对象.close()—> Connection对象.close()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public static void close(Connection conn, Statement stmt, ResultSet rs)  {
        if(rs !=null){
            try {
                rs.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
        if(stmt !=null){
            try {
                stmt.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
        if(conn !=null){
            try {
                conn.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
    }

解决SQL注入问题:

SQL注入的原因:

用户输入的信息中含有sql语句的关键字,并且这些关键字参与sql语句的编译过程,导致sql语句原意被扭曲,进而达到sql注入
解决SQL注入问题:只要用户提供的信息不参与SQL语句的编译过程,问题就解决了.即使用户提供的信息中含有SQL语句的关键字,但是没有参与编译,不起作用.要想用户信息不参与sql语句的编译,那么必须使用java.sql.PreparedStatement是属于预编译的数据库操作对象PreparedStatement接口继承 java.sql.PreparedStatementPreparedStatement是属于预编译数据库操作对象PreparedStatement的原理是:预先对SQL语句框架进行编译,然后再给SQL语句传”值”

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
            //获取连接
            conn= DriverManager.getConnection(url,user,password);
            System.out.println(conn);
            //获取预编译的数据库操作对象
            String sql="select * from t_user where loginName=? and password=?";  //SQL语句框架
            //一个问号代表一个占位符,一个?将来接收一个"值",注意,占位符不能用单引号括起来
            // 程序执行到此处,会发送sql语句框子给DBMS,然后DBMS进行sql语句预先编译
            ps=conn.prepareStatement(sql);
            //给占位符?传值(第一个?下标是1,第二个?下标是2,JDBC中所有下标从1开始)
            ps.setString(1,userLoginInfo.get("userName"));
//此时,如果您的登录名和登录密码中含有sql语句的关键字,但这些语句并没有参加sql语句的编译
//所以自然不会影响sql语句的原意
            ps.setString(2,userLoginInfo.get("password"));
            //执行sql语句
            rs=ps.executeQuery();

解决sql注入的关键:

Statement和PrepareStaetment:–Statement存在sql注入问题

PrepareStatement解决了sql注入问题–在sql中,相同的select语句,第二次执行就不用进行编译,直接执行,Statement因为每次传值不同,所以执行的sql语句也不同,所以每一次都需要编译.但是PrepareStatement每次预编译的语句都是相同的,所以编译一次执行n次,所以PrepareStatement执行效率略高一些–PrepareStatement会在编译阶段做安全检查.如果传入的传入的参数类型不符合,会在编译阶段就报错,所以PrepareStatement更加安全—-综上所述,PropareStatement使用较多.只有极少数情况下使用Statement只有:当业务方面必须要求支持sql注入的时候才会使用Statement,Statement支持sql注入,凡是业务要求是需要进行sql语句拼接的,必须使用Statement.例如:商城中的价格升序降序排列要使用sql注入,就要使用Statement

事务机制

在JDBC中事务是自动提交的JDBC中只要执行任意一条DML语句,则自动提交一次.这是JDBC默认的事务行为但是在实际的业务当中,通常是N条DML语句共同联合才能完成的,必须保证这些DML语句在同一个事务中同时成功或者同时失败
Connection类下的setAutoCommit方法:setAutoCommit(boolean autoCommit) 返回值:void此俩进阶的自动提交模式设置为给定状态,如果连接处于自动提交模式下,则它的所欲偶SQL语句将执行并作为单个事务提交.否则,它的SQL语句将聚集到事务中,调用Commit方法或者rollback方法为止,默认状态下,新连接处于自动提交模式
使用方法:

将自动提交机制改为手动提交—-Connection对象引用.setAutoCommit(false);

程序正常运行完没有异常,事务结束,手动提交事务—-Connection对象引用.commit();

为了保证程序的安全性,在程序发生异常的时候进行事务的回滚所以应该在catch语句块中进行事务的回滚

1
2
3
4
5
6
7
8
9
10
11
catch (SQLException throwables) {
            //回滚
            if(conn !=null){
                try {
                    conn.rollback();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            throwables.printStackTrace();
        }

参考行级锁机制:

JDBC的行级锁机制

上一篇JDBC的行级锁机制下一篇 JS中代码的执行顺序问题

声明:

本网站用于个人学习与交流!

开放时间:24小时开放

 

本站备案号为:陕ICP备20001727号-5