(1)在执行SQL命令之前,需要创建一个Statement对象。要创建Statement对象,需要使用DriverManager.getConnection方法所获得的Connection对象。
e.g.Statements stmt = conn.createStatement();
(2)将要执行的SQL语句放入字符串中
e.g. String command = "UPDATE Books SET price = price - 5.00 WHERE title NOT LIKE '%introduction%' "
(3)调用Statements类中的executeUpdate方法
e.g. stmt.executeUpdate(command);
executeUpdate方法返回受SQL命令影响的行数,或者对于不返回行数的语句返回0。
注意:
1)executeUpdate方法可以即可执行INSERT,UPDATE和DELETE之类的操作,也可执行CREATE TABLE和DROP TABLE之类的数据定义语句。
2)执行SELECT方法必须使用executeQuery方法
3)execute方法可以执行任意SQL语句,此方法通常用于用户提供的交互式查询。
(4)查询结果的遍历
1)通过executeQuery方法返回一个ResultSet对象,可以通过它来每次一行的迭代遍历所有结果。
e.g. ResultSet rs = stmt.executeQuery("SELECT * FROM table");
2)分析结果集时通常使用类似如下的循环语句代码:
e.g. while(rs.next()){
look at a row of result set
}
结果集中顺序是任意的。除非使用ORDER BY子句进行指定行的顺序,否则不能为行序列强加任何意义。
注意 :ResultSet方法与java.util.Iterator方法不同,对于ResultSet类,迭代器初始化时被设定为在第一行之前的位置,必须调用next方法将它移动到第一行。并且ResultSet类没有hasNext方法,需要不断调用next方法,直到返回false。
3)通过访问器(accessor)方法获取每一行的信息,不同的数据类型有不同的访问器,比如getString和getDouble。
e.g. String bookname = rs.getString(1);
double price = re.getDouble("price");
每个访问器都有两种形式,一种接受数字参数,一种接受字符串参数。当使用数字参数时,是指所对应的列;当使用字符串参数时,指的是结果集中以该字符串为列名的列。
注意:使用数字参数效率更高一些,但使用字符串参数可以使代码易于阅读和维护。当get方法的类型和列的数据不一致时,每个get方法都会进行合理的类型转换,但当类型无法强转的时候会抛出java.sql.SQLException异常。例如,调用rs.getString("price")时,该方法会将price列的浮点值转换成字符串。
4.1 管理连接、语句和结果集
一个Connection对象都可以创建一个或多个Statement对象,同一个Statement对象可以用于多个不相关的命令和查询。但是,一个Statement对象最多只能打开一个结果集。如果需要执行多个查询操作,且需要同时分析查询结果,那么必须创建多个Statement对象。
至少有一种常用的数据库(SQLServer)的JDBC驱动程序只允许同时存在一个激活的Statements对象。使用DatabaseMeteData中的getMaxStatments方法,获取JDBC程序同时支持的语句对象的总数。
e.g. conn.getMetaData().getMaxStatements();
实际上应该通过组合查询,就可以只分析一个结果。对数据库进行组合查询比用JAVA程序遍历多个结果集要更高效。
当使用完ResultSet,Statment或Connection对象时,应该立即调用close方法,这些对象都使用了规模较大的数据结构,所以不应该等待垃圾回收器来处理它们。
注意 : 如果Statements对象上有一个打开的结果集,那么调用close方法将自动关闭该结果集。同样Connection类的close方法将关闭该连接上的所有语句。
如果所有连接都是短时的,那么无需考虑关闭语句和结果集。只需要将close语句放finally块中,以便确保最终关闭连接对象。
try{
Connection conn = ...;
try{
Statement stmt = conn.createStatement();
ResultSet result = stmt.executeQuery(querySting);
process query result...
}finally{
conn.close();
}
}catch(SQLException e){
handle exception
}
使用try/finally块关闭连接,而使用一个单独的try/catch块处理一次。分割try程序块可以提高代码的可读性和可维护性。
4.2 分析SQL异常
(1)每个SQLException都有一个由多个SQLException对象构成的链,这些对象可以通过getNextException方法获取。这个异常链是每个异常都具有由Throwable对象构成的“成因”链之外的异常链,因此需要两个嵌套的循环来完全枚举所有的异常。
Java SE 6改进了SQLException,实现了Iterator<Throwable>接口,调用iterator()方法产生迭代器,这个迭代器可以迭代这两个链。首先迭代第一个SQLException的成因链,然后迭代下一个SQLException。
e.g. for(Throwable t : sqlException){
do someting whit t
}
(2)getSQLState/getErrorCode : 可以通过调用SQLException上的getSQLState和getErrorCode方法来进一步分析它。
1)getSQLState方法将产生符合X/Open和SQL:2003标准的字符串,比如42X01(调用DatabaseMetaData的方法getSQLStateType可以查出驱动程序所使用的标准)。
2)getErrorCode方法返回的错误代码是提供商相关的,比如Derby中的-1。
(3)Java SE 6开始,SQL异常按照层次结构树的方式组织到了一起,这使得我们可以按照提供商无关的方式来捕获具体的错误类型。
另外,数据库驱动程序可以将非致命问题作为警告报告,我们可以从连接、语句和结果集中获取这些警告。
(4)SQLWarning类是SQLException类的子类,且SQLWarning类不会被当做异常抛出,可以通过调用getSQLState和getErrorCode来获取有关警告的信息。与SQL异常类似,警告也是成链的。
e.g.获取所有的警告
SQLWarning w = stmt.getWaring();
while(w!=null){
do something with w
w = w.getNextWarning();
当数据从数据库中读出并意外被截断时,SQLWarning的DataTruncation子类将会被当做异常抛出。
4.3 一个读取SQL指令,并执行的DEMO
该DEMO操作步骤
(1)根据database.properties中属性信息创建Connection
(2)使用SQL命令打开文件,如果未提供任何文件名,则在控制台中提示用户输入命令。
(3)使用通用的execute执行sql命令,如果返回true,说明产生结果集。每个SQL文件均在最后执行一个SELECT *语句,这样可以看到是否插入数据。
(4)如果产生结果集则打印结果集。
(5)如果运行中出现SQL异常,则打印这个异常以及所有可能包含在其中的与其链接在一起的相关异常。
(6)关闭数据库连接。
package jdbc.readsql;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
import java.util.Scanner;
public class ExecSQL {
public static void main(String[] args) {
try {
Scanner in;
if (args.length == 0) {
in = new Scanner(System.in);
} else {
in = new Scanner(new File(args[0]));
}
Connection conn = getConnection();
try {
Statement stat = conn.createStatement();
while (true) {
if (args.length == 0) {
System.out.println("Enter command or exit to exit");
if (!in.hasNext()) {
return;
}
String line = in.nextLine();
if ("exit".equalsIgnoreCase(line)) {
return;
}
if (line.trim().endsWith(";")) {
line = line.trim();
line = line.substring(0, line.length() - 1);
}
try {
boolean hasResultSet = stat.execute(line);
if (hasResultSet) {
showResultSet(stat);
}
} catch (SQLException e) {
for (Throwable t : e) {
t.printStackTrace();
}
}
}
}
} finally {
conn.close();
}
} catch (SQLException se) {
se.printStackTrace();
} catch (IOException ie) {
ie.printStackTrace();
}
}
private static Connection getConnection() throws IOException, SQLException {
Properties param = new Properties();
FileInputStream in = new FileInputStream(System.getProperty("user.dir")
+ "/bin/database.properties");
param.load(in);
in.close();
String drivers = param.getProperty("jdbc.drivers");
if (drivers != null && !"".equals(drivers)) {
System.setProperty("jdbc.drivers", drivers);
}
// Class.forName(drivers);
String url = param.getProperty("jdbc.url");
String username = param.getProperty("jdbc.username");
String password = param.getProperty("jdbc.password");
return DriverManager.getConnection(url, username, password);
}
private static void showResultSet(Statement stat) throws SQLException {
ResultSet result = stat.getResultSet();
ResultSetMetaData metaData = result.getMetaData();
int columnCount = metaData.getColumnCount();
for(int i=1;i<=columnCount;i++){
if(i>1){
System.out.print(",");
System.out.print(metaData.getColumnLabel(i));
}
}
System.out.println();
while(result.next()){
for(int i=1;i<=columnCount;i++){
if(i>1){
System.out.print(",");
System.out.print(result.getString(i));
}
}
System.out.println();
}
result.close();
}
}