7.JDBC编程

本章目标

  • 简介
  • 数据库连接常用为和接口
  • JDBC访问数据库

本章内容

一、简介

1、概念

JDBCjava DataBase Connectivity的缩写。它是Sun的Javasoft公司制定的Java数据库连接技术,是一套标准接口。

在Java.sql包中提供了JDBC API,开发人员可以通过它连接到各种数据库系统,编写访问数据库的程序。

JDBC API不能直接访问数据库,它依赖于数据库厂商提供的JDBC Driver(JDBC驱动程序)。

使用Java和JDBC开发的程序可以跨数据库平台运行,受数据库限制很少

2、JDBC编程涉及的三个部分:

  • 应用程序:开发人员编写的客户端程序。在应用程序中调用JDBC API,将SQL语句发送到数据库并检索结果。
  • 驱动程序管理器:JDBC将驱动程序管理器负责使用正确的JDBC驱动程序和连接信息访问数据库,在Java应用程序和数据库系统之间建立连接。
  • 驱动程序:驱动程序由数据库厂商提供,实现数据库驱动接口,能够把SQL指令正确的发送的数据库服务器。

3、JDBC至数据库的通信路径

二、Java.SQL包

Java.SQL包中常用的接口和类包括:

  • Driver接口和DriverManager类
  • Connection
  • Statement或者PreparedStatement
  • ResultSet

1、Driver接口和DriverManager类

所有的JDBC驱动程序都必须实现Driver接口,JDBC驱动程序由数据库厂商提供,所以必须把JDBC驱动程序加入到项目中。

DriverManager用来建立和数据库的连接以及管理JDBC驱动程序。

DriverManager的常用方法

方法 描述
registerDriver(Driver driver) 在DriverManager中注册JDBC驱动程序
getConnection(String url, String user, String password) 建立到给定数据库 URL 的连接,返回Connection对象
setLoginTimeout(int seconds) 设定等待数据库连接的最长时间
setLogWriter(PrintWriter out) 设定输出数据库日志的PrintWriter对象

2、Connection

Connection代表java程序和数据库的连接。在连接上下文中执行 SQL 语句并返回结果

Connection的常用方法如下:

方法 说明
createStatement() 创建并返回Statement对象
preparedStatement(String sql) 创建并返回PreparedStatement对象

3、Statement

Statement接口定义了一组数据库操作的方法,可以通过这个接口的实现类对象执行指定的SQL命令

Statement 对象的方法:

方法 说明 示例
executeUpdate() 用来创建和更新表 stmt.executeUpdate(query);
executeQuery() 对于SELECT 语句 stmt.executeQuery(query);
execute() 返回布尔值,用于执行任何 SQL 语句 stmt.execute();

4、PreparedStatement

PreparedStatement表示预编译的 SQL 语句的对象,是Statement的子接口,扩展了Statement的功能

  • PreparedStatement接口使我们可以使用占位符(?)作为参数定义一条SQL语句。
  • 占位符是在SQL语句中出现的标记符,在SQL语句执行之前会被替换成实际的值。
  • 通过调用connection.preparedStatement()方法来生成PreparedStatement对象

设置查询参数:

setAsciiStream() setBigDecimal() setBinaryStream() setBoolean()
setByte() setBytes() setDate() setDouble()
setFloat() setInt() setLong() setNull()
setObject() setShort setString() setTime()
setTimestamp() setUnicodeStream()

格 式:setXXXX(位置参数,值参数)

位置参数:用于指定所指的是哪一个占位符;

值参数: 指定给该占位符的值。

占位符号从左向右,从1开始。

5、ResultSet

ResultSet表示select语句查询得到的结果集。

  • ResultSet中记录的行号从1开始,一个Statement对象在同一时刻只能打开一个ResultSet对象。
  • 调用ResultSet的next()方法,可以使游标定位到下一条记录。
  • 调用ResultSet的getXXX()方法,可以取得某个字段的值。

ResultSet的常用方法:

