配置了这么久终于可以学习JDBC了,在这之前,给刚刚的表多插入一些表项:
mysql> USE newDB;
Database changed
mysql> INSERT INTO NewUsr
-> (id,name,age)
-> VALUES
-> (1,'lzh',20),
-> (3,'sb',17),
-> (2,'奥特曼',9),
-> (7,'起诉面包',3);
Query OK, 4 rows affected (0.00 sec)
Records: 4 Duplicates: 0 Warnings: 0
mysql> SELECT * FROM NewUsr;
+----+--------------+------+
| id | name | age |
+----+--------------+------+
| 6 | 红色青蛙 | NULL |
| 1 | lzh | 20 |
| 3 | sb | 17 |
| 2 | 奥特曼 | 9 |
| 7 | 起诉面包 | 3 |
+----+--------------+------+
5 rows in set (0.00 sec)
表项在表中逻辑上是没有顺序的,既不能认为按照id的大小,也不能认为是时间先后的顺序(虽然不使用ORDER BY时查询出来就是这个顺序)。
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
public class Main {
public static void main(String[] args) {
try {
// 要求JVM查找并加载指定的类
Class.forName("com.mysql.jdbc.Driver");
// 声明一个sql连接对象
java.sql.Connection con = null;
// 连接信息字符串,MySQL数据库服务器的默认端口号是3306
// jdbc:mysql://ip地址:端口号/要连接的数据库名?其它选项
// useSSL参数指明数据通道是否要加密处理
// characterEncoding参数指明连接编码,要和数据库编码,数据库表的编码,数据库系统的编码一致
String uri = "jdbc:mysql://192.168.0.106:3306/newDB?useSSL=true&characterEncoding=utf8";
String user = "root";// 用户名
String password = "3838438"; // 密码
// 和指定的数据库建立连接(连接信息字符串,用户名,密码)
con = DriverManager.getConnection(uri, user, password);
// 用Statement声明一个SQL语句对象,用连接对象的createStatement()方法创建之
// SQL语句对象用来向数据库发送SQL语句,并返回结果
java.sql.Statement sql = con.createStatement();
// SQL语句对象调用executeQuery()方法对数据库进行查询,返回ResultSet对象
ResultSet rs = sql.executeQuery("SELECT * FROM NewUsr;");// 在这里指定查询语句
// 顺序查询方式
// ResultSetd对象一次只能看到一个数据行,用next()方法移动到下一个数据行
while (rs.next()) {
// 在每个数据行上多次使用getXxx()方法获取这行上每个字段中的数据
// 传入的参数可以是int列号,也可以是String列名
// 而getXxx()中的Xxx就是获得这一字段值时采取的类型,也即返回的数据类型
int id = rs.getInt(1);
String name = rs.getString(2);// 实际上不论哪种类型的字段都可以使用getString(列号/列名)
int age = rs.getInt(3);
System.out.println(id + " " + name + " " + age);
}
// 最后关闭连接
// 注意当Connection对象使用close()方法关闭连接后,ResultSet对象不再可用
// 所以如果要使用ResultSet对象就要一直保持连接
// 如果要关闭连接后还能使用数据,就要把ResultSet对象里的数据存到别的地方去
con.close();
} catch (SQLException e) {
// SQLException异常可能发生在DriverManager.getConnection()方法获得Connection对象时
// 还可能发生在Connection对象调用createStatement()方法创建SQL语句对象时
// 还可能发生在SQL语句对象调用executeQuery()方法对数据库进行查询时
// 还可能发生在Connection对象调用close()方法关闭连接时
e.printStackTrace();
} catch (ClassNotFoundException e) {
// ClassNotFoundException异常可能发生在lass.forName()时,即找不到要加载的类
e.printStackTrace();
}
}
}
输出
6 红色青蛙 0
1 lzh 20
3 sb 17
2 奥特曼 9
7 起诉面包 3
用next()方法让结果集的游标向下一行,如果成功时会返回true,在初始状态,结果集的游标总是在第一行的前面。
如果Connection对象用无参数的createStatement()
方法获取SQL语句对象,那么只能进行向下的顺序查询。为它指定特定的参数以控制查询游标可以上下移动:
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
public class Main {
public static void main(String[] args) {
try {
// 要求JVM查找并加载指定的类
Class.forName("com.mysql.jdbc.Driver");
// 声明一个sql连接对象
java.sql.Connection con = null;
// 连接信息字符串,MySQL数据库服务器的默认端口号是3306
// jdbc:mysql://ip地址:端口号/要连接的数据库名?其它选项
// useSSL参数指明数据通道是否要加密处理
// characterEncoding参数指明连接编码,要和数据库编码,数据库表的编码,数据库系统的编码一致
String uri = "jdbc:mysql://192.168.0.106:3306/newDB?useSSL=true&characterEncoding=utf8";
String user = "root";// 用户名
String password = "3838438"; // 密码
// 和指定的数据库建立连接(连接信息字符串,用户名,密码)
// 用Statement声明一个SQL语句对象,用连接对象的createStatement()方法创建之
// SQL语句对象用来向数据库发送SQL语句,并返回结果
// ResultSet.TYPE_SCROLL_SENSITIVE使结果集可以滚动
// ResultSet.CONCUR_READ_ONLY不能用结果集更新数据库中的表
java.sql.Statement sql = con.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY);
// SQL语句对象调用executeQuery()方法对数据库进行查询,返回ResultSet对象
ResultSet rs = sql.executeQuery("SELECT * FROM NewUsr;");// 在这里指定查询语句
/* 滚动查询方式(即可以控制游标) */
rs.next();// 向下移动到第一行
System.out.println(rs.getInt(1) + " " + rs.getString(2) + " " + rs.getInt(3));
int lnth = rs.getRow();// 获取当前行号
System.out.println("当前行号:" + lnth);
rs.last();// 移动到最后一行
System.out.println(rs.getInt(1) + " " + rs.getString(2) + " " + rs.getInt(3));
rs.previous();// 向上移动一行
System.out.println(rs.getInt(1) + " " + rs.getString(2) + " " + rs.getInt(3));
System.out.println("是否在最后一行之后" + rs.isAfterLast());// 是否在最后一行之后
System.out.println("是否在第一行之前" + rs.isBeforeFirst());// 是否在第一行之前
rs.absolute(3);// 绝对移动:到第3行
System.out.println(rs.getInt(1) + " " + rs.getString(2) + " " + rs.getInt(3));
rs.absolute(-2);// 绝对移动:到倒数第2行
System.out.println(rs.getInt(1) + " " + rs.getString(2) + " " + rs.getInt(3));
// 最后关闭连接
// 注意当Connection对象使用close()方法关闭连接后,ResultSet对象不再可用
// 所以如果要使用ResultSet对象就要一直保持连接
// 如果要关闭连接后还能使用数据,就要把ResultSet对象里的数据存到别的地方去
con.close();
} catch (SQLException e) {
// SQLException异常可能发生在DriverManager.getConnection()方法获得Connection对象时
// 还可能发生在Connection对象调用createStatement()方法创建SQL语句对象时
// 还可能发生在SQL语句对象调用executeQuery()方法对数据库进行查询时
// 还可能发生在Connection对象调用close()方法关闭连接时
e.printStackTrace();
} catch (ClassNotFoundException e) {
// ClassNotFoundException异常可能发生在lass.forName()时,即找不到要加载的类
e.printStackTrace();
}
}
}
输出
6 红色青蛙 0
当前行号:1
7 起诉面包 3
2 奥特曼 9
是否在最后一行之后false
是否在第一行之前false
3 sb 17
2 奥特曼 9
Connection对象调用createStatement()方法创建SQL语句对象时,传入的第二个参数,取值为ResultSet.CONCUR_READ_ONLY
时不能用结果集更新数据库中的表项。教材书这样写感觉很模糊,而且没有具体说明。学习它的对立参数ResultSet.CONCUR_UPDATABLE
就能明白什么是”用结果集更新数据库中的表”了。
Connection对象调用createStatement()方法创建SQL语句对象时,传入的第一个参数可以有多种取值,当取ResultSet.TYPE_SCROLL_SENSITIVE
时,不仅表示结果集可滚动,对数据库表的更新会更新ResultSet结果集对象。
当使用CONCUR_UPDATABLE用结果集更新数据库表时,需要先移动到要操作的行,调用ResultSet对象的updateXxx(列号,新值)
方法(Xxx是类型),再调用updateRow()方法完成更新:
// 用Statement声明一个SQL语句对象,用连接对象的createStatement()方法创建之
// SQL语句对象用来向数据库发送SQL语句,并返回结果
// ResultSet.TYPE_SCROLL_SENSITIVE使结果集可以滚动,当数据变化时,结果集同步改变
// ResultSet.CONCUR_UPDATABLE能用结果集更新数据库中的表
java.sql.Statement sql = con.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
// SQL语句对象调用executeQuery()方法对数据库进行查询,返回ResultSet对象
ResultSet rs = sql.executeQuery("SELECT * FROM NewUsr;");// 在这里指定查询语句
/* 滚动查询方式(即可以控制游标) */
rs.absolute(3);// 绝对移动:到第3行
System.out.println(rs.getInt(1) + " " + rs.getString(2) + " " + rs.getInt(3));
// 用ResultSet结果集去更新数据库表项
rs.updateInt(1, 11);
rs.updateString(2, "皮卡丘");
rs.updateInt(3, 6);
rs.updateRow();// 切记要做了这一步才能完成更新!
// 在ResultSet.TYPE_SCROLL_SENSITIVE参数下,对数据库表项的操作会同时更新结果集
System.out.println(rs.getInt(1) + " " + rs.getString(2) + " " + rs.getInt(3));
输出
3 sb 17
11 皮卡丘 6
这时候数据库表项已经更新了。
如果要取消更新,注意这个取消不是把上面已经更新数据库表项这件事取消,而是把前面的updateXxx(列号,新值)
方法们做的更新取消,可以在updateRow()前调用ResultSet对象的cancelRowUpdates()
方法取消:
// 用Statement声明一个SQL语句对象,用连接对象的createStatement()方法创建之
// SQL语句对象用来向数据库发送SQL语句,并返回结果
// ResultSet.TYPE_SCROLL_SENSITIVE使结果集可以滚动,当数据变化时,结果集同步改变
// ResultSet.CONCUR_UPDATABLE能用结果集更新数据库中的表
java.sql.Statement sql = con.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
// SQL语句对象调用executeQuery()方法对数据库进行查询,返回ResultSet对象
ResultSet rs = sql.executeQuery("SELECT * FROM NewUsr;");// 在这里指定查询语句
/* 滚动查询方式(即可以控制游标) */
System.out.println("[更新前]");
rs.absolute(3);// 绝对移动:到第3行
System.out.println(rs.getInt(1) + " " + rs.getString(2) + " " + rs.getInt(3));
// 用ResultSet结果集去更新数据库表项
rs.updateInt(1, 18);
rs.updateString(2, "暗夜猎手");
rs.updateInt(3, 9);
rs.updateRow();// 尝试更新
rs.absolute(4);// 绝对移动:到第4行
System.out.println(rs.getInt(1) + " " + rs.getString(2) + " " + rs.getInt(3));
// 用ResultSet结果集去更新数据库表项
rs.updateInt(1, 12);
rs.updateString(2, "777");
rs.updateInt(3, 19);
rs.cancelRowUpdates();// 取消更新
rs.updateRow();// 尝试更新
rs.absolute(1);// 绝对移动:到第1行
System.out.println(rs.getInt(1) + " " + rs.getString(2) + " " + rs.getInt(3));
// 用ResultSet结果集去更新数据库表项
rs.updateInt(1, 15);
rs.updateString(2, "SBSB");
rs.updateInt(3, 21);
rs.updateRow();// 尝试更新
// 在ResultSet.TYPE_SCROLL_SENSITIVE参数下,对数据库表项的操作会同时更新结果集
System.out.println("[更新后]");
rs.absolute(3);// 绝对移动:到第3行
System.out.println(rs.getInt(1) + " " + rs.getString(2) + " " + rs.getInt(3));
rs.absolute(4);// 绝对移动:到第4行
System.out.println(rs.getInt(1) + " " + rs.getString(2) + " " + rs.getInt(3));
rs.absolute(1);// 绝对移动:到第1行
System.out.println(rs.getInt(1) + " " + rs.getString(2) + " " + rs.getInt(3));
输出
[更新前]
13 雷丘 7
2 奥特曼 9
6 红色青蛙 0
[更新后]
18 暗夜猎手 9
2 奥特曼 9
15 SBSB 21
从输出可以看出,updateRow()
方法和cancelRowUpdates()
方法也仅仅是针对当前数据行的。这样的好处在于可以把各个数据行间的移动、更新写在前面,而把这两个方法写在最后,只要用绝对移动到各个数据行,再去抉择是否要对这行应用更新即可了。
Connection对象调用createStatement()方法创建SQL语句对象时,传入的第一个参数可以有多种取值,当取ResultSet.TYPE_SCROLL_INSENSITIVE
时,表示结果集可滚动,但对数据库表的更新不会更新ResultSet结果集对象。
但是,CONCUR_UPDATABLE选择的方式就是用结果集去更新数据库表项,所以在这种方式下不论用的是ResultSet.TYPE_SCROLL_INSENSITIVE
还是ResultSet.TYPE_SCROLL_SENSITIVE
,结果集都会改变:
// 用Statement声明一个SQL语句对象,用连接对象的createStatement()方法创建之
// SQL语句对象用来向数据库发送SQL语句,并返回结果
// ResultSet.TYPE_SCROLL_INSENSITIVE使结果集可以滚动,当数据变化时,结果集不改变!
// 但是,注意在CONCUR_UPDATABLE参数下的情况!
// ResultSet.CONCUR_UPDATABLE能用结果集更新数据库中的表
java.sql.Statement sql = con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE);
// SQL语句对象调用executeQuery()方法对数据库进行查询,返回ResultSet对象
ResultSet rs = sql.executeQuery("SELECT * FROM NewUsr;");// 在这里指定查询语句
/* 滚动查询方式(即可以控制游标) */
System.out.println("[更新前,结果集对象中的这行]");
rs.absolute(4);// 绝对移动:到第4行
System.out.println(rs.getInt(1) + " " + rs.getString(2) + " " + rs.getInt(3));
// 用ResultSet结果集去更新数据库表项
rs.updateInt(1, 12);
rs.updateString(2, "777");
rs.updateInt(3, 19);
rs.updateRow();// 更新这行
// 在ResultSet.TYPE_SCROLL_INSENSITIVE参数下,对数据库表项的操作不会同时更新结果集
// 但是,注意在CONCUR_UPDATABLE参数下的情况!
System.out.println("[数据项更新后,当前结果集对象中的这行]");
rs.absolute(4);// 绝对移动:到第4行(其实已经在第4行了)
System.out.println(rs.getInt(1) + " " + rs.getString(2) + " " + rs.getInt(3));
输出
[更新前,结果集对象中的这行]
2 奥特曼 9
[数据项更新后,当前结果集对象中的这行]
12 777 19
那么如何去看这两个参数的区别之处呢,总不能把SELECT语句前面加上UPDATE语句来看吧,这样得到的已经不是同一个结果集了,我猜想ResultSet.TYPE_SCROLL_INSENSITIVE
参数提供的是多线程情形下的对数据库访问结果的保护,以后的学习中再来验证吧。
除了可以用结果集去操作数据库表项,还可以直接用SQL语句去做这些操作。但是注意更新、添加、删除操作使用的是SQL语句对象的.executeUpdate()
方法且返回的是一个int型的受影响的行数,而不是.executeQuery()
方法返回结果集:
// 用Statement声明一个SQL语句对象,用连接对象的createStatement()方法创建之
// SQL语句对象用来向数据库发送SQL语句,并返回结果
// ResultSet.TYPE_SCROLL_INSENSITIVE使结果集可以滚动,当数据变化时,结果集不改变!
// ResultSet.CONCUR_UPDATABLE能用结果集更新数据库中的表
java.sql.Statement sql = con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE);
// SQL语句对象调用executeQuery()方法对数据库进行查询,返回ResultSet对象
ResultSet rs = sql.executeQuery("SELECT * FROM NewUsr;");// 在这里指定查询语句
System.out.println("[更新,添加,删除之前]");
// 顺序查看这张表
while (rs.next()) {
int id = rs.getInt(1);
String name = rs.getString(2);
int age = rs.getInt(3);
System.out.println(id + " " + name + " " + age);
}
System.out.println("[更新,添加,删除操作]");
int num = sql.executeUpdate("UPDATE NewUsr SET name='刘知昊' WHERE id=12;");// 更新数据库表项
System.out.println(num + "行受影响");
num = sql.executeUpdate("DELETE FROM NewUsr WHERE name='SBSB';");// 删除数据库表项
System.out.println(num + "行受影响");
num = sql.executeUpdate("INSERT INTO NewUsr VALUES(22,'啊啊啊',23);");// 添加数据库表项
System.out.println(num + "行受影响");
// SQL语句对象调用executeQuery()方法对数据库进行查询,返回ResultSet对象
rs = sql.executeQuery("SELECT * FROM NewUsr;");// 在这里指定查询语句
System.out.println("[更新,添加,删除之后]");
// 顺序查看这张表
while (rs.next()) {
int id = rs.getInt(1);
String name = rs.getString(2);
int age = rs.getInt(3);
System.out.println(id + " " + name + " " + age);
}
输出
[更新,添加,删除之前]
15 SBSB 21
1 lzh 20
18 暗夜猎手 9
12 777 19
7 起诉面包 3
[更新,添加,删除操作]
1行受影响
1行受影响
1行受影响
[更新,添加,删除之后]
22 啊啊啊 23
1 lzh 20
18 暗夜猎手 9
12 刘知昊 19
7 起诉面包 3