JDBC操作数据库

一.JDBC概述

1.JDBC简介

JDBC(Java DataBase Connectivity)即java数据库连接是Java连接不同数据库的类库,它有三个核心功能:1.连接数据库,2.向数据库发送SQL语句,3.操作SQL语句的返回结果

2.JDBC概念

JDBC是SUN公司提供一套访问数据库的规范(就是一组接口),并提供连接数据库的协议标准,各个数据库厂商遵循SUN的规范提供一套访问自己公司的数据库服务器的API。SUN提供的规范命名为JDBC,而各个厂商提供的,遵循了JDBC规范的可以访问不同型号的数据库的API被称之为数据库驱动

二.JDBC核心类与接口

2.1 DriverManager类

DriverManger是java.sql包下的一个类,它的作用是:

1) 注册驱动:

使用DriverManager类中的,让JDBC知道要使用的是哪个驱动
DriverManager类中registerDriver方法的源码:

//一个静态同步方法,参数是一个实现了java.sql.Driver接口的类(不同的数据库驱动有java.sql.Driver接口的不同实现,所以JDBC可以操作不同的数据库)
public static synchronized void registerDriver(java.sql.Driver driver) throws SQLException {
        if(driver != null) {
            registeredDrivers.addIfAbsent(new DriverInfo(driver));
        } else {
            throw new NullPointerException();
        }
        println("registerDriver: " + driver);

    }

MySQL驱动包中实现了java.sql.Driver接口的类com.mysql.jdbc.Driver

public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    public Driver() throws SQLException {
    }
    //静态代码块,加载此类后即执行此代码块中的代码
    static {
        try {
            //调用DriverManager类的registerDriver方法注册MySQL驱动
            DriverManager.registerDriver(new Driver());
        } catch (SQLException var1) {
            throw new RuntimeException("Can't register driver!");
        }
    }
}

由以上代码可以看出在MySQL驱动中,Driver类一经加载即可自动注册MySQL驱动,如果使用DriverManager类的registerDriver方法注册MySQL驱动会注册两次MySQL驱动

/**
 * 注册了两次驱动
 * 1.new com.mysql.jdbc.Driver()加载Driver类时
 * 2.DriverManager调用registerDriver方法时
 */
DriverManager.registerDriver(new com.mysql.jdbc.Driver());

所以,实际开发中,为了节省CPU资源,避免重复加载驱动,使用反射加载驱动类的方式:

Class.forName("com.mysql.jdbc.Driver");

2)获取Connection:

DriverManager类的第二个功能是获取Connection数据库连接,成功获取与数据库的连接后才能操作数据库。
DriverManager类获取数据库连接的方法:

public static Connection getConnection(String url,String user,String password)

该方法作用试图建立到给定数据库 URL 的连接,并返回一个数据库连接对象connection
getConnection方法有三个参数:

参数功能
urlURL用于标识数据库的位置信息(好比访问网站时的网址,提供服务端信息)
user数据库用户名
password对应用户的密码

url参数详解:

通过URL地址基本格式为:
1协议,2子协议,3主机,4端口,5数据库
例如: jdbc:mysql://localhost:3306/shop,如果连接的是本机并且端口号是3306,以上代码也可以简写成jdbc:mysql:///shop

url部分含义规定来源
1协议jdbcJDBC规范,固定的
2子协议数据库类型名称,这里使用MySQLJDBC规范,根据数据库种类填写
3主机数据库所在的主机IP数据库厂商(MySQL)的要求
4端口号MySQL默认端口号是3306数据库厂商(MySQL)的要求
5数据库DATABASE名称数据库厂商(MySQL)的要求

url后面也可以携带参数例如:
jdbc:mysql://localhost:3306/day06?useUnicode=true&characterEncoding=UTF-8
useUnicode参数指定连接数据库的过程中,使用的字节集是Unicode字节集;characherEncoding参数指定连接数据库的过程中,使用的字节集编码为UTF-8编码。
注:mysql中指定UTF-8编码是给出的是UTF8,而不是UTF-8

2.2 Connection类

Connection代表的是一个与数据库连接的对象,常用方法:

