Java与数据库的桥梁:JDBC使用详解
JDBC
在这篇文章中,我们将深入探讨JDBC(Java Database Connectivity)的核心概念和实际应用。
你将了解如何通过JDBC实现数据库的连接、操作和管理,从而使你的Java应用程序与各种数据库无缝对接。
无论是执行SQL查询、处理结果集,还是处理事务,我们将逐步解析每个环节,提供实用示例和最佳实践。
掌握JDBC不仅能提升你的数据操作能力,更能为你的项目带来更高的灵活性和效率,让我们一起揭开这一强大工具的面纱,开启你的数据库编程之旅!
JDBC简介
为了便于程序保存和读取数据,而且能直接通过条件快速查询大指定的数据,就出现了数据库(Datebase)这种专门用于集中存取和查询的软件。
JDBC是Java程序访问数据库的标准接口。
使用Java程序访问数据库时,Java代码并不是直接通过TCP连接去访问数据库,而是通过JDBC接口来访问数据库的,而JDBC接口则通过JDBC驱动来实现真正对数据库的访问。
JDBC接口是Java标准库自带的,具体的JDBC驱动是由数据库厂商提供的从代码上来看,JDBC就是定义了一组接口,某个具体的JDBC驱动就是实现这些JDBC接口的类。
MySQL的JDBC驱动就是一个jar包,它本身也是纯Java编写的。我们只需要引入这个JDBC驱动的jar包就可以正常访问MySQL服务器。
我们自己编写的代码只需要引用Java标准库提供的java.sql包下面的相关接口,就可以间接地通过JDBC驱动jar包,通过网络,来访问MySQL服务器了,所有的复杂的网络通讯也都被封装到了JDBC驱动中。
现在Maven的依赖最新版本号是8.0.33。
</dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.33</version> <scope>runtime</scope></dependency>
JDBC连接
Connection
Connection代表一个JDBC连接,它相当于Java程序到数据库的连接(通常是TCP连接)。
打开一个Connection时,需要准备URL、用户名和密码,才能成功连接到数据库。
核心代码
核心代码是DriverManager提供的静态方法getConnection()。
DriverManager会自动扫描classpath,找到所有的JDBC驱动,然后根据我们传入的URL自动挑选一个合适的驱动。
因为JDBC连接是一种昂贵的资源,所以使用后要及时释放,所以使用try (resource)来自动释放。
class Main{ public static void main(String[] args) throws SQLException { String JDBC_URL = "jdbc:mysql://localhost:3306/learnjdbc?useSSL=false&serverTimezone=UTC";//要连接已创建的数据库,我这个test数据库是已经创建好的。 String JDBC_USER = "root";//root是默认的超级用户 String JDBC_PASSWORD = "11111111";//自定义密码,千万设置完别忘了 Connection conn = null; try { // 获取连接 conn = DriverManager.getConnection(JDBC_URL, JDBC_USER, JDBC_PASSWORD); System.out.println("连接成功!"); // TODO: 访问数据库... } catch (SQLException e) { e.printStackTrace(); } finally { // 关闭连接 if (conn != null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } }}
JDBC查询
代码步骤:
1.通过Connection提供的createStatement()方法创建一个Statement对象,用于执行一个查询;
2.执行Statement对象提供的executeQuery("SELECT * FROM students")并传入SQL语句,执行查询并获得返回的结果集,使用ResultSet来引用这个结果集;
3.反复调用ResultSet的next()方法并读取每一行结果。
注意:
因为Connection、Statement、ResultSet都是许哟啊关闭的资源,所以需要嵌套使用try,确保即时关闭,所以显得繁琐。
ResultSet获取列时,是从1开始的。
ResultSet获取列时,必须根据select的列对应的位置调用getLong()、getString()这些方法,否则对应位置的数据类型不对,将报错。
Connection conn = null; try { // 获取连接 conn = DriverManager.getConnection(JDBC_URL, JDBC_USER, JDBC_PASSWORD); System.out.println("连接成功!"); // TODO: 访问数据库... Statement statement=null; try { statement = conn.createStatement(); ResultSet result=null; try { result = statement.executeQuery("select * from students"); while (result.next()){ long id=result.getLong(1); String name=result.getString(2); long gender=result.getLong(3); long grade=result.getLong(4); int score=result.getInt(5); System.out.println(id+" "+name+" "+gender+" "+grade+" "+score); } } catch (SQLException e) { throw new RuntimeException(e); } finally { if (result!=null) try { result.close(); } catch (SQLException e) { throw new RuntimeException(e); } } } catch (SQLException e) { throw new RuntimeException(e); } catch (RuntimeException e) { throw new RuntimeException(e); } } catch (SQLException e) { e.printStackTrace(); } finally { // 关闭连接 if (conn != null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } }
SQL注入
为了避免SQL注入攻击,和statement拼接字符串非常容易引发SQL注入问题。
所以使用Java对数据库进行操作时,必须使用PreparedStatement,严禁任何通过参数拼字符串的代码!!!!
// TODO: 访问数据库... PreparedStatement ps=null; ps = conn.prepareStatement("select * from students where id>? and score>=?"); ps.setObject(1,2); ps.setObject(2,60); ResultSet result=null; result=ps.executeQuery(); while (result.next()){ long id=result.getLong(1); String name=result.getString(2); long gender=result.getLong(3); long grade=result.getLong(4); int score=result.getInt(5); System.out.println(id+" "+name+" "+gender+" "+grade+" "+score); }
数据类型
使用JDBC的时候,我们需要在Java数据类型和SQL数据类型之间进行转换。JDBC在java.sql.Types定义了一组常量来表示如何映射SQL数据类型。
JDBC的增删改查
查
// TODO: 访问数据库...PreparedStatement ps=null; ps = conn.prepareStatement("select * from students where id>? and score>=?"); ps.setObject(1,2); ps.setObject(2,60); ResultSet result=null; result=ps.executeQuery(); while (result.next()){ long id=result.getLong(1); String name=result.getString(2); long gender=result.getLong(3); long grade=result.getLong(4); int score=result.getInt(5); System.out.println(id+" "+name+" "+gender+" "+grade+" "+score); }
增
// TODO: 访问数据库... PreparedStatement ps=null; ps=conn.prepareStatement("INSERT INTO students (grade, name, gender,score) VALUES (?,?,?,?)",Statement.RETURN_GENERATED_KEYS); ps.setObject(1, 1); // grade ps.setObject(2, "olderhard"); // name ps.setObject(3, 1); // gender ps.setObject(4, 90); // score int n = ps.executeUpdate(); ResultSet result=null; result=ps.getGeneratedKeys(); if (result.next()){ System.out.println(result.getLong(1)); }
改
// TODO: 访问数据库... PreparedStatement ps=null; ps=conn.prepareStatement("UPDATE students SET name=? WHERE id=?"); ps.setObject(1,"bob"); ps.setObject(2,14); iint n=ps.executeUpdate();
删
// TODO: 访问数据库... PreparedStatement ps=null; ps=conn.prepareStatement("DELETE FROM students WHERE id=?"); ps.setObject(1,15); int n=ps.executeUpdate();
JDBC事务
数据库事务时由若干个SQL语句构成的一个操作序列。数据库系统保证在一个事务中所有SQL要么全部执行成功,要么全部不执行,即数据库事务具有ACID特性,原子性,一致性,隔离性,持久性。
要在JDBC中执行事务,本质上就是如何把多条SQL包裹在一个数据库事务中执行。
注意关键点即可:连接成功后;conn.setAutoCommit(false);之后对数据库操作完成后;就conn.commit(),提交事务;再加一个异常回滚conn.rollback()。
Connection conn = null; PreparedStatement p1=null; PreparedStatement p2=null; try { // 获取连接 conn = DriverManager.getConnection(JDBC_URL, JDBC_USER, JDBC_PASSWORD); System.out.println("连接成功!"); conn.setAutoCommit(false); //关闭自动提交模式 // TODO: 一系列对数据库的操作... conn.commit(); //提交事务 } catch (SQLException e) { if(conn!=null){ try { conn.rollback(); System.out.println("Transaction rolled back."); } catch (SQLException ex) { throw new RuntimeException(ex); } } }
JDBC批处理
在JDBC代码中,我们可以利用SQL数据库的特性,把同一个SQL但参数不同的若干次操作合并为一个batch执行。
使用JDBC的batch操作会大大提高执行效率,对内容相同,参数不同的SQL,要优先考虑batch操作。
// TODO: 访问数据库... PreparedStatement ps=null; ps=conn.prepareStatement("INSERT INTO students (name, gender, grade, score) VALUES (?, ?, ?, ?)"); for (Student s : students) { ps.setString(1, s.name); ps.setBoolean(2, s.gender); ps.setInt(3, s.grade); ps.setInt(4, s.score); ps.addBatch(); // 添加到batch } // 执行batch: int[] ns = ps.executeBatch(); for (int n : ns) { System.out.println(n + " inserted."); // batch中每个SQL执行的结果数量 }
JDBC连接池
与多线程的线程池原因类似,在执行JDBC的增删改查的操作时,如果每一次操作都来一次打开连接,操作,关闭连接。那么创建和销毁JDBC连接的开销就太大了。为了避免频繁地创建和销毁JDBC连接,我们可以通过连接池复用已经创建好的连接。
JDBC连接池有一个标准的接口javax.sql.DataSource,要使用JDBC连接池,我们必须选择一个JDBC连接池的实现。
常用的JDBC连接池有:HikariCP、C3P0、BoneCP、Druid
之后我们通过创建一个DATa Source实例(对象),这个就是连接池。
HikariConfig config = new HikariConfig();config.setJdbcUrl("jdbc:mysql://localhost:3306/test");config.setUsername("root");config.setPassword("password");config.addDataSourceProperty("connectionTimeout", "1000"); // 连接超时:1秒config.addDataSourceProperty("idleTimeout", "60000"); // 空闲超时:60秒config.addDataSourceProperty("maximumPoolSize", "10"); // 最大连接数:10DataSource ds = new HikariDataSource(config);
如何使用连接池?
类似这个
conn = DriverManager.getConnection(JDBC_URL, JDBC_USER, JDBC_PASSWORD);
这样使用
conn = config.getConnection();#Java##数据库##Java后端#
本专栏汇总了大量的Java面试题和Java面经,希望对大家有所帮助!