Java JDBC 详解、使用、连接池

您所在的位置:网站首页 java的数据库连接池 Java JDBC 详解、使用、连接池

Java JDBC 详解、使用、连接池

2024-05-11 09:19| 来源: 网络整理| 查看: 265

JDBC介绍

Java数据库连接,JDBC(Java Database Connectivity,简称JDBC)是Java语言中用来规范客户端程序如何来访问数据库的应用程序接口,提供了诸如查询和更新数据库中数据的方法。

简单说,jdbc 是Java语言为了屏蔽具体的具体的数据库操作的细节不同提供的一个框架。

在关系型数据库的处理中,大致流程都一样:

连接数据库 执行语句 返回数据 处理数据

所以,为了统一,Java官方就增加了对流程做了规范,增加了一步加载驱动,也就是加载不同的实现。

下图是 JDBC 作用图示[1]:

image.png

JDBC使用

JDBC 的使用比较简单,简单总结还是数据的大致流程。

这里看看 JDBC 连接数据的细致流程:

读取配置 加载驱动 连接数据库

这里以 MySQL 为例:

// 1.读取配置 Properties properties = new Properties(); try { InputStream in = Util.class.getClassLoader().getResourceAsStream("app.properties"); properties.load(in); } catch (IOException e) { e.printStackTrace(); System.out.println("未找到配置文件"); } String url = properties.getProperty("url"); String username = properties.getProperty("username"); String password = properties.getProperty("password"); // 2,加载驱动 Class.forName("com.mysql.cj.jdbc.Driver"); // 3.连接数据库 conn = DriverManager.getConnection(url, username, password); 数据操作

重要类、接口、方法

Statement

接口,主要声明执行语句和结果获取,在我们进行 DDL 操作的时候,可以通过 Connection.createStatement() 获取再操作。

preparestatement

是 Connection 类 的方法,通过调用此方法,传入提前定义好的 sql 语句,返回 PrepareStatement 接口的对象,同样也可以去调用 executeQuery/executeUpdate 去执行静态语句。

callablestatement

继承自PreparedStatement接口,用于调用存储过程。

boolean execute() 在 PrepareStatement 语句中使用,效果等同于 executeQuery/executeUpdate,但有一点需要注意,如是是查询语句,执行后要返回 true/false,如果要获取查询的结果,应该再次调用 Statement.getResultSet,如果是 update 类的语句,则可以调用 Statement.getUpdateCount。

ResultSet executeQuery()

执行了 SQL DQL 查询语句后,返回查询的结果集。

int executeUpdate

执行 DML 后,返回整型值,可以是影响行数,或者0,主要针对 INSERT、UPDATE、DELETE 语句。

void addBatch()

在如执行批量插入操作时,将一组参数加入到 PrepareStatement 中。

int[] executeBatch()

提交批量操作的命令,正式执行批量操作。

setSomeType()

主要针对 PrepareStatement 对象,对占位符的参数进行设置,占位符的序号+字段值。

结果集-ResultSet

getSomeType()

类似于 ResultSet.getString(),传入参数主要2种,一种通过字段名,一种通过字段在表中的序号。

Boolean next()

通过游标的移动判断是否还有数据,如返回数据后,通过 next() 方法判断是否可以继续获取数据。

使用步骤

基本步骤:

加载JDBC驱动程序 建立数据库连接Connection 创建执行SQL的语句Statement 处理执行结果ResultSet/int

这里我们通过一个简短的示例看看 JDBC 的使用。

环境:

JDK:17 MySQL:5.7

新建一个 maven 项目,添加如下依赖:

com.mysql mysql-connector-j 8.1.0 com.alibaba druid 1.2.15 org.junit.jupiter junit-jupiter-api 5.10.0 org.junit.jupiter junit-jupiter-engine 5.10.0

在 resource 目录新建个配置文件,app.properties:

url=jdbc:mysql://{ip}:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false username=root password=123456 initialSize=5 maxActive=10 minIdle=5

这里新建个 工具类,主要功能就是获取连接和关闭连接:

