27 JDBC

120 阅读7分钟

1 JDBC

JDBC是Java访问数据库的标准规范,是一种用于执行SQL语句的API,可以为任何关系型数据库提供统一的访问,由一组用Java语言编写的类和接口组成.

注意JDBC只是个接口,数据库驱动是接口的实现,而各个数据库的驱动都是由各个数据库厂商负责提供,用来连接自己公司的数据库。

JDBC概述.png

2 JDBC开发

先准备数据库的数据,具体代码如下:

create table jdbc_user(
    id int primary key auto_increment,
    username varchar(50),
    password varchar(50),
    birthday date
);
insert into jdbc_user values (null,'tom','123456','1991/12/24'),
                             (null,'jack','root','1999/10/24'),
                             (null,'admin','123','2000/12/24');

将MySql对应的驱动包添加到jar包库文件夹中,下面开始正式的JDBC代码编写工作:

1 注册驱动

JDBC规范定义驱动接口:java.sql.Driver,MySql驱动包提供了实现类:com.mysql.jdbc.Driver,所以我们选择这个实现类

加载驱动的语法格式是:Class.forName(数据库驱动实现类),实例代码是:

Class.forName(com.mysql.jdbc.Drive);

注意:从JDBC3开始,可以不用注册驱动而直接使用,Class.forName这句话可以省略掉。

2 获取连接

Connection接口代表连接对象,具体的实现类有厂商实现,使用DriverManager类的静态方法getConnection可以获取数据库的连接,getConnection方法介绍如下:

方法声明介绍
Connection getConnection(String url,String user,String password)根据连接字符串,用户名和密码获取数据库连接对象

其中URL数据库连接字符串写法如下:jdbc:mysql://localhost:3306/db?characterEncoding=utf-8,其中第一部分是协议jdbc,这是固定的;第二部分是子协议,就是数据库名称,这里是MysSql数据库;第三部分是数据库厂商规定的,而MysSql要求是有数据库服务器的IP地址、端口号以及以及要使用的数据库名称组成。

3 获取语句执行平台

通过Connection的createStatement()方法获取sql语句执行对象,关于Connection接口的方法介绍如下:Statement createStatement() 创建SQL语句执行对象。

Statement类:代表一条语句对象,用于发送SQL语句给服务器,用于执行静态SQL语句并返回生成的结果对象,具体方法介绍如下。

方法声明介绍
int excuteUpdate(String sql)执行insert,update,delete语句,返回受影响的行数
ResultSet executeQuery(String sql)执行select语句,返回ResultSet结果集对象

4 处理结果集

只有当执行查询语句的时候,才用处理结果集,其中ResultSet接口是用来封装数据库查询的结果集,对结果集进行遍历,取出每一条记录,关于ResultSet接口的方法介绍如下:

方法声明功能介绍
boolean next()游标指向下一行,如果有下一条记录,就返回true,否则就返回false
Xxx getXxx(String or int )当参数是字符串时,通过列名获取值
当参数是整数时,通过列号来获取值

5 释放资源

在JDBC中需要我们是释放的资源有:ResultSet结果集、Statement语句、Connection连接,释放原则就是先开的后关,后开的先关,一般建议将释放资源的语句放在finally块中。

一般来说关闭顺序是:ResultSet结果集 -> Statement语句 -> Connection连接

释放资源.jpg

关于JDBC访问数据库的步骤:

  1. 获取驱动(可以省略)
  2. 获取连接
  3. 获取Statement语句平台对象
  4. 处理结果集(只在执行查询时处理)
  5. 释放资源

3 JDBC实现增删改查

当一个功能经常需要使用的时候,我们可以将这个功能封装成一个工具类,可以在不同的地方重用。由于获取数据库连接的操作在以后的增删改查操作中都会使用到,因此我们可以封装一个JDBCUtils工具类,实现数据库连接的创建和资源的释放,具体代码如下:

package com.lagou.task01.utils;

import java.sql.*;

