JDBC 设置 PostgreSQL 查询中 any(?) 的参数

这段时间都纠缠于 Java 如何操作 PostgreSQL 数据库上,千方百计的为求得更好的性能。为此我们用上了 Batch, 或用 id = any(?) 这种更 PostgreSQL 化的数组参数操作。其实它还有更多数组方面的花样可以玩,毕竟 PostgreSQL 数据库有一种广纳百川的胸怀,总有好的新特性能在 PostgreSQL 中首先体验到。

回到之前的一篇 postgres in (?,?) 和 =any(?) 用法/性能对比,其中关于如何向查询语句中 id = any(?) 占位符传入数组参数的代码是

在 PreparedStatement(PgPreparedStatement) 中设置数组参数的函数是用 阅读全文 >>

postgres in (?,?) 和 =any(?) 用法/性能对比

刚刚回顾了一下 JDBC 操作 SQL Server 时如何传入列表参数,即如何给 in (?) 条件直接传入一个列表参数,然而本质上是不支持,最终不得不展开为 in (?, ?,...?) 针对每个元素单独设置参数,不定长的参数对于重用已编译 PreparedStatement 语句的帮助不大。

那么 JDBC 操作 PostgreSQL 是何种状态呢?展开为多个参数当然是有效的。继续尝试 Spring 提供的 NamedParameterJdbcTemplate 的操作方式

String query = "select * from users where id in (:ids)";
Map<String, Object> parameters = new HashMap<>();
parameters.put("ids", IntStream.rangeClosed(1, 5).boxed().collect(toList()));
List<Map<String, Object>> maps = namedParameterJdbcTemplate.queryForList(query, parameters);

执行后查看到实际执行的语句是

select * from users where id in (?, ?, ?, ?, ?)

阅读全文 >>

JDBC 连接串中指定当前 schema(含 Oracle, DB2, PostgreSQL 和 SQLServer)

现在流行数据都有 Schema 的概念,一般作为数据库对象(表,函数,存储过程等)的命名空间。所以在数据库端往往存在 实例/数据库/Schema 这样层级划分。对于 DB2 和 Oracle 用客户端创建一个新的数据库并非易事,灵活的在数据库中较轻量的划分隔离空间的办法因数据库类型而异

  1. MySQL:  创建数据库(create database), create schema 是 create database 的别名
  2. PostgreSQL: create database 创建新的数据库,或在当前数据库下用 create schema 创建 schema
  3. SQLServer: 和 PostgreSQL 一样的自由,create database 创建新的数据库,或在当前数据库下用 create schema 创建 schema
  4. DB2: 用 create schema 创建新的 schema, 或创建数据库对象时直接加上前缀,create table abc.test1..., 没有 abc schema 则会自动创建
  5. Oracle: create schema 较麻烦,涉及到 authorization. 但可以通过 create user 创建新用户后就有了对应的新 schema

下面我们来了解下在 PostgreSQL/SQLServer 中创建新的 schema,如何在 JDBC 连接字串中指定默认 schema, 同时也涉及到 database/schema/user 的创建以及在 SQL 中如何切换。 阅读全文 >>

Java 直接插入 CLOB/BLOB 数据到 Oracle 数据库

向数据库中插入 CLOB 或 BLOB 类型的数据,Oracle 总是比其他类型的数据库操作上要麻烦多了。当然,对于不大于 4K 长度的 CLOB 字符串在 JDBC 中可简单的用 PreparedStatement.setString(idx, "short string") 。如果要插入大于 4K 长度的内容,网上找来的例子许多都是分两步走

  1. 先插入 EMPTY_CLOB() 或 EMPTY_BLOB()
  2. 然后 SELECT 原来的记录 FOR UPDATE, 再更新先前插入的记录

这就存在两个问题,含 CLOB/BLOB 的表必需要有主键,还有因为 FOR UPDATE 的使用我们需要开启事物,不能采用自动提交。

其实还有更简单的方法可直接插入大的 CLOB/BLOB 数据,要用到 Oracle JDBC 驱动的 setStringForClob(),  CLOB.createTemporary(), 或 BLOB.createTemporary() 方法。来看下面的例子,例子中只演示 CLOB, 类似的方法可应用于 BLOB, NCLOB。

本文中所使用的 Oracle JDBC 驱动比较老,是 ojdbc:ojdbc:5。Docker 启动一个本地的 Oracle 11G 作为测试数据库

$ docker run -d -p 1521:1521 -p 8080:8080 wnameless/oracle-xe-11g-r2

