JDBC技术详解

1 综述

本文档主要对JDBC进行详细说明,包括JDBC的相关概念、架构说明、工作原理、常用接口以及具体的使用方法等。

2 JDBC相关概念

2.1 什么是JDBC

JDBC英文名为:Java Data Base Connectivity(Java数据库连接)

官方解释它是Java编程语言和广泛的数据库之间独立于数据库的连接标准的Java API,根本上说JDBC是一种规范,它提供的接口,一套完整的,允许便捷式访问底层数据库。可以用JAVA来写不同类型的可执行文件:JAVA应用程序、JAVA Applets、Java Servlet、JSP等,不同的可执行文件都能通过JDBC访问数据库,又兼备存储的优势。简单说它就是JAVA与数据库的连接的桥梁或者插件,用JAVA代码就能操作数据库的增删改查、存储过程、事务等。

2.2 JDBC有什么用

我们用JAVA就能连接到数据库;创建SQL或者MYSQL语句;执行SQL或MYSQL的查询数据库;查看和修改结果记录。

2.3 数据库驱动

我们安装好数据库之后,我们的应用程序也是不能直接使用数据库的,必须要通过相应的数据库驱动程序,通过驱动程序去和数据库打交道。其实也就是数据库厂商的JDBC接口实现,即对Connection等接口的实现类的jar文件。

下载地址:https://mvnrepository.com/artifact/mysql/mysql-connector-java

2.4 JDBC架构

分为双层架构和三层架构。

2.4.1 双层结构

jdbc

  • 作用:此架构中,Java Applet 或应用直接访问数据源。
  • 条件:要求 Driver 能与访问的数据库交互。
  • 机制:用户命令传给数据库或其他数据源,随之结果被返回。
  • 部署:数据源可以在另一台机器上,用户通过网络连接,称为 C/S配置(可以是内联网或互联网)。

2.4.2 三层架构

jdbc

3 JDBC常用接口

3.1 Driver接口

Driver接口由数据库厂家提供,作为java开发人员,只需要使用Driver接口就可以了。在编程中要连接数据库,必须先装载特定厂商的数据库驱动程序,不同的数据库有不同的装载方法。如:

  • 装载MySql驱动:
Class.forName("com.mysql.jdbc.Driver");
  • 装载Oracle驱动:
Class.forName("oracle.jdbc.driver.OracleDriver");

3.2 Connection接口

Connection与特定数据库的连接,在连接上下文中执行sql语句并返回结果。DriverManager.getConnection(url, user, password)方法建立在JDBC URL中定义的数据库Connection连接上。

  • 连接MySql数据库:
Connection conn = DriverManager.getConnection("jdbc:mysql://host:port/database", "user", "password");
  • 连接Oracle数据库:
Connection conn = DriverManager.getConnection("jdbc:oracle:thin:@host:port:database", "user", "password");
  • 连接SqlServer数据库:
Connection conn = DriverManager.getConnection("jdbc:microsoft:sqlserver://host:port; DatabaseName=database", "user", "password");

常用方法:

  • createStatement():创建向数据库发送sql的statement对象。
  • prepareStatement(sql) :创建向数据库发送预编译sql的PrepareSatement对象。
  • prepareCall(sql):创建执行存储过程的callableStatement对象。
  • setAutoCommit(boolean autoCommit):设置事务是否自动提交。
  • commit() :在链接上提交事务。
  • rollback() :在此链接上回滚事务。

3.3 Statement接口

用于执行静态SQL语句并返回它所生成结果的对象。

三种Statement类:

  1. Statement:由createStatement创建,用于发送简单的SQL语句(不带参数)。
  2. PreparedStatement :继承自Statement接口,由preparedStatement创建,用于发送含有一个或多个参数的SQL语句。PreparedStatement对象比Statement对象的效率更高,并且可以防止SQL注入,所以我们一般都使用PreparedStatement。
  3. CallableStatement:继承自PreparedStatement接口,由方法prepareCall创建,用于调用存储过程。

常用Statement方法:

  • execute(String sql):运行语句,返回是否有结果集
  • executeQuery(String sql):运行select语句,返回ResultSet结果集。
  • executeUpdate(String sql):运行insert/update/delete操作,返回更新的行数。
  • addBatch(String sql) :把多条sql语句放到一个批处理中。
  • executeBatch():向数据库发送一批sql语句执行。

3.4 ResultSet接口

ResultSet提供检索不同类型字段的方法,常用的有:

  • getString(int index)、getString(String columnName):获得在数据库里是varchar、char等类型的数据对象。
  • getFloat(int index)、getFloat(String columnName):获得在数据库里是Float类型的数据对象。
  • getDate(int index)、getDate(String columnName):获得在数据库里是Date类型的数据。
  • getBoolean(int index)、getBoolean(String columnName):获得在数据库里是Boolean类型的数据。
  • getObject(int index)、getObject(String columnName):获取在数据库里任意类型的数据。
  • 等还有其他的获取类型方法。

