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】进程地...   🤣 爆笑教程 👉 《看表情包学Linux》👈 猛...
育碧GDC2018程序化大世界... 1.传统手动绘制森林的问题 采用手动绘制的方法的话,每次迭代地形都要手动再绘制森林。这...
编译原理陈火旺版第三章课后题答... 下面答案仅供参考! 1.编写一个对于 Pascal 源程序的预处理程序。该程序的作用是...
MacBookPro M2芯片... MacBookPro M2芯片下如何搭建React-Native环境目录软件下载环境配置 目录 写在...
Android studio ... 解决 Android studio 出现“The emulator process for AVD ...
pyflink学习笔记(六):... 在pyflink学习笔记(一)中简单介绍了table-sql的窗口函数,下面简单介绍下...
创建deployment 创建deployment服务编排-DeploymentDeployment工作负载均衡器介绍Depl...
gma 1.1.4 (2023... 新增   1、地图工具    a. 增加【GetWorldDEMDataSet】。提供了一套 GEO...
AI专业教您保姆级在暗影精灵8... 目录 一、Stable Diffusion介绍    二、Stable Diffusion环境搭建 ...
vue笔记 第一个Vue应用 Document{{content}}{{...
Unity自带类 --- Ti... 1.在Unity中,自己写的类(脚本)的名字不能与Unit...
托福口语21天——day5 发... 目录 一、连读纠音 二、语料输入+造句输出 三、真题 一、连读纠音 英语中的连读方式有好几种...
五、排序与分页 一、排序 1、语法 ORDER BY 字段 ASC | DESC ASC(ascen...
Linux系统中如何安装软件 文章目录一、rpm包安装方式步骤:二、deb包安装方式步骤:三、tar....
开荒手册4——Related ... 0 写在前面 最早读文献的时候,每每看到related work部分都会选择性的忽略&...
实验01:吃鸡蛋问题 1.实验目的: 通过实验理解算法的概念、算法的表示、算法的时间复杂度和空间复杂度分析&...
8个免费图片/照片压缩工具帮您... 继续查看一些最好的图像压缩工具,以提升用户体验和存储空间以及网站使用支持。 无数图像压...
Spring Cloud Al... 前言 本文小新为大家带来 Sentinel控制台规则配置 相关知识,具体内容包括流控...
多项目同时进行,如何做好进度管... 多项目同时进行,如何做好进度管理? 大多数时候,面对项目进...
ATTCK红队评估实战靶场(二... 前言 第二个靶机来喽,地址:vulunstack 环境配置 大喊一声我...