方法功能
Statement createStatement( )创建一个 Statement 对象来将 SQL 语句发送到数据库
PreparedStatement prepareStatement(String sql)创建一个 PreparedStatement 对象来将参数化的 SQL 语句发送到数据库
CallableStatement prepareCall(String sql)创建一个 CallableStatement 对象来调用数据库存储过程
Statement createStatement(int resultSetType, int resultSetConcurrency)设置结果集(ResultSet)的属性,通过两个参数resultSetType和resultSetConcurrency进行设置
resultSetType的可选值:
含义
ResultSet.TYPE_FORWARD_ONLY不滚动结果集
ResultSet.TYPE_SCROLL_INSENSITIVE滚动结果集,结果集数据不会跟随数据库而变化
ResultSet.TYPE_SCROLL_SENSITIVE(不常用)滚动结果集,结果集数据会跟随数据库而变化
resultSetConcurrency的可选值:
含义
CONCUR_READ_ONLY结果集是只读的,不能通过修改结果集而反向影响数据库
CONCUR_UPDATABLE(不常用)结果集是可更新的,对结果集的更新可以反向影响数据库

2.3 Statement类

Statement对象用于执行静态 SQL 语句并返回它所生成结果的对象,常用方法:

方法功能
ResultSet executeQuery(String sql)执行给定的sql语句,通常是查询语句,返回单个的ResultSet结果集对象
int executeUpdate(String sql)执行给定的sql语句,该语句可能为update,delete,insert,返回的是行计数
boolean execute(String sql)执行给定的sql语句,返回值boolean表示sql语句是否正常执行
int getUpdateCount( )前提:使用execute方法进行了增删改操作,则可用此方法接收影响的行数
ResultSet getUpdateCount( )前提:使用execute方法进行了查询操作,则可用此方法接收返回的结果集

2.4 ResultSet类

ResultSet对象表示数据库结果集的数据表,是一个二维数据表,可以通过索引操作其中的数据,通常通过执行查询数据库的语句生成。ResultSet封装执行结果时,类似迭代器。

ResultSet滚动结果集(纵向遍历)

ResultSet内部有一个用来标记当前操作位置的游标,ResultSet提供了一系列的方法来移动游标。这些移动游标的方法的使用是有前提的,即在Connection调用方法createStatement时设置方法的resultSetType参数为结果集可滚动。若resultSetType参数设置为结果集不可滚动,则所有操作游标的方法中只有next( )方法能够使用
滚动结果集的操作是以行为单位进行的,也就是纵向的遍历数据库表中的数据

方法功能
void beforeFirst( )把光标放到第一行的前面,这也是光标默认的位置
void afterLast( )把光标放到最后一行的后面
boolean first( )把光标放到第一行的位置上,返回值表示调控光标是否成功
boolean last( )把光标放到最后一行的位置上
boolean isBeforeFirst( )当前光标位置是否在第一行前面
boolean isAfterLast( )当前光标位置是否在最后一行的后面
boolean isFirst( )当前光标位置是否在第一行上
boolean isLast( )当前光标位置是否在最后一行上
boolean previous( )把光标向上挪一行
boolean next( )把光标向下挪一行
boolean relative(int row)相对位移,当row为正数时,表示向下移动row行,为负数时表示向上移动row行
boolean absolute(int row)绝对位移,把光标移动到指定的行上
int getRow( )返回当前光标所有行

ResultSet获取数据(横向获取)

1,获取指定字段(通过字段索引)中某种类型的数据

参数columnIndex表示字段(列)的索引,字段(列)索引从1开始,而不是0,这第一点与数组不同。如果已经明确当前字段(列)的数据类型,使用getXXX( )方法来获取确定类型的数据,如果不明确当前字段(列)的数据类型,使用getObject( )方法获取

方法功能
Object getObject(int columnIndex)获取任意类型数据,参数columnIndex代表的是列的序号,第一列序号为1,第二列是2,以此类推
int getInt(int columnIndex)获取指定字段(列)的int类型的数据
Date getDate(int columnIndex)获取指定字段(列)的日期类型的数据
String getString(int columnIndex)获取指定字段(列)的String类型的数据
double getDouble(int columnIndex)获取指定字段(列)的int类型数据
boolean getBoolean(int columnIndex)获取指定字段(列)的boolean类型数据
2,通过字段名(列名)获取数据
方法功能
String getString(String columnName)获取名称为columnName的列的String类型的数据
int getInt(String columnName)获取名称为columnName的列的int类型的数据
double getDouble(String columnName)获取名称为columnName的列的double类型的数据
boolean getBoolean(String columnName)获取名称为columnName的列的boolean类型的数据
Object getObject(String columnName)获取名称为columnName的列的Object类型的数据

三.JDBC操作数据库