默认的 SID 是 xe, 数据库用户名和密码分别是 system/oracle 阅读全文 >>

Logging tool for JDBC

jdbcdslog 是一款知名用来跟踪 JDBC 的全功能工具.

特征包括:
- 可挂接到数据库连接, 驱动或数据源上(XA, 连接池)
- 可记录 PreparedStatement 和 CallableStatement 调用所绑定的参数
- 可记录 ResultSet 对象中的查询记录行
- 可记录查询执行时间
- 支持多种日志引擎 (感谢 SLF4J, 你可以使用多数流行的日志组件库, 像 log4j, apache common logging, java logging 或者简单的输出到 System.out) 阅读全文 >>

又是 Oracle JDBC 驱动版本太低引发的错误-rs.getByte() 的差异

前面写过一个因为 Oracle 驱动版太低引起的一在题:Oracle 驱动版本引起的显示字段奇怪编码问题。今天又遇着一个。

本地写好的一段连接数据库的程序,一放到正式服务器上又不能正常工作了,原来的代码中没有打印出异常栈,都看不出什么问题来,后来加上异常栈输出,得到如下信息:

Fail to convert to internal representation
 java.sql.SQLException: Fail to convert to internal representation
        at oracle.jdbc.dbaccess.DBError.throwSqlException(DBError.java:114)
        at oracle.jdbc.dbaccess.DBError.throwSqlException(DBError.java:156)
        at oracle.jdbc.dbaccess.DBError.throwSqlException(DBError.java:219)
        at oracle.jdbc.driver.OracleStatement.getLongValue(OracleStatement.java:3022) 阅读全文 >>

JDBC 连接 Oracle 时,用 rs.absolute(n) 真的不如 n 次 next() 性能好

前面写过一篇:Oracle 驱动版本引起的显示字段奇怪编码问题。讲到因 Oracle 8.0.5 不支持子查询排序,为改善原来那种每次翻页时都捋出所有数据成对象到 List 中,然后从中拣取页面实际要显示的记录的性能问题时,采用了 rs.absolute() 直接跳到起始记录游标的方法,但又引入了乱码问题,例如:"无效",变成了 "0xE697A0E69588"。

虽说,换个驱动,如 8.1.7.0.0 以上版本的驱动就能解决乱码的问题,但这一换又怕会影响到其他的应用。有朋友评论说,其实循环 next() 到某处比 absolute() 定位要好,乍一看,有些牵强,不过试试就知道了。下面就来做样一个测试,测试代码如下: 阅读全文 >>

Oracle 驱动版本引起的显示字段奇怪编码问题

开门见山把产生问题的原因的解决办法列出来。

我们一般获取 Statement 都是通过 conn.createStatement() 方法,很少传递参数给它的,所以其内置属性都取默认值的,取记录只用 while(rs.next()) 逐个取即可。然而有一个需求(Oracle 8i 之前的版本不支持子查询排序,所以无法用 rownum 取分页记录) 是通过如下代码来得到 Statement:

Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_READ_ONLY);

由它获得的结果集可以 rs.absolute(n) 直接跳到第 n 行记录来获得值,但就这个用法出问题了,取出来的中文出现乱码了,如 "无效",变成了 "0xE697A0E69588" 阅读全文 >>

WAS 安全性中定制用注册表,通过 JDBC 验证用户实现 SSO

通常 Websphere Application Server (WAS) 都是结合 LDAP 来对用户对行验证,实现单点登陆的。因为 LDAP 有着一个得天独厚的优势,它对查询进行了优化,因此 WAS 理所当然的提供了对 LDAP 十分完善的支持。从 WAS 控制台进 安全性->用户注册表->LDAP,在类型里可以看到支持 IBM_Directory_Server、SecureWay、Sun ONE、Domino、Active_Directory、eDirectory 还可定制 LDAP 连接。

好,就此打住,我在这里要介绍的是如何配置 WAS 控制台和应用通过 JDBC 来验证用户。这一想法产生的背景是:公司原所有系统是通过 Portal 做的集成,配置 LDAP 便能实现 SSO,但有一个新的项目暂不能通过 LDAP 来验证,但也要能实现 SSO,于是就意思到要用 JDBC 来进行用户验证。你也许已注意到在 安全性->用户注册表 下除了 本地 OSLDAP 外,还有一个 定制。我们就是要在这个 "定制" 上做文章的。下面详细具体步骤。 阅读全文 >>