JDBC连接数据库
admin
2024-05-07 12:00:44
0

JDBC(Java DataBase Connectivity),简单来讲JDBC是利用Java语言或程序连接并且访问数据库的一门技术,是Java语言中用来规范客户端程序如何访问数据库的应用程序接口,提供了查询和更新数据库操作方法,通常是面向关系型数据库的。

步骤

  • 注册驱动
  • 获取连接
  • 获取传输器
  • 通过传输器发送SQL到服务器质性并且返回执行结果
  • 数据处理
  • 释放资源

1. 快速开始

数据准备

create database testjdbc_db charset utf8;
use testjdbc_db;
create table account(id int primary key auto_increment,name varchar(50),money double
);
insert into account values(null, 'tom', 1000);
insert into account values(null, 'andy', 1000);
insert into account values(null, 'tony', 1000);

mysql 驱动包下载

  • 下载地址
  • MySQL驱动包下载

创建 lib 目录,将 jar 包拷贝到里面,然后将整个 lib 目录 Add as Library

示例:

package com.hubery.jdbc;import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.sql.SQLException;public class ConnectionBasic {public static void main(String[] args) throws ClassNotFoundException, SQLException {//1.注册驱动Class.forName("com.mysql.cj.jdbc.Driver");//2.获取连接Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/testjdbc_db?characterEncoding=UTF-8&serverTimezone=GMT%2B8&useServerPrepStms=true&cachePrepStms=true","root","root");//3.获取传输器Statement st = conn.createStatement();//4.通过传输器发送SQL到服务器执行并且返回执行结果String sql = "select * from account";ResultSet rs = st.executeQuery(sql);//5.数据处理while (rs.next()) {int id = rs.getInt("id");String name = rs.getString("name");double money = rs.getDouble("money");System.out.println(id + ":" + name + ":" + money);}//6.释放资源if (rs != null) {try {rs.close();} catch (Exception e) {e.printStackTrace();} finally {rs = null;}}if (st != null) {try {st.close();} catch (Exception e) {e.printStackTrace();} finally {st = null;}}if (conn != null) {try {conn.close();} catch (Exception e) {e.printStackTrace();} finally {conn = null;}}}
}

1、注册驱动

  • MySQL版本在8.0之前的驱动的全限定类名是 com.mysql.jdbc.Driver
  • MySQL版本在8.0之后的驱动的全限定类名是 com.mydql.cj.jdbc.Driver

2、 连接

Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/testjdbc_db?characterEncoding=UTF-8&serverTimezone=GMT%2B8&useServerPrepStms=true&cachePrepStms=true&useSSL=false","root","root")
// 协议名称
jdbc:mysql// mysql 主机和端口
localhost:3306// 数据库名称
testjdbc_db// 设置编码和时区(mysql8 后需要自己设置时区)
characterEncoding=UTF-8&serverTimezone=GMT%2B// 保证 mysql 驱动把连接器 PreparedStaement 类型传输来的 SQL 语句发送给数据库进行预编译为函数交给服务器存储 key
useServerPrepStms=true// 保证在执行相同 SQL 语句时数据库不会二次编译
cachePrepStms=true// mysql 用户名及密码
root
root

3、发送 SQL 语句即处理结果集

  • st.executeQuery(sql):执行查询类型的 sql 语句,返回 ResultSet 结果集
  • st.executeUpdate(sql):执行更新(添加、删除、修改)类型的 sql 语句,返回 int 值,表示影响的记录数

ResultSet 结果集使用 next() 方法可以判断是否还有下一个,获取值可用以下方法:

// 不同类型用不同方法获取值while (rs.next()) {getInt(int columnIndex)getInt(String columnLable)getString(int columnIndex)getString(String columnLable)getDouble(int columnIndex)getDouble(String columnLable)getObject(int columnIndex)getObject(String columnLable)
}

4、释放资源

遵循的规则:越晚获取的资源越先关闭

2. 数据库注册工具类

可以将上述连接数据库、关闭资源这一系列步骤封装成工具类,直接调用即可,简化操作

1、JDBCUtils.java

