Java JDBC API介绍与实现数据库连接池流程

   2023-02-09 学习力0
核心提示:目录JDBC API详解DriverManagerConnection获取执行对象事务管理Statement概述代码演示ResultSet概述代码演示PreparedStatementSQL注入PreparedStatement 概述代码演示PreparedStatement原理数据库连接池数据库连接池简介数据库连接池实现Driud 的使用前言:上

前言:上一节我带大家上手了JDBC的基本代码,这节我们仔细讲一下JDBC的API和数据库连接池。

JDBC API详解

DriverManager

DriverManager(驱动管理类)作用:注册驱动、获取数据库连接

注册驱动

Java JDBC API介绍与实现数据库连接池流程

registerDriver方法是用于注册驱动的,但是我们之前做的入门案例并不是这样写的。而是如下实现

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

我们查询MySQL提供的Driver类,看它是如何实现的,源码如下:

static {
try {
DriverManager.registerDriver(new Driver());
}catch (SQLException var1) {
throw new RuntimeException( "can 't register driver! ");
}
}

在该类中的静态代码块中已经执行了 DriverManager 对象的registerDriver() 方法进行驱动的注册了,那么我们只需要加载 Driver 类,该静态代码块就会执行。而Class.forName("com.mysql.jdbc.Driver"); 就可以加载Driver 类。

提示:MySQL 5之后的驱动包,可以省略注册驱动的步骤,自动加载jar包中META-INF/services/java.sql.Driver文件中的驱动类

获取数据库连接

Java JDBC API介绍与实现数据库连接池流程

参数说明:

  • user :用户名
  • password:密码
  • url : 连接路径

语法:

jdbc:mysql://ip地址(域名):端口号/数据库名称?参数键值对1&参数键值对2…

示例:
jdbc:mysql://localhost:3306/jdbc

补充:

  • 如果连接的是本机mysql服务器,并且mysql服务默认端口是3306,则url可以简写为:jdbc:mysql:///数据库名称?参数键值对
  • 配置 useSSL=false 参数,禁用安全连接方式,解决警告提示

Connection

Connection(数据库连接对象)作用:获取执行 SQL 的对象、管理事务

获取执行对象

普通执行SQL对象

Statement createStatement()

入门案例中就是通过该方法获取的执行对象。

预编译SQL的执行SQL对象:防止SQL注入(重要!!!)

PreparedStatement prepareStatement(sql)

执行存储过程的对象

CallableStatement prepareCall(sql)

事务管理

回顾一下MySQL事务管理的操作:

开启事务 :

BEGIN; 
或者 
START TRANSACTION;

提交事务 :

COMMIT;

回滚事务 :

ROLLBACK;

MySQL默认是自动提交事务

JDBC事务管理的方法

Connection几口中定义了3个对应的方法:

开启事务

Java JDBC API介绍与实现数据库连接池流程

参与autoCommit 表示是否自动提交事务,true表示自动提交事务,false表示手动提交事务。而开启事务需要将该参数设为为false。

提交事务

Java JDBC API介绍与实现数据库连接池流程

回滚事务

Java JDBC API介绍与实现数据库连接池流程

案例测试事务管理

编写代码

package com.bby;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
public class JdbcTransaction {
    public static void main(String[] args) throws Exception {
        String url = "jdbc:mysql:///jdbc?useSSL=false";
        String username = "root";
        String password = "1234";
        String sql = "update acount set money = 1002 where id = 1";
        String sql2 = "update acount set money = 1002 whe id = 1"; //注意这里SQL语句故意将where写错
        Class.forName("com.mysql.jdbc.Driver");
        Connection connection = DriverManager.getConnection(url, username, password);
        Statement statement = connection.createStatement();
        try {
            connection.setAutoCommit(false); // 开启手动事务
            //执行SQL语句并处理结果
            int count = statement.executeUpdate(sql);
            System.out.println(count);
            int count2 = statement.executeUpdate(sql2);
            System.out.println(count2);
            //提交事务
            connection.commit();
        } catch (Exception e) {
            //程序出现异常,回滚事务
            connection.rollback();
            e.printStackTrace();
        }        
    }
}

执行程序,看控制台结果

Java JDBC API介绍与实现数据库连接池流程

数据库更新前

Java JDBC API介绍与实现数据库连接池流程

数据库更新后

Java JDBC API介绍与实现数据库连接池流程

这里程序出现异常,进行了事务的回滚,所以数据都没有被更新

将代码更改正确

String sql2 = "update acount set money = 1002 wher id = 2";

执行程序测试

Java JDBC API介绍与实现数据库连接池流程

Java JDBC API介绍与实现数据库连接池流程

Statement

概述

Statement对象的作用就是用来执行SQL语句。而针对不同类型的SQL语句使用的方法也不一样。

执行DDL、DML语句

Java JDBC API介绍与实现数据库连接池流程

执行DQL语句

Java JDBC API介绍与实现数据库连接池流程

代码演示

此处只展示核心代码,具体代码可以参考上面的代码案例

DML语句

// 定义sql
String sql = "update account set money = 3000 where id = 1";
// 获取执行sql的对象 Statement
Statement stmt = conn.createStatement();
// 执行sql
int count = stmt.executeUpdate(sql);  //执行完DML语句,受影响的行数 

执行DDL语句

// 定义sql
String sql = "drop database db2";
// 获取执行sql的对象 Statement
Statement stmt = conn.createStatement();
// 执行sql
int count = stmt.executeUpdate(sql); //执行完DDL语句,可能是0

ResultSet

概述

ResultSet(结果集对象)作用:封装了SQL查询语句的结果

执行了DQL语句后就会返回该对象,对应执行DQL语句的方法如下:

ResultSet executeQuery(sql):执行DQL 语句,返回 ResultSet 对象

那么我们就需要从 ResultSet 对象中获取我们想要的数据。ResultSet 对象提供了操作查询结果数据的方法,如下:

Java JDBC API介绍与实现数据库连接池流程

Java JDBC API介绍与实现数据库连接池流程

Java JDBC API介绍与实现数据库连接池流程

如下图为执行SQL语句后的结果

Java JDBC API介绍与实现数据库连接池流程

一开始光标指定于第一行前,如图所示红色箭头指向于表头行。当我们调用了 next() 方法后,光标就下移到第一行数据,并且方法返回true,此时就可以通过 getInt(“id”) 获取当前行id字段的值,也可以通过 getString(“name”) 获取当前行name字段的值。如果想获取下一行的数据,继续调用 next() 方法,以此类推。

代码演示

编写JdbcResultSet

package com.bby;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
public class JdbcResultSet {
    public static void main(String[] args) throws Exception {
        Class.forName("com.mysql.jdbc.Driver");
        String url = "jdbc:mysql://localhost:3306/jdbc?useSSL=false";
        String username = "root";
        String password = "1234";
        Connection connection = DriverManager.getConnection(url, username, password);
        Statement statement = connection.createStatement();
        //定义查询的SQL语句
        String sql = "select * from acount";
        //执行查询语句,获取结果集
        ResultSet resultSet = statement.executeQuery(sql);
        //遍历结果集
        while(resultSet.next()){
            int id = resultSet.getInt("id");
            String name = resultSet.getString("name");
            String money = resultSet.getString("money");
            System.out.println("id:" + id);
            System.out.println("name:" + name);
            System.out.println("money:" + money);
            System.out.println("-----------------");
        }
        resultSet.close();
        statement.close();
        connection.close();
    }
}

查看控制台输出

Java JDBC API介绍与实现数据库连接池流程

PreparedStatement

SQL注入

SQL注入是通过操作输入来修改事先定义好的SQL语句,用以达到执行代码对服务器进行攻击的方法。

代码模拟SQL注入问题

package com.bby;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
public class JdbcSqlInjection {
    public static void main(String[] args) throws Exception {
        Class.forName("com.mysql.jdbc.Driver");
        String url = "jdbc:mysql://localhost:3306/jdbc?useSSL=false";
        String username = "root";
        String password = "1234";
        Connection connection = DriverManager.getConnection(url, username, password);
        // 接收用户输入 用户名和密码
        String name = "abcdefg";
        String pwd = "' or '1' = '1";
        String sql = "select * from users where name = '" + name + "' and password = '" + pwd + "'";
        Statement statement = connection.createStatement();
        ResultSet resultSet = statement.executeQuery(sql);
        // 判断登录是否成功
        if (resultSet.next()) {
            System.out.println("登录成功~");
        } else {
            System.out.println("登录失败~");
        }
        resultSet.close();
        statement.close();
        connection.close();
    }
}

上面代码是将用户名和密码拼接到sql语句中,拼接后的sql语句如下:

select * from users where name = 'abcdefg' and password = '' or '1' = '1'

从上面语句可以看出条件name = 'abcdefg' and password = ''不管是否满足,而 or 后面的 '1' = '1' 是始终满足的,最终条件是成立的,就可以正常的进行登陆了。

所以不管登录的密码是否正确都可以登录成功,控制台输出如下

Java JDBC API介绍与实现数据库连接池流程

PreparedStatement 概述

PreparedStatement作用:预编译SQL语句并执行,预防SQL注入问题

获取 PreparedStatement 对象

// SQL语句中的参数值,使用?占位符替代
String sql = "select * from users where name = ? and password = ?";
// 通过Connection对象获取,并传入对应的sql语句
PreparedStatement ps = connection.prepareStatement(sql);

设置参数值

上面的sql语句中参数使用 ? 进行占位,在之前之前肯定要设置这些 ? 的值。

PreparedStatement对象:

setXxx(参数1,参数2);//给 ? 赋值, 参数1是编号(从1开始) 参数2是值

(1)Xxx:数据类型 ; 如 setInt (参数1,参数2)

(2)参数1: ?的位置编号,从1 开始 参数2: ?的值

执行SQL语句

executeUpdate(); // 执行DDL语句和DML语句
executeQuery(); // 执行DQL语句

注意:调用这两个方法时不需要传递SQL语句,因为获取SQL语句执行对象时已经对SQL语句进行预编译了。

代码演示

编写JdbcPreparedStatement

package com.bby;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
public class JdbcPreparedStatement {
    public static void main(String[] args) throws Exception {
        String url = "jdbc:mysql:///jdbc?useSSL=false";
        String username = "root";
        String password = "1234";
        String name = "abcdefg";
        String pwd = "' or '1' = '1";
        Class.forName("com.mysql.jdbc.Driver");
        Connection connection = DriverManager.getConnection(url, username, password);
        //定义SQL语句
        String sql = "select * from users where name = ? and password = ?";
        //预编译SQL语句
        PreparedStatement ps = connection.prepareStatement(sql);
        //填充占位符
        ps.setString(1, name);
        ps.setString(2, pwd);
        //执行SQL语句
        ResultSet resultSet = ps.executeQuery();
        //判断登录是否成功
        if(resultSet.next()) {
            System.out.println("登陆成功!");
        } else {
            System.out.println("登录失败");
        }
        resultSet.close();
        ps.close();
        connection.close();
    }
}

查看控制台结果

Java JDBC API介绍与实现数据库连接池流程

执行上面语句就可以发现不会出现SQL注入漏洞问题了。那么PreparedStatement又是如何解决的呢?它是将特殊字符进行了转

义,转义的SQL如下:

select * from `users` where name = 'abcdefg' and password = '\'or \'1\' = \'1'

PreparedStatement原理

PreparedStatement 好处:1.预编译SQL,性能更高。2.防止SQL注入:将敏感字符进行转义

Java JDBC API介绍与实现数据库连接池流程

Java代码操作数据库流程如图所示:

将sql语句发送到MySQL服务器端

MySQL服务端会对sql语句进行如下操作

检查SQL语句

检查SQL语句的语法是否正确。

编译SQL语句。将SQL语句编译成可执行的函数。