ResultSet还提供了对结果集进行滚动的方法:

  • next():移动到下一行
  • Previous():移动到前一行
  • absolute(int row):移动到指定行
  • beforeFirst():移动resultSet的最前面。
  • afterLast():移动到resultSet的最后面。

注意:使用后依次关闭对象及连接:ResultSet → Statement → Connection。

4 JDBC使用方法

4.1 下载jar包

上面以及展示下载地址,下载合适的版本即可。

4.2 导入jar包

  1. 在项目下建立lib文件夹
  2. 将jar包复制到文件夹中
  3. 将文件夹添加为Library

4.3 JDBC使用步骤

使用JDBC的步骤:

  1. 加载JDBC驱动程序
  2. 构造数据库链接信息
  3. 建立数据库连接Connection
  4. 创建执行SQL的语句Statement对象
  5. 使用Statement对象执行SQL
  6. 处理执行结果ResultSet
  7. 释放资源

4.4 使用代码示例

package com.wzh.demo01;

import java.sql.*;

public class JdbcDemo01 {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        // 1 加载JDBC驱动程序
        Class.forName("com.mysql.jdbc.Driver");

        // 2 构造数据库链接信息(以MySql为例)
        String url = "jdbc:mysql://localhost:3306/test_go?useUnicode=true&characterEncoding=utf8&useSSL=true";
        String username = "root";
        String pwd = "root";

        // 3 建立数据库连接Connection
        Connection connection = DriverManager.getConnection(url, username, pwd);

        // 4 创建执行SQL的语句Statement对象
        Statement statement = connection.createStatement();

        // 5 使用Statement对象执行SQL
        String sql = "select * from user";
        ResultSet resultSet = statement.executeQuery(sql);

        // 6 处理执行结果ResultSet
        while (resultSet.next()) {
            System.out.println("id=" + resultSet.getObject("id"));
            System.out.println("name=" + resultSet.getObject("name"));
            System.out.println("birthday=" + resultSet.getObject("birthday"));
        }

        // 7 释放资源 (注意关闭顺序)
        resultSet.close();
        statement.close();
        connection.close();
    }
}

4.4.1 插入更新操作

String sql = "insert into user(name,birthday) value ('hao','2021-06-28')";
int i = statement.executeUpdate(sql);
if(i>0){
    System.out.println("插入数据成功");
}

String sql1 = "update user set name='nn' where id = 2";
int i1 = statement.executeUpdate(sql1);
if(i1 > 0){
    System.out.println("更新数据成功");
}

5 封装提取工具类

  1. 在src目录下新建db.properites用于保存数据库配置信息;
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/test_go?useUnicode=true&characterEncoding=utf8&useSSL=true
username=root
password=root
  1. 新建utils包,再新建JdbcUtils.java文件,写入如下代码
package com.wzh.demo02.utils;

import java.io.InputStream;
import java.sql.*;
import java.util.Properties;

public class JdbcUtils {
    private static String driver = null;
    private static String url = null;
    private static String username = null;
    private static String password = null;