JDBC连接数据库是一个固定步骤,是常规Java项目的基本操作。

  1. 通过DriverManager注册驱动
  2. 通过DriverManager获取连接对象Connection
  3. 通过Connection获取一个可以向数据库发送sql语句的对象Statement
  4. 通过Statement对象执行sql语句(select) 得到一个结果集ResultSet
  5. 遍历结果集ResultSet,得到数据表中的数据 释放资源
  6. 释放资源
package com.itheima.utils;
import org.junit.Test;
import java.sql.*;

public class JDBCUtils {
    @Test
    public void testJDBC() throws ClassNotFoundException, SQLException {
        /**
         * 1.常用反射的方式注册驱动
         * 使用registerDriver()方法注册驱动最终会组测两次驱动,因为Driver对象中有一个静态代码块用来注册驱动
         *
         */
        Class.forName("com.mysql.jdbc.Driver");
        /**2.通过DriverManager获取连接对象*/
        Connection connection = DriverManager.getConnection(
                "jdbc:mysql://localhost:3306/shop", "root", "123");

        /**3.通过Connection获取一个操作sql语句的对象Statement*/
        Statement statement = connection.createStatement();
        /**
         * 4.执行sql语句(select) 得到一个ResultSet
         * 使用executeQuery方法执行查询会有结果集,使用executeUpdate方法执行更新则没有结果集
         */
        String sql = "select * from USER ";
        ResultSet resultSet = statement.executeQuery(sql);
        /**
         * 5.操作结果集,得到数据(使用类似迭代器)
         * ResultSet就是一张二位表格,它内部有一个"行光标",光标默认的位置在"第一行上方",我们可以调用ResultSet对象的next()方法把"行光标"向下移动一行,当第一次调用next()方法之后,"行光标"就到了第一行记录的位置,这时就可以使用ResultSet提供的getXXX(int col)方法来获取指定列的数据了
         * resultSet.next();//光标移动到第一行
         * resultSet.getInt(1);//获取第一行第一列的数据
         * 
         */
        while (resultSet.next()) {
            System.out.println("ID:" + resultSet.getString("uid") + "\tNAME:"+ resultSet.getString("name"));
        }
        /**
         * 6.释放资源
         * 包括结果集 resultSet
         * 负责向数据库发送sql语句的对象 statement
         * 数据库连接对象 connection
         * 关闭是有顺序的,先得到的后关闭,后得到的先关闭
         * 
         */
        resultSet.close();
        statement.close();
        connection.close();
    }

}

四.PreparedStatement类

4.1 SQL注入攻击

假设有登录案例SQL语句如下:

SELECT * FROM 用户表 WHERE NAME = 用户输入的用户名 AND PASSWORD = 用户输的密码

此时,当用户输入正确的账号密码后,查询到了信息则让用户登录。
但是当用户输入的账号为XXX 密码为:XXX’ OR 1=1时
则真正执行的代码变为:

SELECT * FROM 用户表 WHERE NAME='XXX' AND PASSWORD='XXX' OR 1=1'

此时,上述查询语句时永远可以查询出结果的。那么用户就直接登录成功了,显然我们不希望看到这样的结果,这便是SQL注入问题。

4.2 解决方案:

1)过滤用户输入的数据,检查其中是否包含非法字符;
2)分步校验,先使用用户名来查询用户,如果查找到了,再比较密码;
3)使用PreparedStatement代替Statement传递sql语句(最优)

4.3 PreparedStatement类

PreparedStatement即预编译声明,是Statement的子接口,PreparedStatement支持对sql语句的参数进行封装,可以有效的防止SQL攻击。同时还可以提高代码的可读性和维护性。
使用PreparedStatement类开发:

1)获取对象:

使用数据库连接返回PreparedStatement对象代替Statement对象

2)操作占位符数据

使用PreparedStatement对象传递sql语句时,sql语句中比较重要的信息比如用户名,密码,等不会直接写在sql语句中,而是用占位符”?”代替
对预编译的sql语句中”?”占位符赋值操作需要使用到的方法:

方法功能
void setInt(int parameterIndex,int x)设置int
void setString(int parameterIndex,String x)设置字符串
void setObject(int parameterIndex,Object x)设置其他类型

3)参数的含义:

参数含义
parameterIndex表示占位符位置(从1开始计数)
x传入参数的值

4)执行sql语句

PreparedStatement ppState = conn.prepareStatement("INSERT INTO aaa(id,NAME) VALUES(null,?)");
ppState.setString(1, "baby");
ppState.executeUpdate();

注:建议在开发中尽可能使用PreparedStatement代替Statement

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值