戴老师的博客 - jdbc
type: channel
link: https://tushu.info/tag/jdbc/
items: JDBC批处理Select语句
items: Java 连接池的工作原理
items: JDBC为什么要使用PreparedStatement而不是Statement
items: 基于JDBC的数据库连接池高效管理策略
items: 深入理解JDBC的超时设置
items: 用java实现JDBC数据库连接池
items: 使用Sql操作Excel-DBeaver 与 Excel JDBC 驱动使用说明
items: ResultSet自动封装数据到实体对象
items: commons.dbutils1.2介绍及使用
items: JDBC性能小贴

JDBC批处理Select语句
type: item
date: 2026-07-07T08:35:56+00:00
description: JDBC 批处理 Select 语句在网络开销中，成本最高的部分往往是客户端与服务器之间的往返请求（Round-Trips）。在 JDBC 操作中，这种情况尤为明显。对于数据插入、更新或删除操作，我们可以使用 executeBatch() 方法来减少数据库调用次数，例如：Statement pstmt = conn.createStatement(); pstmt.addBatch("insert into settings values(3,'liu'.html)"); pstmt.addBatch("insert into settings values(4,'zhi'.html)"); pstmt.addBatch("insert into settings values(5,'jun'.html)"); pstmt.executeBatch();但不幸的是，对于批量查询（Batch Select），JDBC 并没有内建（built-in）的支持方法。此外，JDBC 在执行批处理时不允许包含 SELECT 语句。例如以下代码：Statement pstmt = conn.createStatement(); pstmt.addBatch("select * from settings".html); pstmt.executeBatch();将会抛出异常：Exception in thread "main" java.sql.BatchUpdateException: Can not issue SELECT via executeUpdate(). at com.mysql.jdbc.Statement.executeBatch(Statement.java:961.html) at test.SelectBatchTest.test2(SelectBatchTest.java:49.html) at test.SelectBatchTest.main(SelectBatchTest.java:12.html)假设你想从一系列指定的 ID 列表中获取对应的名字，逻辑上我们可能期望这样做：PreparedStatement stmt = conn.prepareStatement( "select id, name from users where id in (?.html)"); stmt.setString(1, "1,2,3".html); // 错误示范但这样做并不能得到预期的结果。JDBC 只允许你用单个字面值来替换 ? 占位符。JDBC 之所以这样设计是有必要的：如果 SQL 结构自身可以随意改变，JDBC 驱动就无法预编译（Pre-compile）SQL 语句；另一方面，严格限制参数类型也能有效防止 SQL 注入攻击。针对批量查询的需求，主要有四种可替代的实现方法可供选择：分别对每个 ID 做查询一个查询做完所有事使用存储过程分批处理方法一：分别对每个 ID 做查询假设有 100 个 ID，那么就需要进行 100 次数据库调用：PreparedStatement stmt = conn.prepareStatement( "select id, name from users where id = ?"); for (int i = 0; i < 100; i++.html) { stmt.setInt(1, ids[i].html); // 设置具体的参数值 // 执行语句并获取结果 }这种方法写起来非常简单，但是性能非常慢，数据库往返需要处理 100 次，网络开销巨大。方法二：一个查询完成所有事在运行时，你可以使用循环来构建如下 SQL 语句：PreparedStatement stmt = conn.prepareStatement( "select id, name from users where id in (?, ?, ?.html)"); stmt.setInt(1, id1.html); stmt.setInt(2, id2.html); stmt.setInt(3, id3.html);这种方案从代码复杂度来看，相比第一种方法算是第二简单的。它解决了来回多次请求数据库的问题。但是，如果每次请求参数的个数不一样，预处理语句（PreparedStatement）就必须重新编译。由于每次 SQL 字面值不匹配（例如分别用 10 个 ID、3 个 ID、100 个 ID），这样会在数据库缓存中产生三个不同的预处理语句。除了重新编译预处理语句之外，先前缓存池中的预处理语句将被移除（受限于缓存池大小），进而导致后续重复查询时再次重新编译。最后，这种查询方式在内存溢出或触发磁盘分页操作时，查询会占用…

