JDBC链接数据库
JDBC连接数据库的七步走:
jdbc链接数据库
* 1.加载驱动
* 2.获取链接(url 用户名 密码)
* 3.编写sql
* 4.获取执行sql的statement对象
* 两个 stmt(sql注入(字符串拼接)) pstmt(预编译 防止sql注入 占位符)
* 5.执行 stmt并获取结果
* 6.遍历结果集
* 7.关闭资源(先开启的后关闭)
一、JDBC1.0版本
初版本只使用JDBC工具类完成数据库操作,即为单纯的java代码,不涉及非.java文件的内容。
1、示例场景
user表:
CREATE TABLE users (id INT(11) NOT NULL ,name VARCHAR(255),email VARCHAR(255),PRIMARY KEY (id)
);
演示数据:
INSERT INTO user (id, name, email) VALUES
(1, 'Alice', 'alice@example.com'),
(2, 'Bob', 'bob@example.com'),
(3, 'Charlie', 'charlie@example.com'),
(4, 'David', 'david@example.com'),
(5, 'Eva', 'eva@example.com'),
(6, 'Frank', 'frank@example.com'),
(7, 'Grace', 'grace@example.com'),
(8, 'Hannah', 'hannah@example.com'),
(9, 'Irene', 'irene@example.com'),
(10, 'Jack', 'jack@example.com'),
(11, 'Katherine', 'katherine@example.com'),
(12, 'Leo', 'leo@example.com'),
(13, 'Mona', 'mona@example.com'),
(14, 'Nina', 'nina@example.com'),
(15, 'Oscar', 'oscar@example.com'),
(16, 'Paul', 'paul@example.com'),
(17, 'Quincy', 'quincy@example.com'),
(18, 'Rita', 'rita@example.com'),
(19, 'Sam', 'sam@example.com'),
(20, 'Tina', 'tina@example.com'),
(21, 'Uma', 'uma@example.com'),
(22, 'Vera', 'vera@example.com'),
(23, 'Wendy', 'wendy@example.com'),
(24, 'Xander', 'xander@example.com'),
(25, 'Yvonne', 'yvonne@example.com'),
(26, 'Zach', 'zach@example.com'),
(27, 'Alex', 'alex@example.com'),
(28, 'Bella', 'bella@example.com'),
(29, 'Carl', 'carl@example.com'),
(30, 'Diana', 'diana@example.com'),
(31, 'Eve', 'eve@example.com'),
(32, 'Fred', 'fred@example.com'),
(33, 'George', 'george@example.com'),
(34, 'Holly', 'holly@example.com'),
(35, 'Ivy', 'ivy@example.com'),
(36, 'Jackie', 'jackie@example.com'),
(37, 'Kevin', 'kevin@example.com'),
(38, 'Laura', 'laura@example.com'),
(39, 'Mike', 'mike@example.com'),
(40, 'Nate', 'nate@example.com'),
(41, 'Olivia', 'olivia@example.com'),
(42, 'Peter', 'peter@example.com'),
(43, 'Quinn', 'quinn@example.com'),
(44, 'Rachel', 'rachel@example.com'),
(45, 'Sophie', 'sophie@example.com'),
(46, 'Tom', 'tom@example.com'),
(47, 'Ursula', 'ursula@example.com'),
(48, 'Victor', 'victor@example.com'),
(49, 'Walter', 'walter@example.com'),
(50, 'Xena', 'xena@example.com');
2、加载驱动
两种方式
//1.直接调用方法
DriverManager.registerDriver(new Driver());
//2. 反射加载
//Class.forName("com.mysql.jdbc.Driver");
3、获取链接
Connection conn = DriverManager.getConnection(数据库url,用户名,密码);
4、编写sql
String sql = "select * from t_user";
5、获取执行SQL的Statement对象
// 有能执行SQL语句的对象 Statement对象,能执行SQL语句,不能解决sql注入问题
Statement stmt = conn.createStatement();
//Statement stmt = conn.prepareStatement()
6、执行Statement对象
// 执行SQL语句,数据封装到结果集中
ResultSet rs = stmt.executeQuery(sql);
7、遍历结果集
while(rs.next()){// 获取到数据就OKint id = rs.getInt("id");String username = rs.getString("name");String email = rs.getString("email");// 做打印System.out.println(id+"-"+username+"-"+password+"-"+email);
}
8、关闭资源
先开启的后关闭
rs.close();
stmt.close();
conn.close();
9、完整示例
package jdbcDemo.demo1;import com.mysql.jdbc.Driver;import java.sql.*;public class JDBCTest1_1 {public static void main(String[] args) {try {// 1.加载驱动DriverManager.registerDriver(new Driver());// 2.创建链接Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "00000");// 3.编写SQL语句String sql = "select * from user limit 10";// 4. 获取执行SQL的Statement对象Statement stmt = conn.createStatement();// 5. 执行sql并获得结果集ResultSet rs = stmt.executeQuery(sql);// 6.遍历结果集while (rs.next()) {// 获取到数据就OKint id = rs.getInt("id");String username = rs.getString("name");String email = rs.getString("email");// 做打印System.out.println(id + "-" + username +"-" + email);}rs.close();stmt.close();conn.close();} catch (SQLException e) {e.printStackTrace();}}
}
为了避免资源泄露,一般使用try-catch-finally结构,在finally中关闭资源。
try{...
}catch(SQLException e){e.printStackTrace();
}finally{rs.close();stmt.close();conn.close();
}
此时不能再在try块内创建资源对象
// 数据库连接
Connection conn = null;
// 能执行SQL语句的对象
Statement stmt = null;
// 查询数据,把数据封装到该结果集中,表格
ResultSet rs = null;
try{...
}catch(SQLException e){e.printStackTrace();
}finally{rs.close();stmt.close();conn.close();
}
Java 7 后可以用 try-with-resources 简化资源释放。该方式自动关闭资源,不需要 finally 手动关闭
public static void main(String[] args) {// 加载驱动类(新版JDBC不需要这一步,如果使用的是mysql-connector-java 6.0+ 可以省略)try {DriverManager.registerDriver(new com.mysql.cj.jdbc.Driver());} catch (SQLException e) {e.printStackTrace();return; // 驱动加载失败直接退出}// try-with-resources 自动关闭资源try (Connection conn = DriverManager.getConnection("jdbc:mysql:///jdbcdemo", "root", "root");Statement stmt = conn.createStatement();ResultSet rs = stmt.executeQuery("SELECT * FROM t_user")) {while (rs.next()) {int id = rs.getInt("id");String username = rs.getString("username");String password = rs.getString("password");String email = rs.getString("email");System.out.println(id + " - " + username + " - " + password + " - " + email);}} catch (SQLException e) {e.printStackTrace();}}
10、使用工具类
每操作一次数据库,都需要重复上述步骤。
如果操作的数据库不变,那么有这么几个步骤的编写是重复的
- 加载驱动
- 获取链接(url 用户名 密码)
- 关闭资源(先开启的后关闭)
重复的代码可以将其封装成不同的方法,使用的时候进行调用即可。
1.加载驱动
/*** 加载驱动*/
public static void loadDriver(){try {// 加载驱动类DriverManager.registerDriver(new Driver());// Class.forName("com.mysql.jdbc.Driver");} catch (ClassNotFoundException e) {e.printStackTrace();}
}
2.获取链接
/*** 加载完驱动,获取到连接,返回连接对象* @return*/
public static Connection getConnection(){// 加载驱动loadDriver();// 获取到连接对象,返回Connection conn = null;try {// 获取到连接conn = DriverManager.getConnection(url,username,password);} catch (SQLException e) {e.printStackTrace();}return conn;
}
3.关闭资源
/*** 关闭资源* @param conn* @param stmt* @param rs*/
public static void close(Connection conn, Statement stmt, ResultSet rs){if(rs != null){try {rs.close();} catch (SQLException e) {e.printStackTrace();}}if(stmt != null){try {stmt.close();} catch (SQLException e) {e.printStackTrace();}}if(conn != null){try {conn.close();} catch (SQLException e) {e.printStackTrace();}}
}/*** 关闭资源* @param conn* @param stmt*/
public static void close(Connection conn, Statement stmt){if(stmt != null){try {stmt.close();} catch (SQLException e) {e.printStackTrace();}}if(conn != null){try {conn.close();} catch (SQLException e) {e.printStackTrace();}}
}
这里关闭资源需要重写两个
有ResultSet类型参数针对查询操作进行
没有ResultSet类型参数针对增删改操作进行
二、JDBC2.0版本
JDBC2.0版本使用properties文件对MySQL数据库表进行约束,每一个数据库可以对应一个properties文件进行IO操作来读取数据库的链接信息。
2.0版本和1.0版本的区别只发生在加载驱动类和创建链接的参数使用上。1.0版本中,驱动类的加载和链接数据库都是固定写死的,在2.0版本中,只需要读取对应数据库的配置信息即可。
2.0版本工具类
package jdbccc.utils;import com.mysql.jdbc.Driver;import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Properties;public class JDBCUtils {private static final String driverManager;private static final String url;private static final String username;private static final String password;// 静态代码块 类一加载(调用这个类的时候)的时候就会执行static {/** 加载db.properties 文件读取链接数据库的所有参数** */// 加载属性文件Properties properties = new Properties();// 将文件的内容获取到流中InputStream resourceAsStream = JDBCUtils.class.getResourceAsStream("/db.properties");try {properties.load(resourceAsStream);} catch (IOException e) {e.printStackTrace();}// 获取文件中的key-value值// 给常量赋值driverManager = properties.getProperty("driverclass");url = properties.getProperty("url");username=properties.getProperty("username");password=properties.getProperty("password");}/** 加载驱动* Driver函数也可以直接写进static代码块中* */public static void Driver(){try {// 1.直接调用方法// DriverManager.registerDriver(new Driver());// 2. 反射加载// Class.forName("com.mysql.jdbc.Driver");Class.forName(driverManager);}catch (Exception e){e.printStackTrace();}}/** 获取链接* */public static Connection getConnection(){Connection connection = null;try{Driver();connection = DriverManager.getConnection(url,username,password);}catch (Exception e ){e.printStackTrace();}return connection;}/** 关闭资源* 有结果集的情况——查* */public static void close(Connection connection, ResultSet resultSet, Statement statement){try{statement.close();resultSet.close();connection.close();}catch (Exception e){e.printStackTrace();}}/** 关闭资源* 有结果集的情况——增删改* */public static void close(Connection connection, Statement statement){try{statement.close();connection.close();}catch (Exception e){e.printStackTrace();}}
}
配置文件格式
driverclass=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbcdemo
username=root
password=00000
测试用例:
package com.goose;import com.goose.utils.JDBCUtils;import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;/*** @Author: Insight* @Description: TODO* @Date: 2025/5/10 17:13* @Version: 1.0*/public class JDBCTest2_2 {public static void main(String[] args) {Connection conn = JDBCUtils.getConnection();String sql = "select * from user limit 10";try {Statement statement = conn.createStatement();ResultSet rs = statement.executeQuery(sql);while (rs.next()) {int id = rs.getInt("id");String name = rs.getString("name");String email = rs.getString("email");System.out.println(id + "-" + name + "-" + email);}JDBCUtils.close(conn,rs,statement);} catch (SQLException e) {e.printStackTrace();}}
}
如果不使用工具类,每次需要执行SQL时,都需要读取一遍db.properties文件。使用工具类的话,只需要加载一次即可。
不使用工具类的示例:
package com.goose;import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;/*** @Author: Insight* @Description: TODO* @Date: 2025/5/10 17:01* @Version: 1.0*/public class JDBCTest2_1 {public static void main(String[] args) {Properties properties = new Properties();InputStream inputStream = JDBCTest2_1.class.getResourceAsStream("/db.properties");try {properties.load(inputStream);} catch (IOException e) {e.printStackTrace();}String url = properties.getProperty("url");String driverclass = properties.getProperty("driverclass");String username = properties.getProperty("username");String password = properties.getProperty("password");Connection conn = null;Statement stmt = null;ResultSet rs = null;try {// 1.加载驱动Class.forName(driverclass);conn = DriverManager.getConnection(url, username, password);stmt = conn.createStatement();String sql = "select * from user limit 10";rs = stmt.executeQuery(sql);while (rs.next()) {int id = rs.getInt("id");String name = rs.getString("name");String email = rs.getString("email");System.out.println(id + "-" + name + "-" + email);}rs.close();stmt.close();conn.close();} catch (ClassNotFoundException e) {e.printStackTrace();} catch (SQLException e) {e.printStackTrace();}}
}
三、JDBC3.0
再次升级的JDBC使用了数据库连接池。
数据库连接池是一个用于管理数据库连接的容器,主要作用为:复用数据库连接,提高性能,避免频繁创建和销毁连接带来的开销。
可以简单理解为:一个连接的缓存池,事先创建好一些连接放到池子里面,程序需要时就“借”,用完后“还”,而且不是每次都重新创建数据库连接。
举个例子理解一下:
在公司门口最开始投放了 50 辆共享单车,供公司里面 50 个人上下班使用。大家下班从门口骑车回家,第二天上班再把车停回公司门口。这样大家就不需要每个人都花钱买一辆车了,既省钱又环保。
这就好比我们程序一开始设置了一个数据库连接池,里面准备好了 50 个数据库连接,程序中每个请求来了,就去借一个连接,用完还回去,不用每次都“买”一个新连接(即新建连接对象),这样大大提高了效率。
时间久了,公司里的一些员工发现骑车太累,或者住得近,干脆选择走路上下班,于是 50 辆车每天并不会都用完,会有几辆闲着。这也就相当于连接池中,并不是每次都需要用到所有连接,有些连接会空闲着等待下一次请求。
后来公司发展迅速,新来了不少员工,这时候原来的 50 辆车开始不够用了。于是,公司决定增加车的上限,最多可以投放到 80 辆(对应连接池的 maxActive=80)。但公司不是一口气就扔出 80 辆车,而是按需增加,只有在不够用的时候,才会临时增加几辆备用车。
但公司也不是傻,车太多没人骑也是浪费资源,占地方还要维护。于是他们设置了一个策略:如果闲置的车超过 30 辆(maxIdle=30),就会主动把一部分拉走回收;如果发现空闲车太少了,比如只剩 5 辆了(低于 minIdle=10),就再提前安排几辆送过来。
而员工们使用单车也很讲规矩,用完要停回原位(就像我们使用完连接后要 close(),实际是归还给连接池,而不是真正销毁)。不守规矩的员工,用完不归还,或者直接骑回家放几天,那就是连接泄漏了 —— 连接被借走后没还回来,连接池迟早就会被“借空”,后来的同事一个连接也借不到,只能干瞪眼。
为了避免这种事,公司还装上了智能监控系统(比如 Druid 的 web 管控台),可以随时查看有多少人在用车、多少车闲着、有没有车丢了、使用情况是不是正常。
直接使用连接池
package com.goose;import com.alibaba.druid.pool.DruidDataSource;import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;/*** @Author: Goose* @Description: TODO* @Date: 2025/5/10 16:42* @Version: 1.0*/public class JDBCTest3_1 {/** 直接使用连接池* */public static void main(String[] args) {DruidDataSource dataSource = new DruidDataSource();// 设置加载类dataSource.setDriverClassName("com.mysql.jdbc.Driver");// 设置urldataSource.setUrl("jdbc:mysql://localhost:3306/jdbcdemo");// 设置用户dataSource.setUsername("root");// 设置密码dataSource.setPassword("00000");// 设置最大连接数dataSource.setMaxActive(10);// 设置初始链接数dataSource.setInitialSize(5);// 设置最大等待时间dataSource.setMaxWait(3000);// 设置最大空闲连接数dataSource.setMinIdle(6);// 设置最小最小空闲连接数dataSource.setMinIdle(3);Connection conn = null;Statement stmt = null;ResultSet rs = null;try {// 创建链接conn = dataSource.getConnection();// 定义SQLString sql = "select * from user limit 10";// 创建Statement对象stmt = conn.createStatement();// 执行SQL,并保存结果rs = stmt.executeQuery(sql);while (rs.next()) {// 获取到数据就OKint id = rs.getInt("id");String username = rs.getString("name");String email = rs.getString("email");// 做打印System.out.println(id + "-" + username + "-" + email);}} catch (Exception e) {e.printStackTrace();} finally {// 释放资源,可以使用try-catch 块,也可以直接throws异常交给上一级处理,这里最好使用try-catchtry {rs.close();} catch (SQLException e) {e.printStackTrace();}try {conn.close();} catch (SQLException e) {e.printStackTrace();}try {conn.close();} catch (SQLException e) {e.printStackTrace();}}}
}
使用工具类完成
工具类:
package com.goose.utils;import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.sql.*;
import java.util.Properties;
import java.util.logging.Logger;/*** @Author: Goose* @Description: TODO* @Date: 2025/5/10 16:17* @Version: 1.0*/
/*
* 连接池工具类
* */
public class JDBCUtils3 {private static DataSource dataSource ;static{Properties properties = new Properties();InputStream inputStream = JDBCUtils3.class.getResourceAsStream("/druid.properties");try {properties.load(inputStream);dataSource= DruidDataSourceFactory.createDataSource(properties);} catch (IOException e) {e.printStackTrace();} catch (Exception e) {e.printStackTrace();}}/** 创建链接* */public static Connection getConnection(){Connection conn = null;try {conn = dataSource.getConnection();} catch (SQLException e) {e.printStackTrace();}return conn;}/** 关闭连接——查询* */public static void closeConnection(ResultSet rs, Statement stmt,Connection conn){try {rs.close();} catch (SQLException e) {e.printStackTrace();}try {stmt.close();} catch (SQLException e) {e.printStackTrace();}try {conn.close();} catch (SQLException e) {e.printStackTrace();}}/** 关闭连接——增删改* */public static void closeConnection(Statement stmt,Connection conn){try {stmt.close();} catch (SQLException e) {e.printStackTrace();}try {conn.close();} catch (SQLException e) {e.printStackTrace();}}
}
配置文件:
driverclass=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbcdemo
username=root
password=00000
initialSize=5
maxActive=10
maxWait=3000
maxIdle=6
minIdle=3
测试示例:
package com.goose;import com.goose.utils.JDBCUtils;
import com.goose.utils.JDBCUtils3;import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;/*** @Author: Goose* @Description: TODO* @Date: 2025/5/10 17:37* @Version: 1.0*/public class JDBCTest3_2 {public static void main(String[] args) {try {// 创建链接Connection conn = JDBCUtils3.getConnection();// 创建执行sql 的Statement对象Statement stmt = conn.createStatement();// 执行sql并获取数据String sql = "INSERT INTO user (id, name, email) VALUES\n" +"(55, 'Alice', 'alice@example.com')";int i = stmt.executeUpdate(sql);System.out.println(i);JDBCUtils3.closeConnection(stmt,conn);} catch (SQLException e) {e.printStackTrace();}}
}