public class JDBCUtils {
    public static final String DRIVERNAME="com.mysql.jdbc.Driver";
    public static final String URL="jdbc:mysql://localhost:3306/db?characterEncoding=utf-8";
    public static final String USER="root";
    public static final String PASSWORD="root";
    static {
        try {
            Class.forName(DRIVERNAME);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
    public static Connection getConnection(){
        try {
            Connection connection = DriverManager.getConnection(URL, USER, PASSWORD);
            return connection;
        } catch (SQLException throwables) {
            throwables.printStackTrace();
            return null;
        }
    }
    public static void close(Connection cn, Statement st) {
        if (null != cn && null != st) {
            try {
                st.close();
                cn.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }

        }
    }
    public static void close(Connection cn,Statement st,ResultSet rs){
        if(null!=rs){
            try {
                rs.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
        close(cn,st);
    }


}

使用JDBC对数据库的数据进行增删改查操作,具体代码如下:

package com.lagou.task01.jdbc01;

import com.lagou.task01.utils.JDBCUtils;
import org.junit.Test;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Date;

public class JDBCTest {
    @Test
    public void testInsert() throws Exception {
        Connection connection = JDBCUtils.getConnection();
        Statement statement = connection.createStatement();
        String sql="insert into jdbc_user values(null ,'Eric','qwer','1998/01/06')";
        int rs=statement.executeUpdate(sql);
        System.out.println(rs);
        JDBCUtils.close(connection,statement);



    }
    @Test
    public void testUpdate() throws Exception {
        Connection connection = JDBCUtils.getConnection();
        Statement statement = connection.createStatement();
        String sql="update jdbc_user set username='张百万' where id=5";
        statement.executeUpdate(sql);
        JDBCUtils.close(connection,statement);

    }
    @Test
    public void testDelete() throws Exception{
        Connection connection = JDBCUtils.getConnection();
        Statement statement = connection.createStatement();
        String sql="delete from jdbc_user where id=5";
        statement.executeUpdate(sql);
        JDBCUtils.close(connection,statement);

    }
    @Test
    public void testQuery() throws Exception{
        Connection connection = JDBCUtils.getConnection();
        Statement statement = connection.createStatement();
        String sql="select * from jdbc_user";
        ResultSet resultSet = statement.executeQuery(sql);
        while (resultSet.next()){
            int id=resultSet.getInt("id");
            String username=resultSet.getString("username");
            String password=resultSet.getString("password");
            Date birthday=resultSet.getDate("birthday");
            System.out.println(id+" "+username+" "+password+" "+birthday);
        }
        JDBCUtils.close(connection,statement,resultSet);

    }


}

4 SQL注入问题

先看一句SQL代码

select * from jdbc_user where username='tom' and password='222' or '1'='1';

可以看到只要执行上面的代码,不管用户名和密码是否正确,都可以从数据库中查询出数据,这正是SQL注入所使用的方法。

所谓SQL注入是用户输入的内容作为SQL语句的一部分,改变了原有SQL的真正意义,这就是SQL注入。

解决方案:很简单我们只需要不让用户输入的内容和我们的SQL语句进行简单的字符串拼接,这就需要使用到预处理对象PreparedStatement。

PreparedStatement是Statement接口的子接口,是一个预编译的SQL语句对象,而所谓的预编译就是指SQL语句被预编译,并存储在PreparedStatement对象中,然后可以使用此对象多次高效的执行该语句。

PreparedStatement对象的特点:

  • 因为有预先编译的功能,提高了SQL语句执行的效率
  • 可以有效的防止SQL注入的问题,安全性更高

PrepareStatement接口的常用方法:

方法声明功能介绍
PreparedStatement prepareStatement(String sql)指定预编译的SQL语句,创建语句对象
int executeUpdate()执行insert、update、delete语句
ResultSet executeQuery()执行select语句,返回查询到的结果集

使用PrepareStatement的实例代码:

@Test
    public void PrepareStatementTest()throws Exception{
        Connection connection = JDBCUtils.getConnection();
        String sql="select * from jdbc_user where username=? and password=? ";
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        preparedStatement.setString(1,"admin");
        preparedStatement.setString(2,"123");
        ResultSet resultSet = preparedStatement.executeQuery();
        while (resultSet.next()){
            int id=resultSet.getInt("id");
            String username=resultSet.getString("username");
            String password=resultSet.getString("password");
            Date birthday=resultSet.getDate("birthday");
            System.out.println(id+" "+username+" "+password+" "+birthday);
        }
        JDBCUtils.close(connection,preparedStatement,resultSet);

    }

可以看到PreparedStatement和Statement的区别是:

  • Statement用于执行静态的SQL语句,在执行时,必须制定一个事先准备好的SQL语句
  • PreparedStatement是预编译的SQL语句对象,语句中可以包含动态参数"?",在执行时可以为"?"占位符动态设置参数值
  • PreparedStatement可以减少编译次数,从而提高数据库性能

预处理对象执行原理.jpg

5 JDBC控制事务

在MySQL中我们可以通过命令的方式操作事务,当然在JDBC中我们也可以通过代码的方式操作事务,使用Connection中的方法实现对事务的管理,具体方法介绍如下:

方法声明功能介绍
void setAutoCommit(boolean auto)参数是true时时自动提交事务,否则就关闭自动提交,相当于开启事务
void commit()提交事务
void rollback()回滚事务

JDBC控制事务的步骤是:

  1. 获取连接
  2. 开启事务
  3. 获取到PreparedStatement,执行SQL语句
  4. 正常情况下提交事务
  5. 出现异常就回滚
  6. 关闭资源

代码示例:

@Test
    public void TransactionTest(){
        Connection connection=null;
        PreparedStatement ps=null;
        try {
            connection = JDBCUtils.getConnection();
            connection.setAutoCommit(false);
            String sql="update jdbc_user set username=? where id=?";
            ps=connection.prepareStatement(sql);
            ps.setString(1,"王大锤");
            ps.setInt(2,2);
            ps.executeUpdate();
            connection.commit();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }finally {
            JDBCUtils.close(connection,ps);
        }

    }