写点什么

数据库 JDBC:Statement 查询

发布于: 2020 年 10 月 31 日
数据库JDBC:Statement查询

本文基于PostgreSQL创建一个shop数据库,

CREATE DATABASE shop
WITH
OWNER = postgres
ENCODING = 'UTF8'
LC_COLLATE = 'Chinese (Simplified)_China.936'
LC_CTYPE = 'Chinese (Simplified)_China.936'
TABLESPACE = pg_default
CONNECTION LIMIT = -1;

再创建一个Product表插入一些数据,为后面JDBC的Statement执行创建数据:

-- 创建Product表
CREATE TABLE Product(
product_id CHAR(4) NOT NULL,
product_name VARCHAR(100) NOT NULL,
product_type VARCHAR(32) NOT NULL,
sale_price INTEGER DEFAULT 0,
purchase_price INTEGER,
regist_date DATE,
PRIMARY KEY(product_id)
);
-- 插入数据
BEGIN TRANSACTION;
INSERT INTO Product VALUES ('0001', 'T恤衫', '衣服', 1000, 500, '2009-09-20');
INSERT INTO Product VALUES ('0002', '打孔器', '办公用品', 500, 320, '2009-09-11');
INSERT INTO Product VALUES ('0003', '运动T恤', '衣服', 4000, 2800, NULL);
INSERT INTO Product VALUES ('0004', '菜刀', '厨房用具', 3000, 2800, '2009-09-20');
INSERT INTO Product VALUES ('0005', '高压锅', '厨房用具', 6800, 5000, '2009-01-15');
INSERT INTO Product VALUES ('0006', '叉子', '厨房用具', 500, NULL, '2009-09-20');
INSERT INTO Product VALUES ('0007', '擦菜板', '厨房用具', 880, 790, '2008-04-28');
INSERT INTO Product VALUES ('0008', '圆珠笔', '办公用品', 100, NULL,'2009-11-11');
COMMIT;

下面可以开始创建JDBC连接,然后执行查询语句。

import java.sql.Connection; // 负责连接数据库
import java.sql.DriverManager; // 负责JDBC驱动
import java.sql.ResultSet; // 负责保存SQL语句的执行结果
import java.sql.Statement; // 负责存储和执行SQL语句
import java.sql.SQLException;
public class Hello {
public static void main(String[] args) {
/* 1) PostgreSQL的连接信息 */
String url = "jdbc:postgresql://localhost:5432/shop";
String user = "postgres";
String password = "a";
try {
/* 2) 定义JDBC驱动 */
Class.forName("org.postgresql.Driver");
/* 3) 连接PostgreSQL */
Connection con = DriverManager.getConnection(url, user, password);
Statement st = con.createStatement();
/* 4) 执行SELECT语句 */
ResultSet rs = st.executeQuery("SELECT 1 AS val;");
/* 5) 在画面中显示结果 */
rs.next();
String val = rs.getString("val");
System.out.println("val = " + val);
/* 6) 切断与PostgreSQL的连接*/
rs.close();
st.close();
con.close();
} catch(ClassNotFoundException e) { //驱动出现异常
System.out.println("Got a ClassNotFoundException: " + e.getMessage());
e.printStackTrace();
} catch (SQLException e) {
System.out.println("Got a SQLException: " + e.getMessage());
e.printStackTrace();
}
}
}

上述代码,在断开连接阶段,若不断开连接,可能会引起内存泄漏(memory leak)问题,虽然Java的垃圾回收功能可以自动释放掉无用连接以及对象占用的内存,防止发生内存不足的问题,但是,该功能还是无法百分之百地防止内存泄漏,所以显式地通过编码断开连接很重要。


JDBC提供了三种Statement接口来执行SQL语句,分别是executeQuery、executeUpdate、execute,在实际的使用中使用哪种方法取决于SQL语句生成的内容。

查询单个结果集

executeQuery用于产生单个结果集(ResultSet)的语句,例如常见的SELECT语句。

查看结果集