package com.hubery.jdbc.utils;import java.sql.*;/*** 注册驱动 + 获取连接*/
public class JDBCUtils {public static Connection getConnection() {try {Class.forName("com.mysql.cj.jdbc.Driver");//2.获取连接Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/testjdbc_db?characterEncoding=UTF-8&serverTimezone=GMT%2B8&useServerPrepStms=true&cachePrepStms=true","root","root");return conn;} catch (Exception e) {e.printStackTrace();}return null;}public static Connection getConnection(String dbName, String username, String password) {try {Class.forName("com.mysql.cj.jdbc.Driver");//2.获取连接Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/" + dbName + "?characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8&useServerPrepStms=true&cachePrepStms=true",username,password);return conn;} catch (Exception e) {e.printStackTrace();}return null;}/*** 关闭连接** @param conn* @param statement* @param resultSet*/public static void close(Connection conn, Statement statement, ResultSet resultSet) {if (conn != null) {try {conn.close();} catch (Exception e) {e.printStackTrace();} finally {conn = null;}}if (statement != null) {try {statement.close();} catch (Exception e) {e.printStackTrace();} finally {statement = null;}}if (resultSet != null) {try {resultSet.close();} catch (Exception e) {e.printStackTrace();} finally {resultSet = null;}}}}

2、测试类 testJdbcUtil.java

import com.hubery.jdbc.utils.JDBCUtils;
import org.junit.Test;import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;public class testJdbcUtil {/*** 查询所有数据*/@Testpublic void testSelect() {Connection connection = null;Statement statement = null;ResultSet resultSet = null;try {connection = JDBCUtils.getConnection(); // 注册驱动、获取连接statement = connection.createStatement();// 通过传输器将 SQL 语句发送过去String sql = "select * from account";resultSet = statement.executeQuery(sql);// 处理结果while (resultSet.next()) {int id = resultSet.getInt("id");String name = resultSet.getString("name");double money = resultSet.getDouble("money");System.out.println("id: " + id + ", name: " + name + ", money: " + money);}} catch (Exception e) {e.printStackTrace();} finally {// 关闭连接,释放资源JDBCUtils.close(connection, statement, resultSet);}}/*** 修改、删除、插入数据*/@Testpublic void testUpdate() {Connection connection = null;Statement statement = null;ResultSet resultSet = null;try {connection = JDBCUtils.getConnection(); // 注册驱动、获取连接statement = connection.createStatement();// 通过传输器将 SQL 语句发送过去String sql = "update account set money = 4800 where name = 'tony'";// 删除数据
//            String sql = "delete from account where name = 'tom'";// 插入数据
//            String sql = "insert into account values(null, 'Anny', 40000) ";int rows = statement.executeUpdate(sql);// 处理结果System.out.println("更新完毕,影响行数:" + rows);} catch (Exception e) {e.printStackTrace();} finally {// 关闭连接,释放资源JDBCUtils.close(connection, statement, resultSet);}}
}

3. SQL 注入

3.1 Statement 和 PreparedStatement 区别

PreparedStatementStatement 子接口,比之更安全,效率更高

  • Statement:每次都需要将语句发送给数据库编译,正确则执行,否则抛出异常,每次都是一次新的操作,参数以拼接形式线程不安全
  • PreparedStatement:线程安全,先将语句发送给数据库预编译,错误则抛出异常,正确则生成骨架函数,用户只需传递参数即可

3.2 SQL 注入

通过拼接的方式来传递一些 SQL 语句需要的参数时,有时候可能会因为一些特殊的符号导致 SQL 语句语义发生变化,导致一些有问题的语句也能成功执行(比如下面几个示例中,不需要用户名或密码也能登录成功):

stat = conn.createStatement();
String sql = "select * from user where username='"+ user + "'and password = '"+ password +"'";
resultSet = stat.executeQuery(sql);// 正常 SQL 语句
select * from user where username = 'rose' and password = '123';// 用户名输入 rose '#',密码为空
select * from user where username = 'rose '#'' and password = '';// 用户名输入 rose 'or' 1=1,密码为空
select * from user where username = 'rose 'or' 1=1' and password = '';// 用户名为空,密码为 or' 1=1
select * from user where username = '' and password = ''or' 1=1';

解决办法

使用 PreparedStatement 对象替换 Statement,对传递的参数进行校验:

String user = "rose";
String password = "123456";//1.注册驱动,2.获取连接
conn = JdbcUtil.getConnection();//3.获取传输器,通过传输器发送SQL语句到服务器执行并返回结果
String sql = "select * from user where username = ? and password = ?";ps = conn.prepareStatement(sql);//4.设置SQL语句中的参数,其中 1/2 表示 sql 语句中 ? 的位置
ps.setString(1, user);
ps.setString(2, password);//5.执行SQL语句
rs = ps.executeQuery();

4. 连接池

在上面我们每次操作数据库都需要创建连接、初始化连接、关闭连接,这些操作都比较耗时,效率低下,要解决这种问题可以使用连接池;连接池可以一次性创建多个连接,当 close 后,就把连接放回池子中,而不是直接关闭,这样就避免了频繁地创建、初始化、关闭连接。

获取连接方式:

  • 将配置写死在静态代码中(不推荐,不够灵活)
  • 通过配置文件获取连接(c3p0.properties
  • 通过配置文件获取连接(c3p0-config.xml

1、src/c3p0-config.xml


com.mysql.cj.jdbc.Driverjdbc:mysql:///testjdbc_db?characterEncoding=UTF-8&serverTimezone=GMT%2B8&useServerPrepStms=true&cachePrepStms=truerootroot

2、src/c3p0.properties

#key=value
c3p0.driverClass=com.mysql.cj.jdbc.Driver
c3p0.jdbcUrl=jdbc:mysql:///testjdbc_db?characterEncoding=UTF-8&serverTimezone=GMT%2B8&useServerPrepStms=true&cachePrepStms=true
c3p0.user=root
c3p0.password=root

3、工具类:JdbcPoolUtil.java

package com.hubery.jdbc.utils;import com.mchange.v2.c3p0.ComboPooledDataSource;import java.sql.*;public class JdbcPoolUtil {// 创建连接池对象static ComboPooledDataSource pool = new ComboPooledDataSource();//    // 设置连接池信息(方式一)
//    static {
//        try {
//            pool.setDriverClass("com.mysql.cj.jdbc.Driver");
//            pool.setJdbcUrl("jdbc:mysql://localhost:3306/testjdbc_db?characterEncoding=UTF-8&serverTimezone=GMT%2B8&useServerPrepStms=true&cachePrepStms=true");
//            pool.setUser("root");
//            pool.setPassword("root");
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
//    }// 获取连接(配置文件方式)public static Connection getConnection() throws SQLException {return pool.getConnection();}public static void close(Connection conn, Statement statement, ResultSet resultSet) {if (resultSet != null) {try {resultSet.close();} catch (Exception e) {e.printStackTrace();} finally {resultSet = null;}}if (statement != null) {try {statement.close();} catch (Exception e) {e.printStackTrace();} finally {statement = null;}}if (conn != null) {try {conn.close();} catch (Exception e) {e.printStackTrace();} finally {conn = null;}}}
}

4、使用 TestJdbcPoolDemo.java

import com.hubery.jdbc.utils.JdbcPoolUtil;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.junit.Test;import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;public class TestJdbcPoolDemo {// 查询所有数据@Testpublic void testFindAll() throws SQLException {// 初始化连接对象Connection connection = JdbcPoolUtil.getConnection();// 编写 SQL,获取连接器String sql = "select * from account";PreparedStatement preparedStatement = connection.prepareStatement(sql);// 查询ResultSet resultSet = preparedStatement.executeQuery();// 处理结果while (resultSet.next()) {int id = resultSet.getInt("id");String name = resultSet.getString("name");double money = resultSet.getDouble("money");System.out.println(id + ":" + name + ":" + money);}// 关闭连接JdbcPoolUtil.close(connection, preparedStatement, resultSet);}
}

以上使用的是 C3P0 连接池,速度较慢,但稳定性不错,更多选择可参考:JDBC连接池

静态代码封装读取 properties 配置文件

1、JdbcPoolUtil.java 工具类:

package com.hubery.jdbc.utils;import com.mchange.v2.c3p0.ComboPooledDataSource;import java.beans.PropertyVetoException;
import java.io.FileReader;
import java.io.IOException;
import java.net.URL;
import java.sql.*;
import java.util.Properties;public class JdbcPoolUtil {// 创建连接池对象static ComboPooledDataSource pool = new ComboPooledDataSource();// 文件读取采用静态代码读取,只需要读取一次static {try {// 创建 Properties 集合Properties properties = new Properties();// 获取 src 下文件的文件方式:ClassLoader 类加载器ClassLoader classLoader = JdbcPoolUtil.class.getClassLoader();URL res = classLoader.getResource("jdbc.properties");String path = res.getPath();// 加载文件properties.load(new FileReader(path));// 获取值String url = properties.getProperty("url");String user = properties.getProperty("user");String password = properties.getProperty("password");String driver = properties.getProperty("driver");System.out.println("url " + url + ", user" + user + ", password: " + password + ", driver: " + driver);// 给数据源设置相关参数pool.setDriverClass(driver);pool.setJdbcUrl(url);pool.setUser(user);pool.setPassword(password);// 初始化连接数pool.setInitialPoolSize(10);pool.setMaxPoolSize(50);    // 最大连接数System.out.println("连接成功!");} catch (IOException | PropertyVetoException e) {e.printStackTrace();}}public static Connection getConnection() throws SQLException {return pool.getConnection();}public static void close(Connection conn, Statement statement, ResultSet resultSet) {if (resultSet != null) {try {resultSet.close();} catch (Exception e) {e.printStackTrace();} finally {resultSet = null;}}if (statement != null) {try {statement.close();} catch (Exception e) {e.printStackTrace();} finally {statement = null;}}if (conn != null) {try {conn.close();} catch (Exception e) {e.printStackTrace();} finally {conn = null;}}}
}

2、jdbc.properties

driver=com.mysql.cj.jdbc.Driver
user=root
password=root
url=jdbc:mysql:///testjdbc_db?characterEncoding=UTF-8&serverTimezone=GMT%2B8&useServerPrepStms=true&cachePrepStms=true

参考地址:JDBC连接数据库详讲

相关内容

热门资讯

linux入门---制作进度条 了解缓冲区 我们首先来看看下面的操作: 我们首先创建了一个文件并在这个文件里面添加了...
C++ 机房预约系统(六):学... 8、 学生模块 8.1 学生子菜单、登录和注销 实现步骤: 在Student.cpp的...
JAVA多线程知识整理 Java多线程基础 线程的创建和启动 继承Thread类来创建并启动 自定义Thread类的子类&#...
【洛谷 P1090】[NOIP... [NOIP2004 提高组] 合并果子 / [USACO06NOV] Fence Repair G ...
国民技术LPUART介绍 低功耗通用异步接收器(LPUART) 简介 低功耗通用异步收发器...
城乡供水一体化平台-助力乡村振... 城乡供水一体化管理系统建设方案 城乡供水一体化管理系统是运用云计算、大数据等信息化手段࿰...
程序的循环结构和random库...   第三个参数就是步长     引入文件时记得指明字符格式,否则读入不了 ...
中国版ChatGPT在哪些方面... 目录 一、中国巨大的市场需求 二、中国企业加速创新 三、中国的人工智能发展 四、企业愿景的推进 五、...
报名开启 | 共赴一场 Flu... 2023 年 1 月 25 日,Flutter Forward 大会在肯尼亚首都内罗毕...
汇编00-MASM 和 Vis... Qt源码解析 索引 汇编逆向--- MASM 和 Visual Studio入门 前提知识ÿ...
【简陋Web应用3】实现人脸比... 文章目录🍉 前情提要🌷 效果演示🥝 实现过程1. u...
前缀和与对数器与二分法 1. 前缀和 假设有一个数组,我们想大量频繁的去访问L到R这个区间的和,...
windows安装JDK步骤 一、 下载JDK安装包 下载地址:https://www.oracle.com/jav...
分治法实现合并排序(归并排序)... 🎊【数据结构与算法】专题正在持续更新中,各种数据结构的创建原理与运用✨...
在linux上安装配置node... 目录前言1,关于nodejs2,配置环境变量3,总结 前言...
Linux学习之端口、网络协议... 端口:设备与外界通讯交流的出口 网络协议:   网络协议是指计算机通信网...
Linux内核进程管理并发同步... 并发同步并发 是指在某一时间段内能够处理多个任务的能力,而 并行 是指同一时间能够处理...
opencv学习-HOG LO... 目录1. HOG(Histogram of Oriented Gradients,方向梯度直方图)1...
EEG微状态的功能意义 导读大脑的瞬时全局功能状态反映在其电场结构上。聚类分析方法一致地提取了四种头表面脑电场结构ÿ...
【Unity 手写PBR】Bu... 写在前面 前期积累: GAMES101作业7提高-实现微表面模型你需要了解的知识 【技...