检查SQL和编译SQL花费的时间比执行SQL的时间还要长。如果我们只是重新设置参数,那么检查SQL语句和编译SQL语句将不需要重复执行。这样就提高了性能。

执行SQL语句

MySQL服务端将结果返回

数据库连接池

数据库连接池简介

  • 数据库连接池是个容器,负责分配、管理数据库连接(Connection)
  • 它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个;
  • 释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据库连接遗漏
  • 好处:资源重用、提升系统响应速度、避免数据库连接遗漏

之前我们代码中使用连接使没有使用都创建一个Connection对象,使用完毕就会将其销毁。这样重复创建销毁的过程是特别耗费计算机的性能的及消耗时间的。而数据库使用了数据库连接池后,就能达到Connection对象的复用,如下图:

Java JDBC API介绍与实现数据库连接池流程

连接池是在一开始就创建好了一些连接(Connection)对象存储起来。用户需要连接数据库时,不需要自己创建连接,而只需要从连

接池中获取一个连接进行使用,使用完毕后再将连接对象归还给连接池;这样就可以起到资源重用,也节省了频繁创建连接销毁连接

所花费的时间,从而提升了系统响应的速度。

数据库连接池实现

标准接口:DataSource

官方(SUN) 提供的数据库连接池标准接口,由第三方组织实现此接口。该接口提供了获取连接的功能:

Connection getConnection()

那么以后就不需要通过 DriverManager 对象获取 Connection对象,而是通过连接池(DataSource)获取 Connection 对象。

常见的数据库连接池:DBCP 、C3P0 、Druid

我们现在使用更多的是Druid,它的性能比其他两个会好一些

Druid(德鲁伊)

Druid连接池是阿里巴巴开源的数据库连接池项目,功能强大,性能优秀,是Java语言最好的数据库连接池之一

Driud 的使用

导入jar包 druid-1.1.12.jar

将druid的jar包放到项目下的lib下并添加为库文件

Java JDBC API介绍与实现数据库连接池流程

编写配置文件 druid.properties

driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql:///jdbc
useSSL=false&useServerPrepStmts=true
username=root
password=1234
# 初始化连接数量
initialSize=5
# 最大连接数
maxActive=10
# 最大等待时间
maxWait=3000

编写Java代码(JdbcDruid)

package com.bby;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.FileInputStream;
import java.sql.Connection;
import java.util.Properties;
public class JdbcDruid {
    public static void main(String[] args) throws Exception {
        // 加载配置文件
        Properties prop = new Properties();
        prop.load(new FileInputStream("jdbc-demo/src/druid.properties"));
        // 获取连接池对象
        DataSource dataSource = DruidDataSourceFactory.createDataSource(prop);
        // 获取数据库连接 Connection
        Connection connection = dataSource.getConnection();
        System.out.println(connection); //获取到了连接后就可以继续做其他操作了
    }
}

运行查看控制台结果

Java JDBC API介绍与实现数据库连接池流程

原文地址:https://blog.csdn.net/qq_58233406/article/details/127647030
 
反对 0举报 0 评论 0
 