ResultSet接口中含有几十种从当前行获取数据的方法,每个可能的数据类型都有一个 get 方法,并且每个 get 方法需要列名和列索引两个版本,假设数据列包含int类型,ResultSet可以调用getInt()方法:

// 返回当前行中名为columnName的列的int值
public int getInt(String columnName) throws SQLException
// 返回当前行中指定列的索引int值。列索引从1开始,意味着行中的第一列是1 ,第二列是2 ,以此类推。
public int getInt(int columnIndex) throws SQLException

例如,实现查询Product表中sale_price列的值。

ResultSet rs = st.executeQuery("SELECT * FROM Product;");
while (rs.next()) {
// 当前光标指向的行数
int row = rs.getRow();
// 获取当前行的数据,也可以使用getInt(colName)方法
String val = rs.getString("sale_price");
System.out.println("row = " + row + " val = " + val);
}

光标移动

在实际执行过程中,我们也可以移动光标的位置实现对查看不同行的结果,ResultSet也提供了一系列移动光标的位置的相关接口。

// 将光标移动到下一行,如果是结果集的最后一行则返回false
public boolean next() throws SQLException
// 返回当前光标指向的行数的值
public int getRow() throws SQLException
// 将光标移动到第一行之前
public void beforeFirst() throws SQLException
// 将光标移动到第一行
public boolean first() throws SQLException
// 将光标移动到最后一行之后
public void afterLast() throws SQLException
// 将光标移动到最后一行
public void last() throws SQLException
// 将光标移动到指定的第row行
public boolean absolute(int row) throws SQLException
// 将光标移动到当前指向的位置往前或往后第 row 行的位置
public boolean relative(int row) throws SQLException
// 将光标移动到上一行,如果超过结果集的范围则返回 false
public boolean previous() throws SQLException
// 将光标移动到结果集中指定的行,可以在数据库中插入新的一行。当前光标位置将被记住
public void moveToInsertRow() throws SQLException
// 如果光标处于插入行,则将光标返回到当前行,其他情况下,这个方法不执行任何操作
public void moveToCurrentRow() throws SQLException

假设我们将光标位置移动到最后一行,查询改行的数据,之后再实现对所有的sale_price列数据的查询。

/*
指定ResultSet类型,默认使用TYPE_FORWARD_ONLY,光标只能向前移动
TYPE_SCROLL_INSENSITIVE类型,光标可以向前和向后移动。当结果集创建后,其他人对数据库的操作不会影响结果集的数据。
*/
Statement st = con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_READ_ONLY);
ResultSet rs = st.executeQuery("SELECT * FROM Product;");
// 将光标位置移动到最后一行
rs.last();
int row = rs.getRow(); // 当前光标指向的行数
String val = rs.getString("sale_price");
rs.next();
System.out.println("row = " + row + " val = " + val);
System.out.println("---------------------------------------");
// 将光标位置移动到第一行之前
rs.beforeFirst();
// 查询所有的sale_price列数据并进行打印
while (rs.next()) {
row = rs.getRow();
val = rs.getString("sale_price");
System.out.println("row = " + row + " val = " + val);
}

执行的结果如下:

row = 8 val = 100
---------------------------------------
row = 1 val = 1000
row = 2 val = 500
row = 3 val = 4000
row = 4 val = 3000
row = 5 val = 6800
row = 6 val = 500
row = 7 val = 880
row = 8 val = 100

更新结果集

ResultSet 接口包含了一系列不同数据类型的更新方法,该方法用于更新结果集中的数据,有需要列名和需要列索引的两种更新方法来更新任一数据类型,以int类型为例:

// 将指定列的字符串的值改为i
public void updateInt(int columnIndex, int i) throws SQLException
// 用列名来指定的列将其更新为i
public void updateInt(String columnName, int i) throws SQLException

更新结果集中的行将改变当前行的列中的 ResultSet 对象,而不是基础数据库中的数据。要更新数据库中一行的数据,你需要调用以下的任一方法-

// 通过更新数据库中相对应的行来更新当前行
public void updateRow()
// 从数据库中删除当前行
public void deleteRow()
// 在结果集中刷新数据,以反映数据库中最新的数据变化
public void refreshRow()
// 取消对当前行的任何修改。
public void cancelRowUpdates()
// 在数据库中插入一行,只有在光标指向插入行的时候才能被调用
public void insertRow()

