JDBC2.0操作
之前介绍的大部分操作都是数据最基本的JDBC
操作,为了方便操作者进行数据库的开发,在JDBC2.0
提供了许多更加方便的操作。包括可滚动的结果集合和使用结果集直接更新数据库。如果要使用这样的特性必须依靠ResultSet
,JDBC2.0
对ResultSet
的新支持如下表所示。
序号 | 常量 | 描述 |
---|---|---|
1 | static int TYPE_FORWARD_ONLY |
表示指针只能向前移动的ResultSet,是默认值 |
2 | static int TYPE_SCROLL_SENSITIVE |
表示ResultSet可以滚动,可以更新内容 |
3 | static int TYPE_SCROLL_INSENSITIVE |
表示ResultSet可以滚动,但是不能更新内容 |
4 | static int CONCUR_READ_ONLY |
以只读方式打开数据库 |
5 | static int CONCUR_UPDATABLE |
表示ResultSet可更新 |
序号 | 方法 | 描述 |
---|---|---|
1 | boolean absolute(int row) |
将光标移动到此 ResultSet对象中的给定行号。 |
2 | void afterLast() |
将光标移动到这个 ResultSet对象的最后一行之后。 |
3 | void beforeFirst() |
将光标移动到这个 ResultSet对象的第一行之前。 |
4 | boolean first() |
将光标移动到此 ResultSet对象中的第一行。 |
5 | boolean last() |
将光标移动到此 ResultSet对象中的最后一行。 |
6 | boolean previous() |
将光标移动到此 ResultSet对象中的上一行。 |
7 | void updateString(int columnIndex, String x) |
根据列号,使用x更新列的内容,此方法被重载多次,支持各种数据类型。 |
8 | void updateString(String columnLabel, String x) |
使用String值x更新指定列名的内容。此方法被重载多次,支持各种数据类型。 |
9 | void moveToInsertRow() |
将光标移动到插入行。 |
10 | void updateRow() |
使用此 ResultSet对象的当前行的新内容更新底层数据库。 |
11 | void cancelRowUpdates() |
取消对此 ResultSet对象中当前行的更新。在updateRow()调用之前有效 |
12 | void insertRow() |
将插入行的内容插入到此 ResultSet对象中并存入数据库。 |
13 | void deleteRow() |
从此 ResultSet对象和底层数据库中删除当前行。 |
上面列出了ResultSet
与JDBC2.0
有关的操作,当然,JDBC2.0不止包括这些,在其中还有一个最重要的操作就是可以进行批处理操作。
关于ResultSet常量的补充说明
ResultSet中提供了很多常量,但是整体化为为两类:
- 设置
ResultSet
类型:TYPE_XXX
设置的都是类型,类型主要表示是否可以滚动以及是否可以修改数据表的内容。 - 设置并发性:
CONCUR_XXX
设置的都是并发性,并发性主要表示结果集是否是只读还是可以进行数据库更新操作等。
可滚动的结果集
在之前所讲解的ResultSet操作中,返回的结果只能由前向后的顺序输出,如果现在想取出结果集中任意位置的数据,则可以使用可滚动的结果集,在创建数据库操作对象时加入若干参数即可实现,如下所示。
con = DriverManager.getConnection(url, user, password);
// 实例化预编译数据库操作对象,设置结果集可滚动,并且以只读形式打开结果集。
pre = con.prepareStatement(sql,ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_READ_ONLY);
//执行SQL语句,实例化ResultSet
rs=pre.executeQuery();
以上在使用Connection
创建PrepareStatement
操作时,除了设置操作的SQL语句之外,还设置了两个ResultSet定义的常量,第一个常量表示结果集可以滚动,第二个常量表示以只读的形式打开结果集。
现在假定usersinfo
数据库中的test
表只有如下图所示的数据。
实例:使用滚动的结果集
package my.scroll.resultset;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class JDCBC20ScrollRead
{
public static final String driver = "com.mysql.jdbc.Driver";
public static final String url = "jdbc:mysql://localhost:3306/usersinfo";
public static final String user = "root";
public static final String password = "root";
public static void main(String[] args) throws ClassNotFoundException, SQLException
{
Connection con = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
// id,name,sex,grade,major,birthday
String sql = "select id,name,sex,grade,major,birthday from test";
Class.forName(driver);
con = DriverManager.getConnection(url,user,password);
pstmt = con.prepareStatement(sql, ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY);
rs=pstmt.executeQuery();
//指针知道第二行
rs.absolute(2);
System.out.println("第2行:");
printRow(rs, 0);
System.out.println("第1行:");
printRow(rs, -1);
System.out.println("第4行:");
printRow(rs, 3);//第一行往后移动三行就是第4行
rs.close();
pstmt.close();
con.close();
}
/**
* 查询距离当前行偏移行数为offset行上的记录
* @param rs 结果集引用(指针)
* @param offset 向前或向后移动指针的行数,
* @throws SQLException
*/
static void printRow(ResultSet rs,int offset) throws SQLException
{
//如果偏移量大于0,表示往后查询
if(offset>0)
{
for(int i=0;i<offset;i++)
{rs.next();}
}
//如果偏移量小于0表示,向前查询
else if(offset<0)
{
for(int i=0;i>offset;i--)
{rs.previous();}
}
//如果偏移量为0则不移动指针
// id,name,sex,grade,major,birthday
System.out.print("id:"+rs.getString(1));
System.out.print(",name:"+rs.getString(2));
System.out.print(",sex:"+rs.getString(3));
System.out.print(",grade:"+rs.getString(4));
System.out.print(",major:"+rs.getString(5));
//取出数据库中的日期
java.sql.Date sqlDate=rs.getDate(6);
//转换成java.util.Date类型的日期
java.util.Date utilDate=new Date(sqlDate.getTime());
SimpleDateFormat dateFormat=new SimpleDateFormat("yyyy-MM-dd");
System.out.print(",birthday:"+dateFormat.format(utilDate));
System.out.println();
}
}
运行结果:
第2行:
id:B1001,name:李四,sex:男,grade:大三,major:计算机科学与技术,birthday:2007-08-27
第1行:
id:B1000,name:张三,sex:男,grade:大三,major:计算机科学与技术,birthday:2007-08-27
第4行:
id:B1003,name:小钱,sex:男,grade:大三,major:计算机科学与技术,birthday:2007-08-27
对比test表:
可以看到上面的操作是正确的。使用rs.next()向前使得指针向前移动一行,使用rs.previous()方法使得指针向前移动一行。然后通过循环的方式来指定向前或者向后移动几行。
上面的程序中因为设置了结果集可以滚动,所以可以通过方法指定当前所在的行,设置前后移动当前行,也可以跳转到第一行,最后一行,以及跳转到第一行的前面。
下面分别给出几个方法来分别介绍。
- 跳转到第一行:
/**
* 打印第1行的记录。
* @param rs 结果集
* @throws SQLException
*/
private static void SelectFirst(ResultSet rs) throws SQLException
{
rs.first();//指针移动到第一行
printRow(rs, 0);
}
- 跳转到最后一行:
/**
* 打印结果集中最后一行中的记录。
* @param rs 结果集。
* @throws SQLException
*/
private static void SelectLast(ResultSet rs) throws SQLException
{
rs.last();
printRow(rs, 0);
}
- 跳转到第一行之前(输出整个结果集):
/**
* 打印滚动结果集中的全部内容。
* @param rs 结果集
* @throws SQLException
*/
private static void SelectAll(ResultSet rs) throws SQLException
{
rs.beforeFirst();//指针移动到第一行之前
while(rs.next())
{
printRow(rs,0);
}
}
测试:在上面的代码后面添加下面的调用语句调用:
System.out.println("----------------------------");
System.out.println("第1行:");
SelectFirst(rs);
System.out.println("最后一行:");
SelectLast(rs);
System.out.println("----------------------------");
System.out.println("表中的全部数据:");
SelectAll(rs);
运行结果:
----------------------------
第1行:
id:B1000,name:张三,sex:男,grade:大三,major:计算机科学与技术,birthday:2007-08-27
最后一行:
id:H1001,name:小芳,sex:男,grade:大三,major:计算机科学与技术,birthday:2007-08-27
----------------------------
表中的全部数据:
id:B1000,name:张三,sex:男,grade:大三,major:计算机科学与技术,birthday:2007-08-27
id:B1001,name:李四,sex:男,grade:大三,major:计算机科学与技术,birthday:2007-08-27
id:B1002,name:小赵,sex:男,grade:大三,major:计算机科学与技术,birthday:2007-08-27
id:B1003,name:小钱,sex:男,grade:大三,major:计算机科学与技术,birthday:2007-08-27
id:H1000,name:小明,sex:男,grade:大三,major:计算机科学与技术,birthday:2007-08-27
id:H1001,name:小芳,sex:男,grade:大三,major:计算机科学与技术,birthday:2007-08-27
对比表中的数据:
可以发现我们的操作是正确的。
完整的代码:
package my.scroll.resultset;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class JDCBC20ScrollRead
{
public static final String driver = "com.mysql.jdbc.Driver";
public static final String url = "jdbc:mysql://localhost:3306/usersinfo";
public static final String user = "root";
public static final String password = "root";
public static void main(String[] args) throws ClassNotFoundException, SQLException
{
Connection con = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
// id,name,sex,grade,major,birthday
String sql = "select id,name,sex,grade,major,birthday from test";
Class.forName(driver);
con = DriverManager.getConnection(url, user, password);
pstmt = con.prepareStatement(sql, ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY);
rs = pstmt.executeQuery();
// 指针知道第二行
rs.absolute(2);
System.out.println("第2行:");
printRow(rs, 0);
System.out.println("第1行:");
printRow(rs, -1);
System.out.println("第4行:");
printRow(rs, 3);// 第一行往后移动三行就是第4行
System.out.println("----------------------------");
System.out.println("第1行:");
SelectFirst(rs);
System.out.println("最后一行:");
SelectLast(rs);
System.out.println("----------------------------");
System.out.println("表中的全部数据:");
SelectAll(rs);
//关闭结果集
rs.close();
//关闭操作
pstmt.close();
//关闭连接
con.close();
}
/**
* 打印结果集中最后一行中的记录。
*
* @param rs
* 结果集。
* @throws SQLException
*/
private static void SelectLast(ResultSet rs) throws SQLException
{
rs.last();
printRow(rs, 0);
}
/**
* 打印第1行的记录。
*
* @param rs
* 结果集
* @throws SQLException
*/
private static void SelectFirst(ResultSet rs) throws SQLException
{
rs.first();// 指针移动到第一行
printRow(rs, 0);
}
/**
* 打印滚动结果集中的全部内容。
*
* @param rs
* 结果集
* @throws SQLException
*/
private static void SelectAll(ResultSet rs) throws SQLException
{
rs.beforeFirst();// 指针移动到第一行之前
while (rs.next())
{
printRow(rs, 0);
}
}
/**
* 查询距离当前行偏移行数为offset行上的记录
*
* @param rs
* 结果集引用(指针)
* @param offset
* 向前或向后移动指针的行数,
* @throws SQLException
*/
static void printRow(ResultSet rs, int offset) throws SQLException
{
// 如果偏移量大于0,表示往后查询
if (offset > 0)
{
for (int i = 0; i < offset; i++)
{
rs.next();
}
}
// 如果偏移量小于0表示,向前查询
else if (offset < 0)
{
for (int i = 0; i > offset; i--)
{
rs.previous();
}
}
// 如果偏移量为0则不移动指针
// id,name,sex,grade,major,birthday
System.out.print("id:" + rs.getString(1));
System.out.print(",name:" + rs.getString(2));
System.out.print(",sex:" + rs.getString(3));
System.out.print(",grade:" + rs.getString(4));
System.out.print(",major:" + rs.getString(5));
// 取出数据库中的日期
java.sql.Date sqlDate = rs.getDate(6);
// 转换成java.util.Date类型的日期
java.util.Date utilDate = new Date(sqlDate.getTime());
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
System.out.print(",birthday:" + dateFormat.format(utilDate));
System.out.println();
}
}
使用结果集插入数据
在JDCB2.0
中,如果要使用结果集进行数据库的更新操作
,则在创建PreparedStatement
对象时必须设置结果集可以更新数据库。
pstmt=con.prepareStatement(sql, ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
实例:使用结果集在test表中增加数据
package my.scroll.resultset;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class JDCBC20ScrollInsert
{
public static final String dirver="com.mysql.jdbc.Driver";
public static final String url="jdbc:mysql://localhost:3306/usersinfo";
public static final String user="root";
public static final String password="root";
public static void main(String[] args) throws ClassNotFoundException,SQLException, ParseException
{
Connection con=null;
PreparedStatement pstmt=null;
ResultSet rs=null;
String sql="select id,name,sex,grade,major,birthday from test";
Class.forName(dirver);
con=DriverManager.getConnection(url,user,password);
//设置结果集可以滚动,设置结果集可以低层数据库
pstmt=con.prepareStatement(sql, ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
rs=pstmt.executeQuery();
**//移动到结果集中可以插入数据的行(也就是最后一行之后)
rs.moveToInsertRow();**
//在结果集中加入一条记录
// id,name,sex,grade,major,birthday
rs.updateString("id", "H1002");
rs.updateString("name", "小赵");
rs.updateString("sex", "男");
rs.updateString("grade", "大一");
rs.updateString("major", "软件工程");
String birthday="2000-1-1";
SimpleDateFormat format=new SimpleDateFormat("yyyy-MM-dd");
java.util.Date utilDate=format.parse(birthday);
java.sql.Date sqlDate=new java.sql.Date(utilDate.getTime());
rs.updateDate("birthday", sqlDate);
**//把结果集中的新加入的记录写到低层数据库usersinfo的test表中:
rs.insertRow();**
//关闭结果集
rs.close();
//关闭操作
pstmt.close();
//关闭数据库连接
con.close();
}
}
运行结果:
test表中原来的数据:
使用结果集插入一条记录后的test表
可以看到我们往结果集中插入的记录,以及成功写到数据库中了。
使用结果集更新数据
使用ResultSet完成更新操作也是比较容易的,但是一般在更新时,都会对执行的行进行更新,所以在一下代码中使用限定查询,查询上面刚插入的学号为”H1002”的学生的信息,然后进行更新操作。
实例:使用结果集更新数据库
package my.scroll.resultset;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class JDCBC20ScrollUpdate
{
public static final String driver="com.mysql.jdbc.Driver";
public static final String url="jdbc:mysql://localhost:3306/usersinfo";
public static final String user="root";
public static final String password="root";
public static void main(String[] args)throws ClassNotFoundException,SQLException,ParseException
{
Connection con=null;
PreparedStatement pstmt=null;
ResultSet rs=null;
String sql="select id,name,sex,grade,major,birthday from test "
+ "where id=?";
Class.forName(driver);
con=DriverManager.getConnection(url,user,password);
pstmt=con.prepareStatement(sql, ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
//设置占位符
pstmt.setString(1, "H1002");
//输出编译好的SQL语句
String toString=pstmt.toString();
System.out.println("mysql>"+toString.substring(toString.lastIndexOf(":")+1).trim());
//执行查询操作,返回结果集
rs=pstmt.executeQuery();
System.out.println("查询结果:");
while(rs.next())
{
printRow(rs);
}
//更改结果集里面的数据,注意要更新就要全部更新,不然会报错
//id,name,sex,grade,major,birthday
rs.first();//把指针移动回去到第一行
rs.updateString("name", "小钱");
rs.updateString("sex", "女");
rs.updateString("grade", "大三");
rs.updateString("major", "计算机科学与技术");
Date date=new Date();
SimpleDateFormat format=new SimpleDateFormat("yyy-MM-dd");
//获取今天的java.util.Date日期
Date birthday=format.parse(format.format(date));
java.sql.Date sqlDate=new java.sql.Date(birthday.getTime());
rs.updateDate("birthday", sqlDate);
**//执行更新操作
rs.updateRow();**
System.out.println("---------------------------------------");
//执行查询操作,返回结果集
rs=pstmt.executeQuery();
System.out.println("查询结果:");
while(rs.next())
{
printRow(rs);
}
}
static void printRow(ResultSet rs) throws SQLException
{
// id,name,sex,grade,major,birthday
System.out.print("id:" + rs.getString(1));
System.out.print(",name:" + rs.getString(2));
System.out.print(",sex:" + rs.getString(3));
System.out.print(",grade:" + rs.getString(4));
System.out.print(",major:" + rs.getString(5));
// 取出数据库中的日期
java.sql.Date sqlDate = rs.getDate(6);
// 转换成java.util.Date类型的日期
java.util.Date utilDate = new Date(sqlDate.getTime());
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
System.out.print(",birthday:" + dateFormat.format(utilDate));
System.out.println();
}
}
运行结果:
- 控制台输出:
mysql>select id,name,sex,grade,major,birthday from test where id='H1002'
查询结果:
id:H1002,name:小赵,sex:男,grade:大一,major:软件工程,birthday:2000-01-01
---------------------------------------
查询结果:
id:H1002,name:小钱,sex:女,grade:大三,major:计算机科学与技术,birthday:2018-07-09
- 操作之前test表中的数据
- 操作之后test表中的数据
与使用结果集插入不同的是,此处使用的是updateRow()
方法进行数据库更新操作。而且在修改数据时,必须将ResultSet的指针指向要更改的行,因为此时,查询到的结果集中只有一行元素,所以使用rs.first()
方法即可将指针移动到要更新的行。
在数据库更新之前,可以用使用cacelRowUpdate()方法取消更新操作。
在执行updateRow()
方法之前,如果发现数据库更新有错误,则可以使用cancelRowUpdates()
方法取消之前的更新,这样就算是再执行updateRow()
方法,也不会去更新数据库,如下所示:
rs.cancelRowUpdates();//取消更新
rs.updateRow();//使用结果集更新低层数据库数据
一定要记住,cancelRowUpdate()方法要在updateRow()方法执行之前调用,否则取消操作无效。
使用结果集删除数据
使用结果集删除数据与之前的操作差不多一样,不同的是,此时调用的是deleteRow()方法。具体的操作是:选中要删除的行,然后调用rs.deleteRow()方法即可删除。
实例:删除test表中指定学号的的学生记录
package my.scroll.resultset;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class JDCBC20ScrollDelete
{
public static final String driver="com.mysql.jdbc.Driver";
public static final String url="jdbc:mysql://localhost:3306/usersinfo";
public static final String user="root";
public static final String password="root";
public static void main(String[] args)throws ClassNotFoundException,SQLException
{
Connection con=null;
PreparedStatement pstmt=null;
ResultSet rs=null;
String sql="select id,name,sex,grade,major,birthday from test "
+ "where id=?";
Class.forName(driver);
con=DriverManager.getConnection(url,user,password);
pstmt=con.prepareStatement(sql, ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
//设置占位符的内容
pstmt.setString(1, "H1002");
String toString=pstmt.toString();
System.out.println("mysql>"+toString.substring(toString.lastIndexOf(":")+1).trim());
rs=pstmt.executeQuery();
while(rs.next())
{
printRow(rs);
}
//移动到第一行,因为之前的打印操作后指针指向了最后一行之后了。
rs.first();
//删除这一行
rs.deleteRow();
rs.close();
pstmt.close();
con.close();
}
/**
* 打印当前结果集中当前行中的记录。
* @param rs 结果集
* @throws SQLException
*/
static void printRow(ResultSet rs) throws SQLException
{
// id,name,sex,grade,major,birthday
System.out.print("id:" + rs.getString(1));
System.out.print(",name:" + rs.getString(2));
System.out.print(",sex:" + rs.getString(3));
System.out.print(",grade:" + rs.getString(4));
System.out.print(",major:" + rs.getString(5));
// 取出数据库中的日期
java.sql.Date sqlDate = rs.getDate(6);
// 转换成java.util.Date类型的日期
java.util.Date utilDate = new Date(sqlDate.getTime());
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
System.out.print(",birthday:" + dateFormat.format(utilDate));
System.out.println();
}
}
运行结果:
- 控制台输出:
mysql>select id,name,sex,grade,major,birthday from test where id='H1002'
id:H1002,name:小钱,sex:女,grade:大三,major:计算机科学与技术,birthday:2018-07-09
- 操作之前的test表中的数据:
- 操作之前的test表中的数据:
以上代码完成了记录的删除操作,首先根据学号id查找到一行记录,在通过first()方法跳转到第一条记录(也就一条记录),只有执行deleteRow()方法,及删除掉当前这一行记录。
批处理
在JDBC2.0
中,最重要的概念是批处理。使用批处理之后一个一次性插入多条SQL语句,如果要完成批处理操作,则要使用addBatch()
加入要执行的一条SQL命令以及executeBatch()
执行全部命令着两个方法完成。
实例:批量插入数据
package my.scroll.resultset;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
public class JDCBC20BatchDemo
{
public static final String driver="com.mysql.jdbc.Driver";
public static final String url="jdbc:mysql://127.0.0.1:3306/usersinfo";
public static final String user="root";
public static final String password="root";
public static void main(String[] args)throws ClassNotFoundException,SQLException, ParseException
{
Connection con=null;
PreparedStatement pstmt=null;
// id,name,sex,grade,major,birthday
String sql="insert into test(id,name,sex,grade,major,birthday) "
+ "values(?,?,?,?,?,?)";
Class.forName(driver);
con=DriverManager.getConnection(url,user,password);
pstmt=con.prepareStatement(sql);
//加入3条数据
// id,name,sex,grade,major,birthday
for(int i=0;i<3;i++)
{
//
pstmt.setString(1,"S100"+i);//id
pstmt.setString(2,"小孙"+i);//name
pstmt.setString(3,"男");//sex
pstmt.setString(4,"大一");//grade
pstmt.setString(5, "信息安全");//major
//birthday
java.util.Date date=new java.util.Date();
SimpleDateFormat format=new SimpleDateFormat("yyyy-MM-dd");
java.util.Date today=format.parse(format.format(date));
java.sql.Date birthday=new java.sql.Date(today.getTime());
pstmt.setDate(6, birthday);
//加入批处理等待执行
pstmt.addBatch();
}
//执行批处理操作
int temp[] =pstmt.executeBatch();
System.out.println("一共插入了:"+temp.length+"条记录");
pstmt.close();
con.close();
}
}
运行结果:
- 控制台输出:
一共插入了:3条记录
- 执行操作之前的test表:
- 执行操作之后的test表:
可以看到以上完成了批处理的操作,数据库一次性插入了3条记录到test
表中。