目录 描述
getString(int index) 返回指定字段的String类型的值,参数index代表字段的序号。
getString(String indexName) 返回指定字段的String类型的值,参数indexName代表字段的名字。
getInt(int index) 返回指定字段的int类型的值,参数index代表字段的序号。
getInt(String indexName) 返回指定字段的int类型的值,参数indexName代表字段的名字。
getFloat(int index) 返回指定字段的float类型的值,参数index代表字段的序号。
getFloat(String indexName) 返回指定字段的float类型的值,参数indexName代表字段的名字。

6、SQL到Java的数据类型

从一个JDBC数据源检索数据的时候,ResultSet实现了把SQL数据类型映射到Java数据类型上

SQL数据类型 Java数据类型
CHAR String
VARCHAR String
LONGVARCHAR String
NUMERIC java.math.BigDecimal
DECIMAL java.math.BigDecimal
BIT boolean
TINYINT byte
SMALLINT short
INTEGER int
BIGINT long
REAL float
FLOAT double
DOUBLE double
BINARY byte[]
VARBINARY byte[]
LONGVARBINARY byte[]
DATE java.sql.Date
TIME java.sql.Time
TIMESTAMP java.sql.Timestamp

三、JDBC访问数据库

1、步骤一

pom.xml文件

<dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>8.0.29</version>
    </dependency>

装载并注册数据库的JDBC驱动程序:

数据库 驱动程序
MySQL Driver Class.forName(“com.mysql.jdbc.Driver”);
Oracle Driver Class.forName(“oracle.jdbc.driver.OracleDriver”);
SQL Server Driver Class.forName(“com.microsoft.sqlserver.jdbc.SQLServerDriver”)

注:Class.forName()这步操作可以省略

Applications no longer need to explicitly load JDBC drivers using Class.forName(). Existing programs which currently load JDBC drivers using Class.forName() will continue to work without modification.

2、步骤二

  • 建立与数据库的连接:

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

其中url表示连接数据库的JDBC URL,user和pwd表示连接数据库的用户名和口令。

  • JDBC的连接由数据库 URL标识 :

    jdbc::

Subprotocol是有效JDBC驱动程序名称,subname通常是指映射到物理数据库的逻辑名或别名。

  • MySQL的url

    jdbc:mysql://localhost:3306/mydb

3、JDBC URL格式

数据库名 驱动程序 URL
MySQL5.7 com.mysql.jdbc.Driver jdbc:mysql://[<:3306>]/
MySQL8 com.mysql.cj.jdbc.Driver jdbc:mysql://[<:3306>]/?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shangha
MS SQL Server2005 com.microsoft.sqlserver.jdbc.SQLServerDriver jdbc:sqlserver://[ip]:[port];DatabaseName=[Database];user=[user];password=[pw]
Oracle thin Driver oracle.jdbc.driver.OracleDriver jdbc:oracle:thin:@[ip]:[port]:[sid]

注意:由于数据库厂商提供的数据库驱动不同,因此url也不同,使用时请查阅相关资料

mysql的 url参数详解

参数名 含义 通常取值
user 数据库用户名(用于连接数据库) —–
passWord 用户密码(用于连接数据库) —–
useSSL SSL(Secure Sockets Layer),安全套接字协议。 MySQL的版本为5.7以上时,必须加上useSSL=false,直接通过用户账号和密码进行连接;MySQL的版本是5.7以下不进行要求,默认使用useSSL=false useSSL=false:使用用户账号密码进行连接。 useSSL=true:y一般通过证书或者令牌进行安全验证。 false
useUnicode 是否使用Unicode字符集,如果参数characterEncoding设置为gb2312或gbk,本参数值必须设置为true true
characterEncoding 当useUnicode设置为true时,指定字符编码。比如可设置为gb2312或gbk utf8
serverTimezone 用于设置时区时间 Asia/Shanghai
autoReconnect 当数据库连接异常中断时,是否自动重新连接? —–
autoReconnectForPools 是否使用针对数据库连接池的重连策略 —–
maxReconnects autoReconnect设置为true时,重试连接的次数 —–
failOverReadOnly 自动重连成功后,连接是否设置为只读? —–
zeroDateTimeBehavior exception:默认值,即抛出SQL state [S1009]. Cannot convert value…的异常 convertToNull:将日期转换成NULL值;round:替换成最近的日期即0001-01-01 convertToNull