免责声明:本文仅代表作者个人观点,与乐学笔记(本网)无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
    本网站有部分内容均转载自其它媒体,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责,若因作品内容、知识产权、版权和其他问题,请及时提供相关证明等材料并与我们留言联系,本网站将在规定时间内给予删除等相关处理.

  • #新闻拍一拍# Oracle 调研如何避免让 Java 开发者投奔 Rust 和 Kotlin | Linux 中国
    #新闻拍一拍# Oracle 调研如何避免让 Java 开发
     导读:• 英特尔对迟迟不被 Linux 主线接受的 SGX Enclave 进行了第 38 次修订 • ARM 支持开源的 Panfrost Gallium3D 驱动本文字数:977,阅读时长大约:1分钟作者:硬核老王Oracle 调研如何避免让 Java 开发者投奔 Rust 和 KotlinOracle 委托分析公司 Omd
    03-08
  • oogle的“ JavaScript杀手” Dart 与JavaScript的比较
    oogle的“ JavaScript杀手” Dart 与JavaScript
    JavaScript通常被称为浏览器脚本语言,但它也已扩展到许多服务器端和移动应用程序开发环境。JS已经存在了将近20年,可以肯定地说它确实是一种成熟且稳定的编程语言。在Facebook发布React和React Native框架之后,JS变得越来越流行。JavaScript具有自己的软件
    03-08
  • sf02_选择排序算法Java Python rust 实现
    Java 实现package common;public class SimpleArithmetic {/** * 选择排序 * 输入整形数组:a[n] 【4、5、3、7】 * 1. 取数组编号为i(i属于[0 , n-2])的数组值 a[i],即第一重循环 * 2. 假定a[i]为数组a[k](k属于[i,n-1])中的最小值a[min],即执行初始化 min =i
    02-09
  • Delphi XE6 通过JavaScript API调用百度地图
    Delphi XE6 通过JavaScript API调用百度地图
    参考昨天的内容,有朋友还是问如何调用百度地图,也是,谁让咱都在国内呢,没办法,你懂的。 首先去申请个Key,然后看一下百度JavaScript的第一个例子:http://developer.baidu.com/map/jsdemo.htm下一步,就是把例子中的代码,移动TWebBrower中。 unit Unit
    02-09
  • JavaScript面向对象轻松入门之抽象(demo by ES5
    抽象的概念  狭义的抽象,也就是代码里的抽象,就是把一些相关联的业务逻辑分离成属性和方法(行为),这些属性和方法就可以构成一个对象。  这种抽象是为了把难以理解的代码归纳成与现实世界关联的概念,比如小狗这样一个对象:属性可以归纳出“毛色”、
    02-09
  • Java与Objective-C的渊源 objective-c和c++的区
    java创始成员Patrick Naughton回忆,通常人们会认为Java是学Modula-3和C+,其实这些都是谣传,而对Java影响比较大的则是Objective-C:单 继承、动态绑定和加载、类对象、纯虚函数、反射、原始类型包装类等。Java的接口直接抄自OC的协议。  Objective-C是扩
    02-09
  • Java项目导出数据为 PDF 文件的操作代码
    Java项目导出数据为 PDF 文件的操作代码
    目录Java项目如何导出数据为 PDF 文件?一、代码结构如下二、代码说明1、添加依赖 pom.xml2、HTML模板文件 audit_order_record.html3、添加字体4、PDF 导出工具类5、导出接口6、打开浏览器测试三、效果图Java项目如何导出数据为 PDF 文件?一个小需求,需要将
  • 盘点Java中延时任务的多种实现方式 java 延时队列怎么实现
    盘点Java中延时任务的多种实现方式 java 延时队
    目录场景描述实现方式一、挂起线程二、ScheduledExecutorService 延迟任务线程池三、DelayQueue(延时队列)四、Redis-为key指定超时时长,并监听失效key五、时间轮六、消息队列-延迟队列场景描述①需要实现一个定时发布系统通告的功能,如何实现? ②支付超时
  • Java Semaphore信号量使用分析讲解
    Java Semaphore信号量使用分析讲解
    目录前言介绍和使用API介绍基本使用原理介绍获取许可acquire()释放许可release()总结前言大家应该都用过synchronized 关键字加锁,用来保证某个时刻只允许一个线程运行。那么如果控制某个时刻允许指定数量的线程执行,有什么好的办法呢? 答案就是JUC提供的信
  • 【Java并发入门】03 互斥锁(上):解决原子性问题
    【Java并发入门】03 互斥锁(上):解决原子性
    原子性问题的源头是线程切换Q:如果禁用 CPU 线程切换是不是就解决这个问题了?A:单核 CPU 可行,但到了多核 CPU 的时候,有可能是不同的核在处理同一个变量,即便不切换线程,也有问题。所以,解决原子性的关键是「同一时刻只有一个线程处理该变量,也被称
    02-09
点击排行