现在,假设实现将第三行的sale_price的值更新为1800,并查新更改后的值,相关实现如下:

// 更新数据需要将并发类型更改为CONCUR_UPDATABLE,否则,默认值为CONCUR_READ_ONLY
Statement st = con.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,
ResultSet.CONCUR_UPDATABLE);
ResultSet = st.executeQuery("SELECT * FROM Product;");
// 将光标移动到第三行
rs.relative(3);
// 将当前行的sale_price更新为1800
rs.updateInt("sale_price", 1800);
// 更新数据库中当前行,若不更新,查询到的将会是更新之前的数据
rs.updateRow();
int row = rs.getRow();
String val = rs.getString("sale_price");
rs.next();
System.out.println("row = " + row + " val = " + val);

执行结果为:

row = 3 val = 1800




修改零或多行中的一列或多列

executeUpdate 的返回值是一个整数,指示受影响的行数(即更新计数)。用于执行 INSERT、UPDATE 或 DELETE 语句以及DDL语句,例如 CREATE TABLE 和 DROP TABLE等不操作行语句,返回值总为0;

INSERT、UPDATE 或 DELETE 语句的效果是修改表中零行或多行中的一列或多列。

例如,向Product表中最后一行插入数据,并进行查询。

Statement st = con.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,
ResultSet.CONCUR_UPDATABLE);
// 在最后一行插入一行数据
int affectedRow = st.executeUpdate("INSERT INTO Product VALUES ('9', '圆珠笔', '办公用品', 100, NULL,'2009-11-11');");
ResultSet rs = st.executeQuery("SELECT * FROM Product;");
// 将光标移动至最后一行
rs.last();
int row = rs.getRow();
String val = rs.getString("sale_price");
rs.next();
System.out.println("row = " + row + " val = " + val);

执行结果:

row = 9 val = 100



通用执行语句

execute方法可用于执行任何SQL语句,并返回一个boolean值,表明执行该SQL语句是否返回了ResultSet。如果执行后第一个结果是ResultSet,则返回true,否则返回false。通常使用executeQuery或executeUpdate就可以,如果不清楚SQL语句的类型时,可以使用便可以使用execute方法来执行该SQL语句,使用示例如下:

Statement st = con.createStatement();
String sql = "SELECT * FROM Product;";
// 执行SQL,返回boolean值表示是否包含ResultSet
boolean hasResultSet = st.execute(sql);
if (hasResultSet) { //如果执行后有ResultSet结果集
ResultSet rs = st.getResultSet();
ResultSetMetaData rsmd = rs.getMetaData();
//ResultSetMetaData是用于分析结果集的元数据接口
int columnCount = rsmd.getColumnCount();
while (rs.next()){//输出ResultSet对象
for (int i = 0 ; i < columnCount ; i++ ) {
System.out.print(rs.getString(i + 1) + "\t");
}
System.out.print("\n");
}
rs.close();
} else {
System.out.println("该SQL语句影响的记录有" + st.getUpdateCount() + "条");
}

执行结果:

0001 T恤衫 衣服 1000 500 2009-09-20
0002 打孔器 办公用品 500 320 2009-09-11
0005 高压锅 厨房用具 6800 5000 2009-01-15
0006 叉子 厨房用具 500 null 2009-09-20
0007 擦菜板 厨房用具 880 790 2008-04-28
0008 圆珠笔 办公用品 100 null 2009-11-11
0003 运动T恤 衣服 1800 2800 null
0004 菜刀 厨房用具 1800 2800 2009-09-20
9 圆珠笔 办公用品 100 null 2009-11-11




参考资料



发布于: 2020 年 10 月 31 日阅读数: 52
用户头像

正向成长 2018.08.06 加入

想要坚定地做大规模数据处理(流数据方向),希望结合结合批处理的传统处理方式,以及之后流批混合处理方向进行学习和记录。

评论

发布
暂无评论
数据库JDBC:Statement查询