import java.io.IOException; import java.io.InputStream; import java.sql.*; import java.util.Properties; public class Util { public static Connection getConn() { Connection conn = null; try { // 1.读取配置 Properties properties = new Properties(); try { InputStream in = Util.class.getClassLoader().getResourceAsStream("app.properties"); properties.load(in); } catch (IOException e) { e.printStackTrace(); System.out.println("未找到配置文件"); } String url = properties.getProperty("url"); String username = properties.getProperty("username"); String password = properties.getProperty("password"); // 2,加载驱动 Class.forName("com.mysql.cj.jdbc.Driver"); // 3.连接数据库 conn = DriverManager.getConnection(url, username, password); } catch (ClassNotFoundException e) { e.printStackTrace(); System.out.println("驱动类找不到"); } catch (SQLException e) { e.printStackTrace(); System.out.println("数据库连接失败"); } return conn; } public static void close(Connection conn, Statement stm, ResultSet rs) { if (rs != null) { try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } if (stm != null) { try { stm.close(); } catch (SQLException e) { e.printStackTrace(); } } if (conn != null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } }

我们操作的表结构:

image.png

接下耒,就可以测试 JDBC的CRUD了。

增-插入操作(insert)

这里主要演示单条插入和批量插入数据:

import org.junit.jupiter.api.Test; import java.sql.*; import java.util.Random; public class TestSqlInsert { @Test public void insert() throws SQLException { Connection conn = null; // 1.获取conn conn = Util.getConn(); // 2.sql 语句,占位符 String sql = "INSERT INTO example (`name`, `age`) VALUES (?, ?)"; // 3.获取sql语句对象 PreparedStatement preparedStatement = conn.prepareStatement(sql); // 4.填入字段内容 // 第一个占位符字段,string类型 preparedStatement.setString(1, "Gabriel"); // 第二个占位符字段,int类型 preparedStatement.setInt(2, 7); // 5.执行sql preparedStatement.executeUpdate(); Util.close(conn, preparedStatement, null); } @Test public void insertBatch() throws SQLException { Connection conn = null; conn = Util.getConn(); // 关闭自动提交 conn.setAutoCommit(false); String sql = "INSERT INTO example (name, age) VALUES (?, ?)"; PreparedStatement statement = conn.prepareStatement(sql); for (int i = 0; i < 100; i++) { statement.setString(1, "Galaxy" + i); statement.setInt(2, new Random().nextInt(18, 65)); statement.addBatch(); if (i % 10 == 0) { statement.executeBatch(); } } statement.executeBatch(); conn.commit(); statement.close(); conn.close(); } } 删-按条件删除记录行(delete) import org.junit.jupiter.api.Test; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; public class TestSqlDelete { @Test public void delete() throws SQLException { Connection conn = null; conn = Util.getConn(); String sql = "DELETE FROM example WHERE age>?"; PreparedStatement preparedStatement = conn.prepareStatement(sql); preparedStatement.setInt(1, 60); preparedStatement.executeUpdate(); Util.close(conn, preparedStatement, null); } } 改-更新数据(update)

按照条件更新记录行:

import org.junit.jupiter.api.Test; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; public class TestSqlUpdate { @Test public void update() throws SQLException { Connection conn = null; // 1.获取conn conn = Util.getConn(); // 2.sql语句 String sql = "UPDATE example SET age=? WHERE id=?"; // 3.获取prepareStatement PreparedStatement preparedStatement = conn.prepareStatement(sql); // 4.根据占位符填入值 preparedStatement.setInt(1, 66); preparedStatement.setInt(2, 2); // 5.执行语句 int result = preparedStatement.executeUpdate(); System.out.println("影响行数:" + result); Util.close(conn, preparedStatement, null); } } 查-查询数据(select)

由于只是示例,这里简单获取所有数据,或者按照条件查询数据:

import org.junit.jupiter.api.Test; import java.sql.*; public class TestSqlQuery { @Test public void queryAll() throws SQLException { Connection conn = null; // 1.获取连接 conn = Util.getConn(); // 2.sql语句 String sql = "SELECT * FROM example"; // 3.获取对象 PreparedStatement preparedStatement = conn.prepareStatement(sql); // 4.执行语句 ResultSet rs = preparedStatement.executeQuery(); // 5.处理结果,多条结果用while,单条用if while (rs.next()) { // 通过字段名获取对象值 int id = rs.getInt("id"); // 通过字段列序号获取值 String name = rs.getString(2); int age = rs.getInt("age"); Date createTime = rs.getDate("create_time"); System.out.println(String.format("id: %d, name: %s, age: %d, create_time: %s", id, name, age, createTime.toString())); } Util.close(conn, preparedStatement, rs); } @Test public void queryByName() throws SQLException{ Connection conn = null; // 1.获取连接 conn = Util.getConn(); // 2.sql语句 String sql = "SELECT * FROM example WHERE name=?"; // 3.获取对象 PreparedStatement preparedStatement = conn.prepareStatement(sql); preparedStatement.setString(1,"Greg"); // 4.执行语句 ResultSet rs = preparedStatement.executeQuery(); // 5.处理结果,多条结果用while,单条用if if (rs.next()) { // 通过字段名获取对象值 int id = rs.getInt("id"); // 通过字段列序号获取值 String name = rs.getString(2); int age = rs.getInt("age"); Date createTime = rs.getDate("create_time"); System.out.println(String.format("id: %d, name: %s, age: %d, create_time: %s", id, name, age, createTime.toString())); } Util.close(conn, preparedStatement, rs); } } 事务处理

事务处理也比较简单,先开启事务,关闭自动提交,然后执行语句,提交,执行有问题则回滚事务:

import org.junit.jupiter.api.Test; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; public class TestTransaction { @Test public void test() throws SQLException { Connection conn = Util.getConn(); PreparedStatement smt = null; try { // 1.开启事务 conn.setAutoCommit(false); String sql = "UPDATE example SET age=? WHERE name=?"; smt = conn.prepareStatement(sql); smt.setInt(1, 55); smt.setString(2, "Greg"); smt.execute(); // 2.提交事务 conn.commit(); } catch (SQLException e) { e.printStackTrace(); // 3.回滚事务 conn.rollback(); } smt.close(); conn.close(); } } 数据定义操作-DML

常用的如建库或者建表,或者设置索引等。

下面演示一个建表的操作:

import org.junit.jupiter.api.Test; import java.sql.*; public class TestDDL { @Test public void testDDL() throws SQLException { Connection conn = Util.getConn(); Statement statement = conn.createStatement(); // create table String tableSql = "CREATE TABLE test (id int auto_increment primary key, name varchar(20), age int)"; // exec int res = statement.executeUpdate(tableSql); System.out.println("建表成功:" + res); } } 连接池

如果是频繁的数据库操作,还是建议通过数据库连接池来代理,通过连接池,我们可以:

不用频繁创建和销毁资源 更加快速的获取连接

连接池选择很多,这里我们主要通过阿里巴巴的 Druid 为例。

在 连接池 的示例中,我们主要实现创建连接池和连接池的简单应用。

连接池工具类:

import com.alibaba.druid.pool.DruidDataSource; import java.io.IOException; import java.io.InputStream; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.Properties; public class PoolUtil { public static Connection getPoolConn() { Connection conn = null; // 读取配置 try { Properties properties = new Properties(); try { InputStream in = PoolUtil.class.getClassLoader().getResourceAsStream("app.properties"); properties.load(in); } catch (IOException e) { e.printStackTrace(); System.out.println("未找到连接池的配置文件"); } // 创建连接池源 DruidDataSource dataSource = new DruidDataSource(); // 设置数据库连接信息 dataSource.setUrl(properties.getProperty("url")); dataSource.setUsername(properties.getProperty("username")); dataSource.setPassword(properties.getProperty("password")); // 设置连接池信息 dataSource.setInitialSize((int)Integer.parseInt((String)properties.get("initialSize"))); dataSource.setMaxActive((int)Integer.parseInt((String)properties.get("maxActive"))); dataSource.setMinIdle((int)Integer.parseInt((String)properties.get("minIdle"))); conn = dataSource.getConnection(); } catch (SQLException e) { e.printStackTrace(); } return conn; } public static void close(Connection conn, Statement stm, ResultSet rs) { if (rs != null) { try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } if (stm != null) { try { stm.close(); } catch (SQLException e) { e.printStackTrace(); } } if (conn != null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } }

连接池的应用测试

import org.junit.jupiter.api.Test; import java.sql.*; public class TestPool { @Test public void test() { System.out.println("Preparing test>>>"); Connection conn = null; PreparedStatement preparedStatement = null; ResultSet rs = null; try { conn = PoolUtil.getPoolConn(); // create sql String sql = "SELECT * FROM example"; preparedStatement = conn.prepareStatement(sql); rs = preparedStatement.executeQuery(); while (rs.next()) { int id = rs.getInt(1); String name = rs.getString(2); int age = rs.getInt(3); Date createTime = rs.getDate(4); System.out.println(String.format("id: %d, name: %s, age: %d, create_time: %s", id, name, age, createTime.toString())); } } catch (SQLException e) { System.out.println("数据库操作有问题"); e.printStackTrace(); } finally { PoolUtil.close(conn, preparedStatement, rs); } } }

通过上面的示例,我们可以发现,其实连接池的使用上和直连差别不大,但还是建议使用 连接池 访问数据库。

总结

上面的学习中,我们了解了 JDBC 是 Java 中用于数据库连接的接口,我们通过 JDBC 简单实现了对数据库的增删改查,最后学习了连接池的创建与使用,实际的项目应用中,只要项目代码稍微复杂点,我们更多的会选择 ORM框架 辅助编码实现。

参考:

1.搞懂Java访问数据库的底层逻辑:轻松搞明白JDBC 2.JDBC连接MySQL数据库 3.JDBC数据库连接池Druid详解 4.JDBC数据库连接池


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3