Java 连接池的工作原理
type: item
date: 2026-07-07T08:35:29+00:00
description: 什么是数据库连接？连接（Connection）是编程语言与数据库交互的一种方式。我们经常会听到这样一句话：“数据库连接很昂贵”。有人接受这种说法，却不知道它的真正含义。因此，下面我将解释它究竟是什么。[如果你已经了解了基础概念，可以直接跳到“连接池的工作原理”部分]创建连接的代码片段如下：String connUrl = "jdbc:mysql://your.database.domain/yourDBname"; Class.forName("com.mysql.jdbc.Driver".html); Connection con = DriverManager.getConnection(connUrl.html); 当我们创建了一个 Connection 对象，它在内部主要执行了以下步骤：DriverManager 检查并注册驱动程序。com.mysql.jdbc.Driver 是我们注册的驱动程序，它会在驱动程序类中调用 connect(url….html) 方法。com.mysql.jdbc.Driver 的 connect 方法根据我们请求的 connUrl，创建一个 Socket 连接，连接到 IP 为 your.database.domain、默认端口 3306 的数据库。创建的 Socket 连接将被用来查询我们指定的数据库，并最终让程序返回得到一个结果。为什么数据库连接“昂贵”？现在让我们谈谈为什么说它“昂贵”。如果创建 Socket 连接花费的时间比实际执行查询操作所花费的时间还要更长，那么频繁创建连接就会成为性能瓶颈。这就是我们所说的“数据库连接很昂贵”。因为连接资源数是 1，它需要每次创建一个 Socket 连接来访问数据库（DB）。连接池的解决方案因此，我们将使用连接池（Connection Pool）。连接池在初始化时创建一定数量的连接，然后从连接池中重用连接，而不是每次创建一个新的。连接池的工作原理接下来我们来看看它是如何工作，以及如何管理或重用现有的连接。我们使用的连接池供应者（Provider），它的内部有一个连接池管理器。当它被初始化时：1. 初始化连接池它创建连接池的默认大小，比如指定创建 5 个连接对象，并把它存放在“可用”状态的任何集合或数组中。例如，代码片段：String connUrl = "jdbc:mysql://your.database.domain/yourDBname"; String driver = "com.mysql.jdbc.Driver"; private Map<java.sql.Connection, String> connectionPool = null; private int poolSize = 5; // 假设池大小为 5 private void initPool() { try { connectionPool = new HashMap<java.sql.Connection, String>(); Class.forName(driver.html); // 创建指定数量的连接 for (int poolInd = 0; poolInd < poolSize; poolInd++.html) { java.sql.Connection con = DriverManager.getConnection(connUrl.html); connectionPool.put(con, "AVAILABLE".html); } } catch (Exception e.html) { e.printStackTrace(); } }2. 获取连接当我们调用 connectionProvider.getConnection()，然后它会从集合中获取一个连接，当然状态也会更改为“不可用”。例如，代码片段：public java.sql.Connection getConnection() throws ClassNotFoundException, SQLException { boolean isConnectionAvailable = true; for (Entry<java.sql.Connection, String> entry : connectionPool.entrySet(.html)) { synchronized (entry.html) { if ("AVAILABLE".equals(entry.getValue(.html))) { entry.setValue("NOTAVAILABLE".html); return (java.sql.Connection) entry.getKey(); } } } //…

