原study表中已经有两条记录,希望新增一条记记录。
study表字段分别是id,username,password,name,sex,age,level,mobile。其中id是主键(自动增值)其余均可空值,因此本次测试只对username,password,name,sex,age字段进行新增记录测试。
源码如下:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class RegisterAccount {
public static void main(String[] args) {
// 创建一个 BufferedReader 对象,它将从 System.in 读取数据
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
try {
// 提示用户输入信息
System.out.println("请输入您要注册的用户名:");
// 从标准输入读取一行文本
String input_username = reader.readLine();
// 提示用户输入信息
System.out.println("请输入您的密码:");
// 从标准输入读取一行文本
String input_password = reader.readLine();
// 提示用户输入信息
System.out.println("请输入您的姓名:");
// 从标准输入读取一行文本
String input_name = reader.readLine();
// 提示用户输入信息
System.out.println("请输入您的性别:");
// 从标准输入读取一行文本
String input_sex = reader.readLine();
// 提示用户输入信息
System.out.println("请输入您的年龄:");
// 从标准输入读取一行文本
int input_age = Integer.parseInt(reader.readLine());
// 调用入表操作
DatabaseInsert(input_username, input_password, input_name, input_sex, input_age);
// 输出用户输入的信息
System.out.println("你的用户名是:" + input_username);
System.out.println("你的密码是:" + input_password);
System.out.println("你的姓名是:" + input_name);
System.out.println("你的性别是:" + input_sex);
System.out.println("你的年龄是:" + input_age);
} catch (IOException e) {
// 处理可能出现的异常
e.printStackTrace();
}
}
public static void DatabaseInsert(String tmp_username, String tmp_password, String tmp_name, String tmp_sex, int tmp_age) {
String url = "jdbc:mysql://localhost:3306/study";
String user = "username"; //连接数据库的用户名
String password = "password"; //连接数据库的用户名对应密码
try {
// 1. 加载并注册JDBC驱动
Class.forName("com.mysql.cj.jdbc.Driver");
// 2. 打开连接
Connection conn = DriverManager.getConnection(url, user, password);
// 3. 创建PrepareStatement
String query = "SELECT username,password,name,sex,age FROM study where id is null LIMIT 1 FOR UPDATE";
PreparedStatement tmpstmt = conn.prepareStatement(query, ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
// 4. 执行语句
ResultSet rs = tmpstmt.executeQuery(query);
rs.insertRow();
rs.updateString("username", tmp_username);
rs.updateString("password", tmp_password);
rs.updateString("name", tmp_name);
rs.updateString("sex", tmp_sex);
rs.updateInt("age", tmp_age);
rs.updateRow();
rs.moveToInsertRow();
rs.refreshRow();
rs.moveToCurrentRow();
// 获取结果集中的数据
System.out.println("userName: " + tmp_username);
System.out.println("Password: " + tmp_password);
System.out.println("Name: " + tmp_name);
System.out.println("Sex: " + tmp_sex);
System.out.println("Age: " + tmp_age);
// ... 更多的列
System.out.println();
// 6. 关闭资源
tmpstmt.close();
conn.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
报错信息如下:
java.sql.SQLException: Not on insert row.
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:68)
at com.mysql.cj.jdbc.result.UpdatableResultSet.insertRow(UpdatableResultSet.java:733)
at RegisterAccount.DatabaseInsert(RegisterAccount.java:75)
at RegisterAccount.main(RegisterAccount.java:43)
排查及解决方案路径:
1、查看数据库的用户权限是否具有insert和update权限。在mysql中使用以下语句,
SELECT Insert_priv FROM mysql.user WHERE User = '你的用户名'@'主机' ;
结果截图如下:
发现该用户并没Insert_priv权限,需要执行以下语句修改其具有Insert_priv的权限。
GRANT INSERT ON study.study TO '你的用户名'@'主机';
如下图所示:显示赋予权限成功。
然后回到IDEA中执行原程序,发现仍未解决问题。
2、重新排查源程序,特别是链接数据库后的query查询字串
发现query查询字串语句也没有问题。
3、时间关系,换个实现思路。
此时回归到条条大路通罗马,换个思路,编程的目的在于实现功能,我的功能只是希望在表中增加一条记录,无论是否对查询结果记录集进行游标操作来用InsertRow来新增一条记录,还是不用查询 结果的记录集,而是直接对表进行insert into操作来解决,都是一样的功能,因此,直接改代码为Insert into来解决。对核心部分的函数进行全部修改如下(方式一):
public static void DatabaseInsert(String tmp_username, String tmp_password, String tmp_name, String tmp_sex, int tmp_age) {
String url = "jdbc:mysql://localhost:3306/study";
String user = "username"; //连接数据库的用户名
String password = "password"; //连接数据库的密码
try {
// 1. 加载并注册JDBC驱动
Class.forName("com.mysql.cj.jdbc.Driver");
// 2. 打开连接
Connection conn = DriverManager.getConnection(url, user, password);
// String query = "SELECT username,password,name,sex,age FROM study where id is null LIMIT 1 FOR UPDATE";
// 改用insert into实现对表新增记录
String query="insert into study (username,password,name,sex,age) value (?,?,?,?,?)";
// 3. 创建Statement
// 4. 执行语句
PreparedStatement tmpstmt = conn.prepareStatement(query, ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
// 插入一条新记录
tmpstmt.setString(1, tmp_username);
tmpstmt.setString(2, tmp_password);
tmpstmt.setString(3, tmp_name);
tmpstmt.setString(4, tmp_sex);
tmpstmt.setInt(5, tmp_age);
tmpstmt.executeUpdate();
// 获取结果集中的数据
System.out.println("userName: " + tmp_username);
System.out.println("Password: " + tmp_password);
System.out.println("Name: " + tmp_name);
System.out.println("Sex: " + tmp_sex);
System.out.println("Age: " + tmp_age);
// ... 更多的列
System.out.println();
// 6. 关闭资源
tmpstmt.close();
conn.close();
//以上方式为直接用insert处理
} catch (Exception e) {
e.printStackTrace();
}
}
编译执行之,完美解决问题。
在navicat查看远程mysql,查询全部发现user3记录,已经成功新增如下:
4、总结经验:所有编程原则,回归本质,“大道至简”。在时间紧迫关系下,用最直接简单的方式实现功能是最要紧的,难题的排查可以放在未来进行补充,希望这种思路能给一些朋友得到启发。
5、事后分析:
解决思路:
- 执行查询语句,该查询语句可能会返回一个空的结果集。
- 对这个空的结果集使用
rs.updateRow
来插入新行,这里存在问题,因为rs.updateRow
是用于更新当前结果集中的行,而不是插入新行。 - 对于插入新行,应该使用
PreparedStatement
的executeUpdate
方法。
错误分析:
rs.updateRow
用于更新从数据库中检索出来的结果集中的现有行的数据,而不是插入新行。当结果集为空时,没有行可供更新,并且使用rs.updateRow
进行插入操作是错误的用法。因此只能使用之前的insert into 语句及executeUpdate方法。- 因过往多年.net+mssql运行环境经验,现这java+mysqll方式有所区别,需要注意。
改进源码的另一种方式,如下:
将原来DatabaseInsert函数进行少许变化,如下(方式二):
public static void DatabaseInsert(String tmp_username, String tmp_password, String tmp_name, String tmp_sex, int tmp_age) {
String url = "jdbc:mysql://localhost:3306/study";
String user = "username"; //连接数据库的用户名
String password = "password"; //连接数据库的用户名对应密码
try {
// 1. 加载并注册JDBC驱动
Class.forName("com.mysql.cj.jdbc.Driver");
// 2. 打开连接
String query = "SELECT username,password,name,sex,age FROM study where id is not null LIMIT 1 FOR UPDATE";
String insert = "INSERT INTO study (username,password,name,sex,age) VALUES (?,?,?,?,?)";
Connection conn = DriverManager.getConnection(url, user, password);
// 3. 创建PrepareStatement
PreparedStatement tmpstmt = conn.prepareStatement(query, ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
PreparedStatement tmpinsert = conn.prepareStatement(insert, ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
// 4. 执行语句
ResultSet rs = tmpstmt.executeQuery(query);
if(rs.next()) {
tmpinsert.setString(1,tmp_username);
tmpinsert.setString(2,tmp_password);
tmpinsert.setString(3,tmp_name);
tmpinsert.setString(4,tmp_sex);
tmpinsert.setInt(5,tmp_age);
tmpinsert.executeUpdate();
System.out.println("生成记录成功");
}
else
{
System.out.println("生成记录失败");
}
// 获取结果集中的数据
System.out.println("userName: " + tmp_username);
System.out.println("Password: " + tmp_password);
System.out.println("Name: " + tmp_name);
System.out.println("Sex: " + tmp_sex);
System.out.println("Age: " + tmp_age);
// ... 更多的列
System.out.println();
// 6. 关闭资源
tmpstmt.close();
tmpinsert.close();
conn.close();
}
catch (Exception e) {
e.printStackTrace();
}
}
其中重点改进地方是在,方式二与方式一中的区别在于增加一个对表状态的判断,空表给不给新增行而已。根本也是利用Insert into语句实现新增记录: