核心技术Ⅱ:数据库编程
关于JDBC
Java数据库连接(JDBC)API:
- 遗循了微软公司非常成功的ODBC模式(ODBC:微软提供为C语言访问数据库的一套编程接口)。JDBC和ODBC都基千同一个思想:根据API编写的程序都可以与驱动管理器进行通信,而驱动管理器则通过驱动程序与实际的数据库进行通信。
- 我们将JDBC包看作是一个用于“将SQL语句传递给数据库”的应用编程接口;
- 【参见:入门:Mybatis—关于JDBC】
JDBC的目标:
- 通过使用标准的SQL语句,甚至是专门的SQL扩展,程序员就可以利用Java语言开发访问数据库的应用,同时还依旧遵守Java语言的相关约定。
- 数据库供应商和数据库工具开发商可以提供底层的驱动程序。因此,他们可以优化各自数据库产品的驱动程序。
JDBC 驱动程序类型:
- 笫1类驱动程序:将JDBC翻译成ODBC, 然后使用一个ODBC驱动程序与数据库进行通信。
- 较早版本的Java包含一个这样的驱动程序:JDBC/ODBC桥,不过在使用这个桥接器之前需要对ODBC进行相应的部署和正确的设置。(Java 8已经不再提供JDBC/ODBC桥)
- 笫2类驱动程序:由部分Java程序和部分本地代码组成的,用于与数据库的客户端API进行通信。
- 在使用这种驱动程序之前,客户端不仅斋要安装Java类库,还需要安装一些与平台相关的代码。
- 第3类驱动程序:纯Java客户端类库,它使用一种与具体数据库无关的协议将数据库请求发送给服务器构件,然后该构件再将数据库请求翻译成数据库相关的协议。这简化了部署,因为平台相关的代码只位于服务器端。
- 第4类驱动程序:纯Java类库,它将JDBC请求直接翻译成数据库相关的协议。
JDBC 的典型用法:
- 传统的客户端/服务器模型:通常是在服务器端部署数据库,而在客户端安装富GUI程序。(在此模型中,JDBC驱动程序应该部署在客户端)
- 三层模型:客户端不直接调用数据库,而是调用服务器上的中间件层,由中间件层完成数据库查询操作。
结构化查询语言:即SQL【SQL,读作“ [ˈsiːkwəl] ”】
JDBC配置
- (驱动程序JAR文件):
- 需要准备对应的数据库驱动包:在运行访问数据库的程序时,需要将驱动程序的 JAR 文件包括到类路径中(编译时并不需要这个 JAR 文件)。
// 在从命令行启动程序时,只需要使用下面的命令: java -classpath driverPath:. ProgramName
- 注册驱动器类:
- 自动注册:许多JDBC的JAR文件会自动注册驱动器类,则可以跳过手动注册步骤。
- (包含“META-INF/services/java.sql.Driver”文件的 JAR 文件可以自动注册驱动器类)
- 自动注册对于遵循 JDBC4 的驱动程序是必须具备的特性。
- 手动注册:通过“DriverManager”来注册驱动器。
- 在 Java 程序中加载驱动器类:(注册驱动器的静态初始化器)
Class.forName("org.postgresql.Driver"); // force loading of driver class
- 设置“jdbc.drivers”属性:
- 命令行参数指定这个属性:
java -Djdbc.drivers=org.postgresql.Driver ProgramName
- 或,应用中通过调用设置系统属性:
System.setProperty("jdbc.drivers", "org.postgresql.Driver");
- 在这种方式中可以提供多个驱动器,用冒号将它们分隔开:
org.postgresq1.Driver:org.apache.derby.jdbc.ClientDriver
- 命令行参数指定这个属性:
- 在 Java 程序中加载驱动器类:(注册驱动器的静态初始化器)
- 自动注册:许多JDBC的JAR文件会自动注册驱动器类,则可以跳过手动注册步骤。
- 连接到数据库:
- 在代码中打开数据库连接:
String url = "jdbc:postgresql :COREJAVA"; String username = "dbuser"; String password = "secret"; Connection conn = DriverManager.getConnection(url, username, password);
- 驱动管理器遍历所有注册过的驱动程序,以便找到一个能够使用数据库 URL 中指定的子协议的驱动程序。
- 使用数据库配置文件(如“database.properties”)
jdbc.drivers=org.postgresql.Oriver jdbc.url=jdbc:postgresql:COREJAVA jdbc.username=dbuser jdbc.password=secret
- 在代码中打开数据库连接:
- 数据库驱动器名称:如:
org.apache.derby.jdbc.ClientDriver org.postgresql.Driver com.mysql.jbdc.driver
- 数据库URL:JDBCURL 的一般语法为:“jdbc:subprotocol:other_stuff”,其中subprotocol用于选择连接到数据库的具体驱动程序。
- URL包含各种与数据库类型相关的参数,例如“主机名”、“端口号”和“数据库名”。如:
jdbc:derby://1ocalhost:1527/COREJAVA;create=true jdbc:postgresq1:COREJAVA
示例:
try{
Class.forName("com.mysql.jbdc.driver");
Connection conn = DriverManager.getConnection("jdbc:mysql:///spring_day01","root","admin");
String sql = "select * from users where username = ?";
PrepareStatement pre = conn.prepareStatement(sql);
pre.setString(1,"小陈");
ResultSet rs = pre.executeQuery();
while(rs.next()){
String username = rs.getString("username");
String password = rs.getString("password");
User user = new User();
user.setUsername(username);
user.setPassword(password);
System.out.println(user);
}
}catch(Exception ex){
ex.printStackTrace();
}finally{
rs.close();
pre.close();
conn.close();
}
使用JDBC语句
执行SQL语句
执行语句:
- “executeUpdate”:返回受SQL语句影响的行数,或者对不返同行数的语句返回0。
- 既可以执行诸如“INSERT”、“UPDATE”和“DELETE”之类的操作,也可以执行诸如“CREATETABLE”和“DROPTABLE”之类的数据定义语句。
- “executeQuery”:返回一个“ResultSet”类型的对象,可以通过它来每次一行地迭代遍历所有查询结果。
- 执行“SELECT”查询时必须使用executeQuery方法。
- “execute”:可能会产生多个结果集和更新计数。
- 如果第一个执行结果是结果集,则返回 true;反之,返回 false。调用“getResultSet”或“getUpdateCount”方法可以得到第一个执行结果。
- 可以执行任意的SQL语句。(通常只用于由用户提供的交互式查询)
结果处理:分析结果集:
while (rs.next()) { String username = rs.getString("username"); String password = rs.getString("password"); int age = rs.getInt("age"); User user = new User(); user.setUsername(username); user.setPassword(password); user.setAge(age); System.out.println(user); }
- ResultSet接口的迭代协议与“java.util.Iterator”接口稍有不同:
- 对于ResultSet接口,迭代器初始化时被设定在第一行之前的位置,必须调用“next”方法将它移动到第一行。另外,它没有“hasNext”方法,我们需要不断地调用“next”,直至该方法返回 false 。
- 不同的数据类型有不同的访问器,比如“getString”和“getDouble”。
- 当 get 方法的类型和列的数据类型不一致时,每个 get 方法都会进行合理的类型转换。例如,调用“rs.getString("Price")”时,该方法会将 Price 列的浮点值转换成字符串。
- 每个访问器都有两种形式:
- 一种接受数字型参数。(列序号从1开始)如,“rs.getString(l)”返回的是当前行中第一列的值;
- 另一种接受字符串参数。如上例;
相关方法:
java.sql.DriverManager 1.1 | |
---|---|
static Connection getConnection(String url, String user, String password) | 建立一个到指定数据库的连接,并返回一个 Connection 对象。 |
java.sql.Connection 1.1 | |
Statement createStatement() | 创建一个 Statement 对象,用以执行不带参数的 SQL 查询和更新。 |
void close() | 立即关闭当前的连接,并释放由它所创建的JDBC资源。 |
java.sql.Statement 1.1 | |
ResultSet executeQuery(String sqlQuery) | 执行给定字符串中的 SQL 语句,并返回一个用于查看查询结果的 ResultSet 对象。 |
int executeUpdate(String sqlStatement) | 执行字符串中指定的“INSERT”、“UPDATE”或“DELETE”等 SQL 语句。还可以执行数据定义语言 (Data Definition Language, DDL) 的语句,如“CREATE TABLE”。返回受影响的行数,如果是没有更新计数的语句,则返回 0。 |
long executeLargeUpdate(String sqlStatement) 8 | |
boolean execute(String sql Statement) | 执行字符串中指定的 SQL 语句。可能会产生多个结果集和更新计数。如果第一个执行结果是结果集,则返回 true;反之,返回 false。调用“getResultSet”或“getUpdateCount”方法可以得到第一个执行结果。【见“多结果集”一节】 |
ResultSet getResultSet() | 返回前一条查询语句的结果集。如果前一条语句未产生结果媒,则返回 null 值。对于每一条执行过的语句,该方法只能被调用一次。 |
int getUpdateCount() | 返回受前一条更新语句影响的行数。如果前一条语句未更新数据库,则返回-1。对于每一条执行过的语句,该方法只能被调用一次。 |
1ong getLargeUpdateCount() 8 | |
void close() | 关闭 Statement 对象以及它所对应的结果集。 |
boolean isClosed() 6 | 如果语句被关闭,则返回true。 |
void closeOnCompletion() 7 | 使得一旦该语句的所有结果集都被关闭,则关闭该语句。 |
java.sql.ResultSet 1.1 | |
boolean next() | 将结果集中的当前行向前移动一行。如果已经到达最后一行的后面,则返同false。注意,初始请况下必须调用该方法才能转到第一行。 |
Xxx getXxx(int columnNumber) | 用给定的列序号或列标签返回或更新该列的值,并将值转换成指定的类型。列标签是SQL的AS子句中指定的标签,在没有使用AS时,它就是列名。 |
Xxx getXxx(String columnLabel)
(Xxx指数据类型, 例如“int”、“double”、“String”和“Date”等) | |
<T> T getObject(int columnIndex, Class<T> type) 7 | |
<T> T getObject(String columnLabel, Class<T> type) 7 | |
void updateObject(int columnIndex, Object x, SQLType targetSqlType) 8 | |
void updateObject(String columnLabel, Object x, SQLType targetSqlType) 8 | |
int findColumn(String columnName) | 根据给定的列名,返回该列的序号。 |
void close() | 立即关闭当前的结果集。 |
boolean isClosed() 6 | 如果该语句被关闭,则返回true。 |
Connection、Statement、ResultSet
- 每个“Connection”对象都可以创建一个或多个“Statement”对象。(1:N)
- 调用“Connection”类的“close”方法将关闭该连接上的所有语句。
- 同一个“Statement”对象可以用于多个不相关的命令和查询。 (1:N)
- 一个“Statement”对象最多只能有一个打开的结果集。(如果需要执行多个查询操作,且需要同时分析查询结果,那么必须创建多个 Statement对象)
- 如果“Statement”对象上有一个打开的结果集,那么调用“close”方法将自动关闭该结果。
- 在JavaSE 7,可以在“Statement”上调用“closeOnCompletion”方法,在其所有结果集都被关闭后,该语句会立即被自动关闭。
- 至少有一种常用的数据库(Microsoft SQL Server)的JDBC驱动程序只允许同时存在一个活动的Statement对象。
- 使用“DatabaseMetaData”接口中的“getMaxStatements”方法可以获取JDBC驱动程序支持的同时活动的语句对象的总数。
- 使用完“ResultSet”、“Statement”或“Connection”对象后,应立即调用“close”方法。
- 如果所用连接都是短时的,那么无需考虑关闭语句和结果集。只需将“close”语句放在带资源的try语句中,以便确保最终连接对象不可能继续保持打开状态:
try (Connection conn = . . .) { Statement stat = conn.createStatement(); ResultSet result = stat.executeQuery(queryString); // process query result }
分析SQL异常
SQL 异常按照层次结构树的方式组织到了一起:
- SQLException:异常
- 每个“SQLException”都有一个由多个“SQLException”对象构成的链,这些对象可以通过“getNextException”方法获取。
- 这个异常链是每个异常都具有的由“Throwable”对象构成的“成因”链之外的异常链,因此,需要用两个嵌套的循环来完整枚举所有的异常。JavaSE6后“SQLException”类实现了“Iterable<Throwable>”接口,其“iterator()”方法可以产生一个“Iterator<Throwable>”,这个迭代器可以迭代这两个链,首先迭代第一个“SQLException”的成因链,然后迭代下一个“SQLException”,以此类推。
- 使用下面这个改进的 for 循环:
for (Throwable t : sqlException) { // do something with t }
- 调用“getSQLState”和“getErrorCode”方法来进一步分析它:
- “getSQLState”将产生符合 X/Open 或 SQL:2003 标准的字符串(调用“DatabaseMetaData”接口的“getSQLStateType”方法可以查出驱动程序所使用的标准);
- 错误代码是与具体的提 供商相关的;
- SQLWarning:警告
- 数据库驱动程序可以将非致命问题作为警告报告,可以从“Connection”、“Statement”和“ResultSet”中获取这些警告。与SQL异常类似,警告也是串成链的。
- “SQLWarning”类是“SQLException”的子类。(但“SQLWarning”不会被当作异常抛出)
- 循环获取 SQLWarning:
SQLWarning w = stat.getWarning(); while (w != null) { // do something with w w = w.nextWarning(); }
- 调用“getSQLState”和“getErrorCode”来获取有关警告的更多信息;
- 当数据从数据库中读出并意外被截断时,SQLWarning的“DataTruncation”子类就派上用场了。如果数据截断发生在更新语句中,那么“DataTruncation”将会被当作异常抛出。【???】
相关方法:
java.sql.SQLException 1.1 | |
---|---|
SOLException getNextException() | 返同链接到该SQL异常的下一个SQL异常,或者在到达链尾时返回null。 |
Iterator<Throwabl e> iterator() 6 | 获取迭代器,可以迭代链接的SQL异常和它们的成因。 |
String getSQLState() | 获取“SQL状态”,即标准化的错误代码。 |
int getErrorCode() | 获取提供商相关的错误代码。 |
java.sql.SQLWarning 1.1 | |
SQLWarning getNextWarning() | 返回链接到该警告的下一个警告,或者在到达链尾时返回 null。 |
java.sql.Connection 1.1 | |
java.sql.Statement 1.1 | |
java.sql.ResultSet 1.1 | |
SQLWarning getWarnings() | 返回未处理警告中的第一个,或者在没有未处理警告时返回 null。 |
java.sql.DataTruncation 1.1 | |
boolean getParameter() | 如果在参数上进行了数据截断,则返回 true;如果在列上进行了数据截断,则返回 false。 |
int getIndex() | 返回被截断的参数或列的索引。 |
int getDataSize() | 返回应该被传输的字节数量,或者在该值未知的情况下返回 -1。 |
int getTransferSize() | 返回实际被传输的字节数量,或者在该值未知的情况下返回 -1。 |
执行查询操作
预备语句(PreparedStatement)
预备语句(prepared statement):准备一个带有宿主变量的查询语句,每次查询时只需为该变量填入不同的字符串就可以反复多次使用该语句。
- PreparedStatement技术改进了查询性能:每当数据库执行一个查询时,它总是首先通过计算来确定查询策略,以便高效地执行查询操作;通过事先准备好查询并多次重用它,我们就可以确保查询所需的准备步骤只被执行一次。
- 在预备查询语句中,每个宿主变批都用“?”来表示;
String publisherQuery = "SELECT Books.Price, Books.Title" + " FROM Books, Publishers" + " WHERE Books.Publisher _Id = Publishers. Publisher _Id AND Publishers. Name = ?"; PreparedStatement stat = conn.prepareStatement(publisherQuery);
- 在执行预备语句之前,必须使用“set”方法将变量绑定到实际的值上;
- (和 ResultSet 接口中的 get 方法类似,针对不同的数据类型也有不同的 set 方法)
- 想要重用已经执行过的预备查询语句,那么除非使用“set”方法或调用“clearParameters”方法,否则所有宿主变量的绑定都不会改变。
stat.setString(l, publisher); // 位置l表示第一个“?”
- 执行查询操作:
ResultSet rs = stat.executeQuery();
- 更新操作可以由“UPDATE”语句实现(不返回结果集),则应调用“executeUpdate”方法(返回值为被修改过的行数),而非“executeQuery”方法(返回查询的结果集);
通过拼接字符串来手动构建查询非常枯燥乏味,而且存在潜在的危险。 必须注意像引号这样的特殊宇符,而且如果查询中涉及用户的输入,那就还需要警惕注入攻击。 因此,只有查询涉及变量时,才应该使用预备语句。
【P.S.:Java字符串拼接】
在相关的Connection对象关闭之后,PreparedStatement对象也就变得无效了。 不过,许多数据库通常都会自动缓存预备语句。如果相同的查询被预备两次,数据库通常会直接重用查询策略。因此,无需过多考虑调用prepareStatement的开销。
相关方法:
java.sql.Connection 1.1 | |
---|---|
PreparedStatement prepareStatement(String sql) | 返回一个含预编译语句的PreparedStatement对象。字符串sql代表一个SQL语句,该语句可以包含一个或多个由?字符指明的参数占位符。 |
java.sql.PreparedStatement 1.1 | |
void setXxx(int n, Xxx x) | (Xxx指int 、 double 、 String、 Date之类的数据类型)设置第n个参数值为x。 |
void clearParameters() | 清除预备语旬中的所有当前参数。 |
ResultSet executeQuery() | 执行预备SQL查询,并返回一个ResultSet对象。 |
int executeUpdate() | 执行预备SQL语句“INSERT”、“UPDATE”或“DELETE”, 这些语句由 PreparedStatement 对象表示。该方法返回在执行上述语句过程中所有受影响的记录总数。如果执行的是数据定义语言(DDL)中的语句,如“CREATE TABLE”,则该方法返回0。 |
读写大对象(LOB)
除了数字、字符串和日期之外,许多数据库还可以存储大对象,例如图片或其他数据。在SQL中,二进制大对象称为BLOB,字符型大对象称为CLOB。
- 读取LOB:
- 读取LOB, 需要执行 SELECT语句。
- 在“ResultSet”上调用“getBlob”或“getClob”方法,这样就可以获得Blob或Clob类型的对象;
- 从Blob、Clob中获取数据:
- 从Blob中获取二进制数据,可以调用“getBytes”或“getBinaryStream”:
// 获取一张图像 . . . stat.set(1, isbn); try (ResultSet resu1t = stat.executeQuery()) { if (result.next()) { Blob coverBlob = result.getBlob(l); Image coverimage = ImageIO.read(coverBlob.getBinaryStream()); } }
- 从Clob获取字符数据,可以调用“getSubString”或“getCharacterStream”方法来获取其中的;
- 从Blob中获取二进制数据,可以调用“getBytes”或“getBinaryStream”:
- 写入LOB:
- 需要在 Connection 对象上调用“createBlob”或“createClob”,然后获取一个用于该 LOB 的输出流或写出器,写出数据,并将该对象存储到数据库中。
// 存储一张图像 Blob coverBlob = connection.createBlob(); int offset = O; OutputStream out = coverBlob.setBinaryStream(offset); ImageIO.write(coverImage, "PNG", out); PreparedStatement stat = conn.prepareStatement("INSERT INTO Cover VALUES (?, ?) "); stat.set(l, isbn); stat.set(2, coverBlob); stat.executeUpdate();
相关方法:
java.sql.ResultSet 1.1 | |
---|---|
Blob getBl ob(int col umnlndex) 1. 2 | 获取给定列的 BLOB 或 CLOB。 |
Blob getBlob(String columnlabel) 1.2 | |
Clob getClob(int columnlndex) 1.2 | |
Clob getClob(String columnlabel) 1.2 | |
java.sql.Blob 1.2 | |
1ong length() | 获取该 BLOB 的长度。 |
byte[] getBytes(long startPosition, long length) | 获取该 BLOB 中给定范围的数据。 |
InputStream getBinaryStream() | 返回一个输入流,用于读取该 BLOB 中全部或给定范围的数据。 |
InputStream getBinaryStream(long startPosition, long length) | |
OutputStream setBinaryStream(1ong startPosition) 1. 4 | 返回一个输出流,用于从给定位览开始写入该 BLOB。 |
java.sql.Clob 1.4 | |
1ong 1ength() | 获取该 CLOB 中的字符总数。 |
String getSubString(1ong startPosition, 1ong 1ength) | 获取该CLOB中给定范围的字符。 |
Reader getCharacterStream() | 返同一个读入器(而不是流),用于读取CLOB中全部或给定范围的数据。 |
Reader getCharacterStream(1ong startPosition, 1ong 1ength) | |
Writer setCharacterStream(1ong startPosition) 1. 4 | 返回一个写出器(而不是流),用于从给定位值开始写入该 CLOB。 |
java.sql.Connection 1.1 | |
Blob createBlob() 6 | 创建一个空的 BLOB 或 CLOB。 |
Clob createClob() 6 |
SQL 转义
“转义”语法是各种数据库普遍支持的特性,但是数据库使用的是与数据库相关的语法变体。因此,将转义语法转译为特定数据库的语法是JDBC驱动程序的任务之一。转义主要用于下列场景:
- 日期和时间字面常量
- (日期和时间字面常量随数据库的不同而变化很大)
- 使用“d”、“t”、“ts”来表示“DATE”、“TIME”和“TIMESTAMP”值:
{d '2008-01-24'} {t '23:59:59'} {ts '2008-01-24 23:59:59.999'}
- 调用标量函数
- 标量函数(scalar function)是指仅返回单个值的函数。在数据库中包含大量的函数,但是不同的数据库中这些函数名存在着差异。JDBC规范提供了标准的名字,并将其转译为数据库相关的名字。
- 要调用函数,需要像下面这样嵌入标准的函数名和参数:
{fn left(?, 20)} {fn user()}
- 调用存储过程
- 存储过程(stored procedure)是在数据库中执行的用数据库相关的语言编写的过程。
- 要调用存储过程,需要使用“call”转义命令,在存储过程没有任何参数时,可以不加括号。另外,应该用“=”来捕获存储过程的返回值:
{call PROCl(?, ?)} {call PROC2} {call ? = PROC3(?)}
- 外连接
- 两个表的外连接(outer join)并不要求每个表的所有行都要根据连接条件进行匹配。
SELECT * FROM {oj Books LEFT OUTER JOIN Pub1ishers ON Books.Pub1isher_Id = Pub1isher.Pub1isher_Id} -- 这个查询的执行结果中将包含有 Publisher_Id 在 Publishers 表中没有任何匹配的书,其中: -- 1、Publisher_ID 为“NULL”值的行,就表示不存在任何匹配; -- 2、如果应该使用“RIGHT OUTER JOIN”,就可以囊括没有任何匹配图书的出版商; -- 3、而使用“FULL OUTER JOIN”可以同时返回这两类没有任何匹配的信息。 -- 由于并非所有的数据库对于这些连接都使用标准的写法,因此需要使用转义语法。
- 在 LIKE 子句中的转义字符
- “_”和“%”字符在“LIKE”子句中具有特殊含义,用来匹配一个字符或一个字符序列。
- 如果想要匹配所有包含“_”字符的字符串,就必须使用下面的结构:
. . . WHERE ? LIKE %!_% {escape '!'} -- 这里我们将“!”定义为转义字符,而“!_”组合表示字面常量下划线。
多结果集(execute、getMoreResults)
在执行存储过程,或者在使用允许在单个查询中提交多个“SELECT”语句的数据库时,一个查询有可能会返回多个结果集。获取所有结果集的步骤:
- 使用“execute”方法来执行 SQL 语句;
- 获取第一个结果集或更新计数;
- 重复调用“getMoreResults”方法以移动到下一个结果集;
- 当不存在更多的结果集或更新计数时,完成操作。
- 由多结果集构成的链中的下一项:【???】
- 如果是结果集:“execute”和“getMoreResults”方法将返回true;“getUpdateCount”方法将返回-1。
- 如果是更新计数:“execute”和“getMoreResults”方法将返回false;“getUpdateCount”方法将返回计数值。
- 如果“getMoreResults”为false,且“updateCount”为-1,表示完成;
遍历所有的结果:(更新计数、结果集)
boo1ean isResult = stat.execute(command);
boolean done = false;
while (!done)
{
if (isResult)
{
ResultSet resu1t = stat.getResu1tSet();
// do something with result
}
else
{
int updateCount = stat.getUpdateCount();
if (updateCount >= 0)
// do something with updateCount
else
dnoe = true; // “getMoreResults”为false,且“updateCount”为-1,表示完成;
}
if (!done) isResult = stat.getMoreResults();
}
相关方法:
java.sql.Statement 1.1 | |
---|---|
boolean getMoreResults() | 获取该语句的下一个结果集,Current 参数是“CLOSE_CURRENT_RESULT”(默认值),“KEEP_CURRENT_RESULT”或“CLOSE_ALL_RESULTS”之一。如果存在下一个结果集,并且它确实是一个结果集,则返回 true。 |
boolean getMoreResults(int current) 6 |
获取自动生成的键
不同的提供商所提供的“对行自动编号”机制之间存在着很大的差异(mysql自增;Oracle中Sequence),这些自动编号的值经常用作主键。JDBC 提供了获取自动生成键的有效途径:当向数据表中插入一个新行,且其键自动生成时,代码来获取这个键:
stat.executeUpdate(insertStatement, Statement.RETURN_CENERATED_KEYS);
ResultSet rs = stat.getGeneratedKeys();
if (rs.next())
{
int key = rs.getInt(l);
...
}
相关方法:
java.sql.Statement 1.1 | |
---|---|
boolean execute(String statement, int autogenerated) 1.4 | 像前面描述的那样执行给定的 SQL 语句,如果“autogenerated”被设置为“Statement.RETURN_GENERATEO_KEYS”,并且该语句是一条“INSERT”语句,那么第一列中就是自动生成的键。 |
int executeUpdate(String statement, int autogenerated) 1.4 |
可滚动和可更新的结果集
可滚动的结果集 可更新的结果集
行集
构建行集 被缓存的行集
元数据
事务
用JDBC对事务编程 保存点 批量更新
高级SQL类型
JDBC支持的 SQL 数据类型以及它们在 Java 语言中对应的数据类型:
其中:
- “ARRAY”:SQLARRAY (SQL 数组)指的是值的序列。例如,Student 表中通常都会有一个 Scores 列,这个列就应该是 ARRAY OF INTEGER(整数数组)。“getArray”方法返回一个接口类型为“java.sql.Array”的对象,该接口中有许多方法可以用于获取数组的值。
- 从数据库中获得一个 LOB 或 数组 并不等于获取了它的实际内容,只有在访问具体的值时它们才会从数据库中被读取出来。(这对改善性能非常有好处,因为通常这些数据的数据量都非常大)
- “ROWID”:某些数据库支持描述行位值的“ROWID”值,这样就可以非常快捷地获取某一行值。JDBC 4 引入了“java.sql.RowId”接口,并提供了用于在查询中提供行 ID,以及从结果中获取该值的方法。
- “NCHAR(N),NVARCHAR(N),LONG NVARCHAR”:国家属性字符串(NCHAR及其变体)按照本地字符编码机制存储字符串,并使用本地排序惯例对这些字符串进行排序。JDBC4提供了方法,用于在查询和结果中进行 Java的 String 对象和国家属性字符串之间的双向转换。
- “NCLOB”:有些数据库可以存储用户自定义的结构化类型。JDBC3提供了一种机制用于将 SQL 结构化类型自动映射成 Java 对象。
- “SQLXML”:有些数据库提供用于 XML 数据的本地存储。 JDBC4 引入了SQLXML接口 , 它可以在内部的 XML 表示和 DOM 的 Source/Result 接口或二进制流之间起到中介作用。
Web与企业应用中的连接管理
- 【使用“database.properties”文件对数据库连接进行简单设置,这种方法适用于小型的测试程序,但是不适用于规模较大的应用。】
- 在Web或企业环境中部署JDBC应用时,数据库连接管理与“Java名字和目录接口”(JNDI)是集成在一起的:
- 遍布企业的数据源的属性可以存储在一个目录中,采用这种方式使得我们可以集中管理用户名、密码、数据库名和 JDBC URL:
Context jndiContext = new InitialContext(); DataSource source = (DataSource) jndiContext.lookup("java:comp/env/jdbc/corejava"); Connection conn = source.getConnection();
- 不再使用“DriverManager”,而是使用 JNDI 服务来定位数据源:
- 数据源(DataSource):就是一个能够提供简单的 JDBC连接和更多高级服务的接口,比如执行涉及多个数据库的分布式事务。(“javax.sql”标准扩展包定义了“DataSource”接口)
- 在 Java EE 的容器中,甚至不必编程进行 JNDI 查找,只常在 DataSource 域上使用 Resource 注解,当加载应用时,这个数据源引用将被设置:
@Resource (name= "jdbc/ corej ava") private DataSou rce source;
- 数据库连接池(pool):数据库连接在物理上并未被关闭,而是保留在一个队列中并被反复重用。
- 通过获取数据源并调用“getConnection”方法来得到连接池中的连接。使用完连接后,需要调用“close”方法。该方法并不在物理上关闭连接,而只是告诉连接池已经使用完该连接。连接池通常还会将池机制作用于预备语句上。