    static {
        try {
            InputStream is = JdbcUtils.class.getClassLoader().getResourceAsStream("db.properties");
            Properties properties = new Properties();
            properties.load(is);

            driver = properties.getProperty("driver");
            url = properties.getProperty("url");
            username = properties.getProperty("username");
            password = properties.getProperty("password");

            // 1 驱动只用加载一次
            Class.forName(driver);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 获取链接
    public static Connection getConnection() throws SQLException {
        return DriverManager.getConnection(url, username, password);
    }

    // 释放资源
    public static void release(Connection conn, Statement st, ResultSet rs) throws SQLException {
        if (rs != null) {
            rs.close();
        }
        if (st != null) {
            st.close();
        }
        if (conn != null) {
            conn.close();
        }
    }
}

  1. 后可在主函数使用
package com.wzh.demo02;

import com.wzh.demo02.utils.JdbcUtils;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class Test {
    public static void main(String[] args) {
        Connection conn = null;
        Statement st = null;
        ResultSet rs = null;
        try {
            conn = JdbcUtils.getConnection();
            st = conn.createStatement();

            // 新增
            String sql_i = "insert into user(name,birthday) value ('张三','2021-06-28')";
            int i = st.executeUpdate(sql_i);
            if (i > 0) {
                System.out.println("插入成功");
            }

            // 修改
            String sql_u = "update user set name='李四' where id = 3";
            int i1 = st.executeUpdate(sql_u);
            if (i1 > 0) {
                System.out.println("更新数据成功");
            }

            // 删除
            String sql_d = "delete from user where id = 3";
            int i2 = st.executeUpdate(sql_d);
            if (i2 > 0) {
                System.out.println("删除成功");
            }

            // 查找
            String sql_q = "select * from user";
            rs = st.executeQuery(sql_q);
            while (rs.next()){
                System.out.println(rs.getString("name"));
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                JdbcUtils.release(conn, st, rs);
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

6 SQL注入

详细可观看此篇博文:https://www.cnblogs.com/myseries/p/10821372.html

6.1 sql注入演示

package com.wzh.demo02;

import com.wzh.demo02.utils.JdbcUtils;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class Sql注入 {
    public static void main(String[] args) {
        //正常使用
        //login("hao", "123456");

        // 此时为sql注入
        login(" 'or '1=1","123456");
    }


    public static void login(String username, String password) {
        Connection conn = null;
        Statement st = null;
        ResultSet rs = null;
        try {
            conn = JdbcUtils.getConnection();
            st = conn.createStatement();

            String sql_login = "select * from user where name='" + username + "' and password='" + password + "'";
            rs = st.executeQuery(sql_login);
            if (rs.next()) {
                System.out.println("登录成功");
            }else{
                System.out.println("登录失败");
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                JdbcUtils.release(conn, st, rs);
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

}

由上可知Statement是不安全的,PreparedStatement可以防止SQL注入。效率更高。

6.2 PreparedStatement使用示例

package com.wzh.demo02;

import com.wzh.demo02.utils.JdbcUtils;

import java.sql.*;

public class JdbcDemo03 {
    public static void main(String[] args) {
        Connection conn = null;
        PreparedStatement st = null;
        ResultSet rs = null;
        try {
            conn = JdbcUtils.getConnection();

            // 区别:先写sql
            // 使用 ? 占位符代替参数
            String sql_i = "insert into user(name,birthday) value (?,?)";
            st = conn.prepareStatement(sql_i); // 预编译sql,
            // 手动给参数赋值
            st.setString(1, "lisi");
            st.setDate(2, new java.sql.Date(new java.util.Date().getTime()));

            int i = st.executeUpdate();
            if (i > 0) {
                System.out.println("插入成功");
            }
            
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                JdbcUtils.release(conn, st, rs);
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

此种方式可以避免sql注入。

7 JDBC操纵事务

事务的四大特性:

  • 原子性(Atomictiy):不可再分割
  • 一致性(Consistency):数据前后一致,总量保持不变
  • 隔离性(Isolation):多个事务互不干扰
  • 持久性(Durability):一旦提交不可逆,持久化到数据库

事务的隔离级别:

  1. 读未提交(Read Uncommitted):最低级别,存在脏读、幻读、不可重复读问题;
  2. 读已提交(Read Committed):可避免脏读情况发生;
  3. 可重复读(Repeatable Read):可避免脏读、不可重复读情况的发生;
  4. 序列化(Serializable):可避免脏读、不可重复读、幻读情况的发生。(串行化)

隔离性问题:

  • 脏读:一个事务读取了另一个没有提交的事务数据
  • 不可重复读:在同一个事务内,相同的语句读取出来的数据不一致
  • 幻读:在一个事务内,读取到别的数据插入的数据,导致前后读出来的数据结果条数不一致。
package com.wzh.demo02;

import com.wzh.demo02.utils.JdbcUtils;

import java.sql.*;

public class JdbcTransaction {
    public static void main(String[] args) {
        Connection conn = null;
        PreparedStatement st = null;
        ResultSet rs = null;
        try {
            conn = JdbcUtils.getConnection();

            // 关闭自动提交 并且会自动开启事务
            conn.setAutoCommit(false);

            String sql1 = "update user set money = money - 100 where id = 1";
            st = conn.prepareStatement(sql1);
            st.executeUpdate();

            String sql2 = "update user set money = money + 100 where id = 2";
            st = conn.prepareStatement(sql2);
            st.executeUpdate();

            // 业务完毕 提交事务
            conn.commit();
        } catch (Exception e) {
            try {
                conn.rollback();
            } catch (SQLException e1) {
                e1.printStackTrace();
            }
            e.printStackTrace();
        } finally {
            try {
                JdbcUtils.release(conn, st, rs);
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

修改事务隔离级别

conn = JdbcUtils.getConnection();
conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);//避免脏读
conn.setAutoCommit(false);

8 批处理

package com.wzh.demo02;

import com.wzh.demo02.utils.JdbcUtils;

import java.sql.*;

public class JdbcDemo04 {
    public static void main(String[] args) {
        Connection conn = null;
        Statement st = null;
        ResultSet rs = null;
        try {
            conn = JdbcUtils.getConnection();
            conn.setAutoCommit(false); //设为手动提交
            long start = System.currentTimeMillis();
            st = conn.createStatement();
            for (int i = 0; i < 100; i++) {
                st.addBatch("insert into user (name,birthday) values ('hao'" + i + ",'2021-06-28')");
            }
            st.executeBatch();
            conn.commit();  //提交事务
            long end = System.currentTimeMillis();
            System.out.println("插入100条数据,耗时(ms):" + (end - start));
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                JdbcUtils.release(conn, st, rs);
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

以上基本就是jdbc基本的使用方式,后续会再专门写一篇关于数据库连接池技术c3p0,dbcp与druid的使用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值