格式一:

jdbc:mysql://localhost:3306/hrms?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai

格式二:

jdbc:mysql://localhost:3306/hrms?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true

二选一:不要混,否则会出现Public Key Retrieval is not all

4、步骤三

创建Statement对象,准备调用sql语句:

Statement stm = conn.createStatement();

或创建PreparedStatement对象

PreparedStatement pstm = conn.preparedStatement(sql);

5、步骤四

执行SQL语句,获取结果集(select操作):Statement对象:

ResultSet rs = stm.executeQuery(sql);

PreparedStatement对象:

ResultSet rs = pstm.executeQuery();

6、步骤五

访问ResultSet中的记录集

While(rs.next()){
    String col1 = rs.getString(1);
    String col2 = rs.getString(2);
    int col3 = rs.getInt(3);
        …
}

7、步骤六

依次关闭ResultSet、Statement/PrearedStatement和Connection对象:

finally {
             try{
                 if(rs != null) rs.close();
                 if(stmt != null) stmt.close();
                 if(con != null) con.close();
             }catch(SQLException e){
                 System.out.println(e.toString());
             }
         }

8、完整示例

/*存放数据的List*/
List depList = new ArrayList();
Connection conn = null;
try{
    //加载驱动程序
    Class.forName("com.mysql.cj.jdbc.Driver");
    //指定连接数据库的URL
    String strUrl = "jdbc:mysql://localhost:3306/hrms?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai";
    //进行数据库连接
    conn=DriverManager.getConnection(strUrl,"root","root");
    //创建一个statement对象
    Statement stm = conn.createStatement();
    //定义sql语句
    String strSql = "select * from Dept";
    //定义一个结果集
    ResultSet rs = stm.executeQuery(strSql);
    while(rs.next)  {
        //创建部门对象
        Dep clsDep=new Dep();
        //将从结果集中取出的数据存放到部门对象指定的属性中
        clsDep.setDepId(rs.getString(1));
        clsDep.setDepName(rs.getString(2));
        clsDep.setDepDesc(rs.getString(3));
        //将部门对象放入List中
        depList.add(clsDep);
    }
}
finally{
    conn.close();
}


扩展JDBC中Statement 和 PrepareStatement 的区别

作用:数据库连接的时候创建一个对象,然后通过对象去调用executeQuery方法来执行sql语句

创建的时候:

Statement statement = conn.createStatement();

PreparedStatement preStatement = conn.prepareStatement(sql);

执行的时候:

ResultSet rSet = statement.executeQuery(sql);

ResultSet pSet = preStatement.executeQuery();

PrepareStatement继承自Statement:

效率:

Statement 执行每一条SQL语句时,都是编译+执行,相当于静态SQL;

PrepareStatement执行的SQL语句,会进行预编译,SQL中可以包含动态参数”?“,在执行时可以为”?“动态设置参数值,当下次执行相同类型的SQL语句时,可以解析并直接执行编译好的SQL语句,从而减少编译次数提高数据库的性能,相当于动态SQL;

安全性:

Sql注入的问题

Statement:SQL语句每次都是重新编译执行的,这就导致你传进去的参数和SQL语句是有联系,可以改变原先SQL的功能结构

select * from user where username= '"+name+"' and userpwd='"+passwd+"'

如果传进去的passwd参数值是 ’ or ‘1’ = ’1 那SQL就变成了永真式:

select * from user where username= '"+name+"' and userpwd=' or 1 = 1'

权限就这样被轻易获取了;

PrepareStatement:SQL的参数会被强制类型转换成表中的列属性值,由于SQL语句已经实现被编译过,参数和原来SQL语句不会有交集,也就避免了这种情况的发送;

思维导图

image

posted @ 2025-04-10 11:48  icui4cu  阅读(37)  评论(0)    收藏  举报