JDBC为什么要使用PreparedStatement而不是Statement
type: item
date: 2026-07-07T08:34:47+00:00
description: JDBC 为什么要使用 PreparedStatement 而不是 StatementPreparedStatement 是用来执行 SQL 查询语句的 API 之一。Java 提供了 Statement、PreparedStatement 和 CallableStatement 三种方式来执行查询语句：Statement：用于通用查询。PreparedStatement：用于执行参数化查询。CallableStatement：用于执行存储过程。同时，PreparedStatement 经常会在 Java 面试中被提及，譬如：Statement 与 PreparedStatement 的区别是什么？如何使用 PreparedStatement 避免 SQL 注入式攻击？这篇教程中我们会讨论为什么要用 PreparedStatement？使用 PreparedStatement 有什么样的优势？它又是如何避免 SQL 注入攻击的？PreparedStatement 是什么？PreparedStatement 是 java.sql 包下面的一个接口，用来执行 SQL 语句查询。通过调用 connection.prepareStatement(sql.html) 方法可以获得 PreparedStatement 对象。数据库系统会对 SQL 语句进行预编译处理（如果 JDBC 驱动支持的话）。预处理语句将被预先编译好，这条预编译的 SQL 查询语句能在将来的查询中重用。这样一来，它比 Statement 对象生成的查询速度更快。下面是一个例子：public class PreparedStmtExample { public static void main(String args[].html) throws SQLException { // 注意：JDBC URL 通常以 jdbc: 开头，此处修正为规范格式 Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306", "root", "root".html); PreparedStatement preStatement = conn.prepareStatement("select distinct loan_type from loan where bank=?".html); preStatement.setString(1, "Citibank".html); ResultSet result = preStatement.executeQuery(); while(result.next(.html)){ System.out.println("Loan Type: " + result.getString("loan_type".html)); } } }Output:Loan Type: Personal Loan Loan Type: Auto Loan Loan Type: Home Loan Loan Type: Gold Loan在这个例子中，如果还是用 PreparedStatement 做同样的查询，哪怕参数值不一样，比如："Standard Chartered" 或者 "HSBC" 作为参数值，数据库系统还是会去调用之前编译好的执行语句（系统库初次会对查询语句做最大的性能优化）。默认会返回 TYPE_FORWARD_ONLY 类型的结果集（ResultSet），当然你也可以使用 prepareStatement() 的重载方法返回不同类型的结果集。预处理语句的优势PreparedStatement 提供了诸多好处，企业级应用开发中强烈推荐使用 PreparedStatement 来做 SQL 查询。下面列出 PreparedStatement 的几点优势。PreparedStatement 可以写动态参数化的查询用 PreparedStatement 你可以写带参数的 SQL 查询语句。通过使用相同的 SQL 语句和不同的参数值来做查询，比创建一个不同的查询语句要好。下面是一个参数化查询：SELECT interest_rate FROM loan WHERE loan_type=?现在你可以使用任何一种 loan 类型，如："personal loan"、"home loan" 或者 "gold loan" 来查询。这个例子叫做参数化查询，因为它可以用不同的参数调用它，这里的 ? 就是参数的占位符。PreparedStatement 比 Statement 更快使用 PreparedStatement 最重要的一点好处是它拥有更佳的性能优势，SQL 语句…

基于JDBC的数据库连接池高效管理策略
type: item
date: 2026-07-07T08:34:39+00:00
description: 介绍在使用 Java 语言进行与数据库相关的应用开发中，通常使用 JDBC（Java Database Connectivity）来进行数据库交互。其中有一个关键概念是 Connection（连接），它在 Java 中是一个类，代表了一个通信通道。应用程序通过该通道访问数据库中的数据。对于简单的数据库应用，由于访问频率不高，可以在需要访问时新建一个连接，用完即关闭。这种做法不会带来明显的性能开销。但对于复杂的数据库应用，情况则完全不同。频繁地建立和关闭连接会极大降低系统性能，因为连接的管理成为了系统性能的瓶颈。本文提出了一种合理、有效的连接管理策略，旨在避免对连接的随意和无规则使用。该策略的核心思想是：连接复用。通过建立数据库连接池以及一套连接使用管理策略，使得数据库连接可以得到高效、安全的复用，避免了频繁建立和关闭连接的开销。此外，通过对 JDBC 原始连接进行封装，方便了应用程序对连接的使用（特别是事务处理），提高了开发效率。封装层的存在也隔离了应用业务逻辑与具体数据库访问逻辑，使应用本身的复用成为可能。问题产生我参与的项目是开发一个网管系统，不可避免地需要与数据库打交道。项目初期，由于数据库访问不频繁，我们采用了“需要时建立，用完即关闭”的简单策略。这很符合 XP（eXtreme Programming，极限编程）的口号："Do the Simplest Thing that Could Possibly Work"（做可能工作的最简单的事情）。确实，初期工作运行良好。随着项目进展，数据库访问变得频繁，问题随之暴露。原先简单地获取和关闭数据库连接的方法严重影响了系统性能。这种影响主要是由数据库资源管理器进程频繁创建和销毁连接对象引起的。此时，有必要对数据库访问方法进行重构（Refactoring），以改进并提高系统性能。解决方案可以看出，问题的根源在于对连接资源的低效管理。对于共享资源，有一个著名的设计模式：资源池（Resource Pool）。该模式正是为了解决资源频繁分配、释放所造成的问题。将此模式应用到数据库连接管理领域，即建立一个数据库连接池，提供一套高效的连接分配和使用策略，最终目标是实现连接的高效、安全复用。3.1 建立连接池第一步是建立一个静态连接池。所谓“静态”，是指池中的连接在系统初始化时分配好，并且不能随意关闭。Java 提供了许多容器类方便构建连接池，如 Vector、Stack 等。在系统初始化时，根据配置创建连接并放置在连接池中，后续所使用的连接均从该池中获取。这样可以避免连接随意建立、关闭造成的开销（当然，无法避免 Java Garbage Collection 带来的开销）。3.2 分配、释放策略有了连接池，我们就可以提供一套自定义的分配、释放策略。当客户请求数据库连接时，首先查看连接池中是否有空闲连接（即目前没有分配出去的连接）。如果存在空闲连接，则将其分配给客户并作相应处理（主要策略是标记该连接为已分配）。若连接池中没有空闲连接，则在已经分配出去的连接中寻找一个合适的连接给客户（选择策略详见关键议题），此时该连接在多个客户间复用。当客户释放数据库连接时，根据该连接是否被复用进行不同处理。如果连接没有使用者，则将其放回连接池中，而不是直接关闭。正是这套策略保证了数据库连接的有效复用。3.3 配置策略数据库连接池中到底要放置多少个连接？连接耗尽后该如何处理？这是一个配置策略问题。一般的策略是：开始时根据具体应用需求，给出一个初始连接数目以及连接池可以扩张到的最大连接数目。本方案即按照这种策略实现。关键议题本节将对上述解决方案中的关键细节进行详述，正是这些关键策略保证了数据库连接复用的高效和安全。4.1 引用计数3.2 节中的分配、释放策略对于有效复用连接非常重要。我们采用了一个著名的设计模式：Reference Counting（引用计数）。该模式在资源复用方面应用广泛，我们将该方法运用到连接的分配释放上。每一个数据库连接保留一个引用计数，用来记录该连接的使用者个数。具体实现上，我们采用了两极连接池：空闲池和使用池。空闲池中存放目前还没有分配出去的连接；一旦一个连接被分配出去，就会放入到使用池中，并且增加引用计数。这样做有一个很大的好处：使得我们可以高效地使用连接。一旦空闲池中的连接被全部分配出去，我们就可以根据相应策略从使用池中挑选出一个正在使用的连接用来复用，而不是随意拿出一个连接。策略可以根据需要选择，我们采用的策略比较简单：复用引用计数最小的连接。Java 的面向对象特性使得我们可以灵活选择不同的策略（提供一个不同策略共用的抽象接口，各个具体策略都实现这个接口，这样策略的处理逻辑就和实现逻辑分离）。4.2 事务处理前面谈到的都是关于使用数据库连接进行普通的数据库访问。对于事务处理，情…

深入理解JDBC的超时设置
type: item
date: 2026-07-07T08:34:06+00:00
description: 深入理解 JDBC 的超时设置原文：http://www.cubrid.org/blog/dev-platform/understanding-jdbc-internals-and-timeout-configuration/恰当的 JDBC 超时设置能够有效地减少服务失效的时间。本文将对数据库的各种超时设置及其配置方法进行详细介绍。真实案例：DDos 攻击后的服务不可用在一次遭到 DDos 攻击后，整个服务陷入瘫痪。由于第四层交换机不堪重负，网络变得无法连接，导致业务系统也无法正常运转。安全组很快屏蔽了所有的 DDos 攻击并恢复了网络，但业务系统却依然无法工作。通过分析系统的 Thread Dump 发现，业务系统停在了 JDBC API 的调用上。20 分钟后，系统仍处于 WAITING 状态，无法响应；直到 30 分钟后，系统抛出异常，服务才恢复正常。为什么我们明明将 Query Timeout 设置成了 3 秒，系统却持续了 30 分钟的 WAITING 状态？为什么 30 分钟后系统又恢复正常了？当你理解了 JDBC 的超时设置机制后，就能找到问题的答案。为什么我们要了解 JDBC当遇到性能问题或系统出错时，业务系统和数据库通常是我们最关心的两个部分。在企业架构中，这两部分通常由不同的部门负责，因此各部门往往集中精力在自身领域内排查问题。这样一来，业务系统和数据库之间的部分就会成为一个盲区。对于 Java 应用而言，这个盲区就是 DBCP 数据库连接池和 JDBC，本文将集中介绍 JDBC。什么是 JDBCJDBC 是 Java 应用中用来连接关系型数据库的标准 API。Sun 公司定义了 4 种类型的 JDBC 驱动，我们主要使用的是第 4 种。该类型的 Driver 完全由 Java 代码实现，通过使用 Socket 与数据库进行通信。图 1 JDBC Type 4.第 4 种类型的 JDBC 通过 Socket 对字节流进行处理，因此涉及一些基本网络操作，类似于 HttpClient 这类网络通信库。当在网络操作中遇到问题时，将会消耗大量的 CPU 资源，并且导致响应超时。如果你之前用过 HttpClient，那么你一定遇到过未设置 Timeout 造成的错误。同样，第 4 种类型的 JDBC 若没有合理地设置 Socket Timeout，也会有相同的错误——连接被阻塞。接下来，就让我们来学习一下如何正确地设置 Socket Timeout，以及需要考虑的问题。应用与数据库间的超时层级图 2 Timeout Class.上图展示了简化后应用与数据库间的超时层级。（译者注：WAS/BLOC 是作者公司的具体应用名称，无需深究）高层级的超时设置依赖于底层级的超时设置，只有当底层级的超时设置无误时，高层级的超时才能确保正常。例如，当 Socket Timeout 出现问题时，高层级的 Statement Timeout 和 Transaction Timeout 都将失效。我们收到的很多评论中提到：即使设置了 Statement Timeout，当网络出错时，应用也无法从错误中恢复。Statement Timeout 无法处理网络连接失败导致的超时，它能做的仅仅是限制 Statement 的操作时间。网络连接失败时的 Timeout 必须交由 JDBC 来处理。JDBC 的 Socket Timeout 会受到操作系统 Socket Timeout 设置的影响，这就解释了为什么在之前的案例中，JDBC 连接会在网络出错后阻塞 30 分钟，然后又奇迹般恢复，即使我们并没有对 JDBC 的 Socket Timeout 进行设置。DBCP 连接池位于图 2 的左侧，你会发现 Timeout 层级与 DBCP 是相互独立的。DBCP 负责的是数据库连接的创建和管理，并不干涉 Timeout 的处理。当连接在 DBCP 中创建，或是 DBCP 发送校验 Query 检查连接有效性的时候，Socket Timeout 将会影响这些过程，但并不直接对应用造成影响。当在应用中调用 DBCP 的 getConnection() 方法时，你可以设置获取数据库连接的超时时间，但是这和 JDBC 的 Timeout 毫不相关。图 3 Timeout for Each Levels.什么是事务超时（Transaction Timeout）Transaction Timeout 一般存在于框架（Spring, EJB）或应用级。Transaction Timeout 或许是个相对陌生的概念，简单地说，Transaction Timeout 大致相当于 Statement Timeout × N（执行语句数量） + 其他业务逻辑耗时（如垃圾…

用java实现JDBC数据库连接池
type: item
date: 2026-07-07T08:31:44+00:00
description: 用 Java 实现 JDBC 数据库连接池本文旨在解决 Servlet 访问数据库时的稳定性问题。通过研究并实现一个适合自身需求的数据库连接池（Database Connection Pool），我们可以更好地控制程序与数据库之间建立的连接。使用连接池不仅能减小数据库访问压力，便于统一管理连接，还能显著提高连接的利用率和工作性能。在设计数据库连接池时，个人认为应重点关注以下几点：控制连接池大小：能够限制最大连接数，防止资源耗尽。统一获取接口：提供一个统一的接口用于获取数据库连接。连接回收机制：使用后的连接需要有接口能够接收并处理，使其回归池化状态。自我维护能力：连接池应具备自我维护能力，例如暂时提高连接池大小以应对连接请求高峰，或定期处理多余及无效的连接。数据结构设计首先，我们确定连接池的基础数据结构。我们需要维护空闲连接列表、已使用连接集合以及数据库配置信息。public class SimpleConnectionPool { private static LinkedList<Connection> m_notUsedConnection = new LinkedList<>(); private static HashSet<Connection> m_usedConnection = new HashSet<>(); private static String m_url = ""; private static String m_user = ""; private static String m_password = ""; private static int m_maxConnect = 3; static final boolean DEBUG = false; static private long m_lastClearClosedConnection = System.currentTimeMillis(); public static long CHECK_CLOSED_CONNECTION_TIME = 5000; // 5 秒 }核心逻辑实现1. 清除多余及无效连接我们需要定期清除连接池中多余的连接。每隔一段时间对池中的所有连接进行检查：第一轮循环判断连接是否已关闭，若已关闭则直接移除；第二轮循环则根据目前规定的最大数量裁撤空闲连接。private static void clearClosedConnection() { long time = System.currentTimeMillis(); // 时间不合理，没有必要检查 if (time < m_lastClearClosedConnection.html) { time = m_lastClearClosedConnection; return; } // 时间太短，没有必要检查 if (time - m_lastClearClosedConnection < CHECK_CLOSED_CONNECTION_TIME.html) { return; } m_lastClearClosedConnection = time; // 开始检查没有使用的 Connection Iterator<Connection> iterator = m_notUsedConnection.iterator(); while (iterator.hasNext(.html)) { Connection con = iterator.next(); try { if (con.isClosed(.html)) { iterator.remove(); } } catch (SQLException e.html) { iterator.remove(); if (DEBUG.html) { System.out.println("问题连接已断开".html); } } } // 清除多余的 Connection int decrease = getDecreasingConnectionCount(); while (decrease > 0 && m_notUsedConnection.size(.html) > 0) { Connection con = m_notUsedConnection.removeFirst(); try { con.close(); } catch (SQLException e.html) { // 忽略关闭异常 } decrease--; } }2. 获取新连接申请新连接时，首先调用清理器清除多余和无法使用的连接。随后在空闲连接中寻找可用连接，若有符合的则直接分配。若未找到，则需要建立新的连接。建立新连接后，将其中…

使用Sql操作Excel-DBeaver 与 Excel JDBC 驱动使用说明
type: item
date: 2026-07-07T08:30:52+00:00
description: 1. 概述本文档详细介绍了如何在 DBeaver 中配置和使用 Excel JDBC 驱动（XLSQL），以实现对 Excel 文件的 SQL 化查询与操作。通过该驱动，用户可以像操作传统数据库一样管理 Excel 数据。项目地址：https://github.com/daichangya/xlsql2. 准备工作2.1 环境与系统要求在开始之前，请确保您的环境满足以下要求：DBeaver 版本：21.0 或更高版本Java 环境：DBeaver 自带 Java 运行时（通常为 Java 11 或更高版本），无需单独安装 Java。XLSQL JDBC 驱动需要 Java 8+，DBeaver 自带的 Java 环境已满足此要求。注：仅在极少数情况下（如 DBeaver 自带 Java 版本低于 8），才需要单独安装 Java 8+。操作系统：Windows 7+, macOS 10.12+, Linux (大多数发行版.html)硬件资源：内存：建议至少 2GB RAM磁盘空间：至少 500MB 可用空间驱动文件：Excel JDBC 驱动 JAR 文件（xlsql）2.2 下载和安装 DBeaverDBeaver 是一款免费的通用数据库管理工具，支持多种数据库系统。下载地址官方网站：https://dbeaver.io/download/直接下载链接：Windows: https://dbeaver.io/files/dbeaver-ce-latest-win32.win32.x86_64.zipmacOS: https://dbeaver.io/files/dbeaver-ce-latest-macos-x86_64.dmgLinux: https://dbeaver.io/files/dbeaver-ce-latest-linux.gtk.x86_64.tar.gz版本选择：企业版（付费）: https://dbeaver.io/product/enterprise/社区版（免费）: https://dbeaver.io/product/community/安装步骤Windows:下载 Windows 安装包（.zip 或 .exe）。解压到目标目录或运行安装程序。运行 dbeaver.exe 启动 DBeaver。macOS:下载 .dmg 文件。双击打开，将 DBeaver 拖拽到 Applications 文件夹。从应用程序中启动 DBeaver。Linux:下载 .tar.gz 文件。解压：tar -xzf dbeaver-ce-*.tar.gz进入解压目录，运行：./dbeaver2.3 获取 Excel JDBC 驱动方式一：从 Maven Central 获取（推荐）XLSQL 5.1.1 已发布到 Maven Central，可以直接通过 Maven 依赖使用：<dependency> <groupId>io.github.daichangya</groupId> <artifactId>xlsql</artifactId> <version>5.1.1</version> </dependency>方式二：手动下载 JAR 文件从 Maven Central 直接下载 JAR 文件：标准 JAR: https://repo1.maven.org/maven2/io/github/daichangya/xlsql/5.1.1/xlsql-5.1.1.jar3. 在 DBeaver 中配置 Excel JDBC 驱动3.1 打开驱动管理器启动 DBeaver。点击菜单栏 Database → Driver Manager。3.2 创建新驱动点击 New 按钮创建新驱动。在 Settings 标签页中填写以下信息：Driver Name: Excel JDBC DriverClass Name: io.github.daichangya.xlsql.jdbc.xlDriverURL Template: jdbc:xlsql:excel:{path}Port: (留空.html)3.3 添加驱动文件切换到 Libraries 标签页。点击 Add File 按钮。选择之前下载的 Excel JDBC 驱动 JAR 文件。路径示例：/path/to/xlsql-5.1.1.jar点击 OK 保存驱动配置。4. 创建数据库连接4.1 新建连接点击 Database → New Database Connection。在连接类型列表中选择 Generic → Generic JDBC。点击 Next。4.2 配置连接参数Driver: 选择之前创建的 "Excel JDBC Driver"。JDBC URL: 输入…

ResultSet自动封装数据到实体对象
type: item
date: 2026-07-07T08:30:26+00:00
description: ResultSet 自动封装数据到实体对象在传统的 JDBC 开发中，将 ResultSet 结果集手动映射到 Java 实体对象（Entity）往往需要编写大量重复的 setter 代码。本文将介绍一种基于 Java 反射（Reflection）机制的工具类 AntResult，它能够自动根据数据库列名匹配实体类的属性，并将数据封装到对象集合中。核心工具类：AntResult该类主要通过反射获取实体类的 setter/getter 方法，并根据 ResultSetMetaData 中的列名动态调用相应方法完成数据填充。package com.daicy.util; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; public class AntResult { /** * 匹配 Bean 的 getter 和 setter 方法 * @param clazz 类对象 * @param beanProperty 属性名 * @return [getter, setter] */ private Object[] beanMatch(Class clazz, String beanProperty.html) { Object[] result = new Object[2]; char beanPropertyChars[] = beanProperty.toCharArray(); beanPropertyChars[0] = Character.toUpperCase(beanPropertyChars[0].html); String s = new String(beanPropertyChars.html); // 构建可能的方法名 String names[] = { ("set" + s.html).intern(), ("get" + s.html).intern(), ("is" + s.html).intern(), ("write" + s.html).intern(), ("read" + s.html).intern() }; Method getter = null; Method setter = null; Method methods[] = clazz.getMethods(); for (int i = 0; i < methods.length; i++.html) { Method method = methods[i]; // 只取公共字段 if (!Modifier.isPublic(method.getModifiers(.html))) continue; String methodName = method.getName().intern(); for (int j = 0; j < names.length; j++.html) { String name = names[j]; if (!name.equals(methodName.html)) continue; if (methodName.startsWith("set".html) || methodName.startsWith("read".html)) setter = method; else getter = method; } } result[0] = getter; result[1] = setter; return result; } /** * 注册 Bean 属性值 * @param object 对象实例 * @param beanProperty 属性名 * @param value 字符串值 */ private void beanRegister(Object object, String beanProperty, String value.html) { Object[] beanObject = beanMatch(object.getClass(.html…

commons.dbutils1.2介绍及使用
type: item
date: 2026-07-07T08:28:47+00:00
description: 一、结构介绍高层结构图：wrappers 包：handlers 包（部分）：二、功能介绍Commons DbUtils 是一个对 JDBC 操作进行封装的工具类库，其主要优点如下：防止资源泄漏：自动处理资源关闭，避免了繁琐的 JDBC 代码。代码整洁：显著减少样板代码。自动映射：支持从 ResultSet 自动生成 JavaBeans 属性。无依赖：除 JDBC 外无其他依赖包。三、基本使用1. 核心类说明基本用到的类主要有 QueryRunner、ResultSetHandler 及其子类。QueryRunner：执行查询的类，可以执行 SELECT、INSERT、UPDATE、DELETE 等语句。QueryRunner 使用 ResultSetHandler 的子类来处理 ResultSet 并返回结果；而包提供的 ResultSetHandler 子类使用 RowProcessor 的子类来处理 ResultSet 中的每一行。RowProcessor 的默认实现为 BasicRowProcessor。BeanProcessor 不是 RowProcessor，可以看作一个工具类。ResultSetHandler 及其子类：实现了 Object handle(ResultSet rs.html) throws SQLException 方法。AbstractListHandler：返回多行 List 的抽象类。ArrayHandler：返回一行的 Object[]。ArrayListHandler：返回 List，每行是 Object[]。BeanHandler：返回第一个 Bean 对象。BeanListHandler：返回 List，每行是 Bean。ColumnListHandler：返回一列的 List。KeyedHandler：返回 Map，具体见代码。MapHandler：返回单个 Map。MapListHandler：返回 List，每行是 Map。ScalarHandler：返回列的头一个值。2. 代码示例建表语句与数据：DROP TABLE IF EXISTS `test`.`user`; CREATE TABLE `test`.`user` ( `name` varchar(10.html) DEFAULT NULL, `password` varchar(10.html) DEFAULT NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1; -- 表中数据 -- 'user1', 'pwd1' -- 'user2', 'pwd2'User 实体类：public class User { private String name; private String pwd; public User() { } public void setName(String val.html) { this.name = val; } public void setPassword(String val.html) { this.pwd = val; } public String getName() { return name; } public String getPassword() { return pwd; } }测试类：package dbutiltest; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.util.List; import java.util.Map; import org.apache.commons.dbutils.DbUtils; import org.apache.commons.dbutils.QueryRunner; import org.apache.commons.dbutils.ResultSetHandler; import org.apache.commons.dbutils.handlers.ArrayHandler; import org.apache.commons.dbutils.handlers.ArrayListHandler; import org.apache.commons.dbutils.handlers.BeanHandler; import org.apache.commons.dbutils.handlers.BeanListHandler; import org.apache.commons.dbutils.handlers.ColumnListHan…

JDBC性能小贴
type: item
date: 2026-07-07T08:28:06+00:00
description: 引言本文翻译自 javarevisited。如需转载本文，请先参见文章末尾处的转载要求。本文收集了一些用于提升 JDBC 性能的方法。Java 应用或 JavaEE Web 应用的性能至关重要，尤其是数据库后端对整体应用性能的影响显著。你是否经历过 Java 应用响应缓慢的案例（例如处理一个简单的请求都要花费数秒时间用于数据库访问、分页或排序）？下面这些贴士或许能帮助提升 Java 应用的性能。它们非常简单，同时也适用于其他使用数据库作为后端存储的编程语言。这几个 JDBC 性能贴士虽然基础，但在实践中很多程序员经常忽略它们。当然，你也可以将本文视为数据库性能优化的一般性建议。贴士一：使用缓存统计应用中有多少数据库调用，并设法将它们减到最少。不管你信不信，性能问题的罪魁祸首大多数情况下是访问数据库的代码。因为连接到数据库需要建立连接（Connections），涉及往返的网络传输以及数据库系统后端的处理。如果你可以把数据缓存下来，这是减少数据库调用最好的方式。即使你的应用数据是完全动态的，短暂的缓存也可以节省很大的数据库往返传输开销。加速 Java 应用后，至少能减少 20-50% 的数据库调用。如果想找出数据库调用热点，仅仅把 DAO 层的每个 DB 调用记录到日志中就行；如果记录好每个线程进入和退出数据库访问的时间更好，它能告诉你一个调用究竟花了多少时间。贴士二：使用数据库索引检查数据库列（Columns）上是否有索引。如果你正做查询发现所花的时间比预想的要长，那么首先想到的是检查在列上（正在查询的 WHERE 子句中的那列）是否建立了索引。程序员常犯这个错误，有索引和没有索引在做查询时有巨大的差异。这条贴士在性能上至少能提速 100%，当然合适的索引更重要。太多的索引反而会减慢数据的插入和更新操作，因此使用索引的时候要小心。像 id、类别（category）、类（class）等字段上做索引是经常使用的场景。贴士三：使用 PreparedStatement使用 PreparedStatement 或者存储过程（Stored Procedure）执行查询时，PreparedStatement（预处理语句）比普通的 Statement 对象要快。因为数据库可以对查询语句做预处理和查询缓存计划。因此，总是使用参数化形式的预处理语句，例如：SELECT * FROM table WHERE id=?而不要使用字符串拼接的方式：SELECT * FROM table WHERE id='" + id + "'"虽然后者仍是一个预处理语句但不是参数化的，使用第二种查询方式在性能上没有任何优势。更多参考可以查看 JDBC 为什么要使用 PreparedStatement 而不是 Statement。贴士四：使用数据库连接池连接池用来存放数据库连接（Connection）。创建数据库连接是个比较慢的过程而且会耗很长时间，所以如果每个请求都要创建一个连接，那么显然响应时间将会更长。用连接池根据上游的流量及并发请求数创建适当数量的连接。即使连接池在开始的请求中创建连接、缓存连接会比较慢，但总体还是能减少不少开销。贴士五：使用 JDBC 批量更新使用 JDBC 批量更新操作能显著提升 Java 数据库应用的性能。你应该始终用批量处理来执行插入和更新操作，通过使用 Statement 或者 PreparedStatement 做批量查询，并用 executeBatch() 方法执行。贴士六：取消自动提交查询时设置 setAutoCommit(false.html)。默认 JDBC 连接自动提交模式是打开的，意味着每个独立的 SQL 语句都将在自己的事务中执行。然而你可以把 SQL 语句按组归到一个逻辑事务中去，这样通过调用 commit() 或 rollback() 要么提交要么回滚。试着运行相同数量的查询次数，对比一下使用自动提交和不使用自动提交时的性能区别有多大的差异性。总结这些 Java 数据库应用性能小提示看起来非常简单，很多高级 Java 程序员能熟练运用在生产级代码中，但我还看到很多 Java 程序员并没有在乎这些，直到他们发现 Java 应用变得很慢时。所以对于新手来说有必要记住并使用它。同时你还可以使用这些 Java 性能小提示作为代码审查机制的一个参考，在你写的 Java 应用是使用数据库作为后端存储的时候。以上就是关于如何改善 Java 应用程序性能的贴士，如果你有什么更好的提示不妨给我留言。说明：本文核心观点翻译自 2012 年的技术文章。虽然 JDBC 基础原理至今未变，上述优化策略仍然适用，但具体实现细节可能随 JDK 版本或数据库驱动更新而有所差异。