PreparedStatement 和 元数据处理
1. PreparedStatement
interface java.sql.PreparedStatement extends Statement
数据库 SQL 语句预处理搬运工对象
获取 PreparedStatement 对象涉及到的方法
java.sql.Connection 调用
java.sql.PreparedStatement prepareStatement(String sql);
通过 数据库 Connection 连接对象调用 prepareStatement 传入参数是预处理 SQL 语句,得到
PreparedStatement
方法:
void setObject(int index, Object value);
给予 SQL 语句赋值操作,index 是下标,value是给予对应下标参数的赋值数据
int executeUpdate();
执行 update insert delete
java.sql.ResultSet executeQuery();
执行 select
PreparedStatement 可以避免 SQL 注入问题
2. SQL注入问题
package com.qfedu.a_jdbc;
import util.JdbcUtils;
import java.sql.*;
public class Demo1 {
private static String username = "张三";
private static String password = "北平没有王乾帅' or 1=1 -- ";
public static void main(String[] args) {
testStatement();
testPreparedStatement();
}
public static void testStatement() {
Connection connection = JdbcUtils.getConnection();
Statement statement = null;
ResultSet resultSet = null;
try {
statement = connection.createStatement();
String sql = "select * from javaee_2203.user where name = '" + username + "' and password = '" + password + "'";
System.out.println(sql);
resultSet = statement.executeQuery(sql);
if (resultSet.next()) {
System.out.println("Statement login success");
} else {
System.err.println("Statement login failed");
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
JdbcUtils.close(connection, statement, resultSet);
}
}
public static void testPreparedStatement() {
Connection connection = JdbcUtils.getConnection();
ResultSet resultSet = null;
PreparedStatement statement = null;
String sql = "select * from javaee_2203.user where name = ? and password = ?";
try {
statement = connection.prepareStatement(sql);
statement.setObject(1, username);
statement.setObject(2, password);
resultSet = statement.executeQuery();
if (resultSet.next()) {
System.out.println("PreparedStatement login success");
} else {
System.err.println("PreparedStatement login failed");
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
JdbcUtils.close(connection, statement, resultSet);
}
}
}
3. PreparedStatement 完成 CRUD 操作
3.1 PreparedStatement 操作 insert
@Test
public void testInsert() {
Connection connection = JdbcUtils.getConnection();
PreparedStatement statement = null;
String sql = "insert into javaee_2203.student(name, age, score) VALUES (?,?,?)";
try {
statement = connection.prepareStatement(sql);
int parameterCount = statement.getParameterMetaData().getParameterCount();
System.out.println(parameterCount);
statement.setObject(1, "大哥");
statement.setObject(2, "15");
statement.setObject(3, "100");
int affectedRows = statement.executeUpdate();
System.out.println("数据库受到影响的行数 : " + affectedRows);
} catch (SQLException e) {
e.printStackTrace();
} finally {
JdbcUtils.close(connection, statement);
}
}
3.2 PreparedStatement 操作 update
@Test
public void testUpdate() {
Connection connection = JdbcUtils.getConnection();
PreparedStatement statement = null;
String sql = "update javaee_2203.student set name = ? where id = ?";
try {
statement = connection.prepareStatement(sql);
statement.setObject(1, "雪珂");
statement.setObject(2, 10);
int affectedRows = statement.executeUpdate();
System.out.println("数据库受到影响的行数 : " + affectedRows);
} catch (SQLException e) {
e.printStackTrace();
} finally {
JdbcUtils.close(connection, statement);
}
}
3.3 PreparedStatement 操作 delete
@Test
public void testDelete() {
Connection connection = JdbcUtils.getConnection();
PreparedStatement statement = null;
String sql = "delete from javaee_2203.student where id = ?";
try {
statement = connection.prepareStatement(sql);
statement.setObject(1, 9);
int affectedRows = statement.executeUpdate();
System.out.println("数据库受到影响的行数 : " + affectedRows);
} catch (SQLException e) {
e.printStackTrace();
} finally {
JdbcUtils.close(connection, statement);
}
}
3.4 PreparedStatement 操作 select 一行数据
@Test
public void testSelectOne() {
Connection connection = JdbcUtils.getConnection();
ResultSet resultSet = null;
PreparedStatement statement = null;
String sql = "select * from javaee_2203.student where id = ?";
try {
statement = connection.prepareStatement(sql);
statement.setObject(1, 10);
resultSet = statement.executeQuery();
Student stu = null;
if (resultSet.next()) {
int id = resultSet.getInt("id");
String name = resultSet.getString("name");
int age = resultSet.getInt("age");
float score = resultSet.getFloat("score");
stu = new Student(id, name, age, score);
}
System.out.println(stu);
} catch (SQLException e) {
e.printStackTrace();
} finally {
JdbcUtils.close(connection, statement, resultSet);
}
}
3.5 PreparedStatement 操作 select 多行数据
@Test
public void testSelectAll() {
Connection connection = JdbcUtils.getConnection();
PreparedStatement statement = null;
ResultSet resultSet = null;
String sql = "select * from javaee_2203.student";
try {
statement = connection.prepareStatement(sql);
resultSet = statement.executeQuery();
ArrayList<Student> list = new ArrayList<>();
while (resultSet.next()) {
int id = resultSet.getInt("id");
String name = resultSet.getString("name");
int age = resultSet.getInt("age");
float score = resultSet.getFloat("score");
list.add(new Student(id, name, age, score));
}
list.forEach(System.out::println);
} catch (SQLException e) {
e.printStackTrace();
} finally {
JdbcUtils.close(connection, statement, resultSet);
}
}
4. 元数据
4.1 参数元数据
通过 java.sql.Connection 数据库连接对象获取 PreparedStatement 对象,需要提供 SQL 语句。
在 PreparedStatement 对象是包含 SQL 语句内容。
并且 PreparedStatement可以通过 setXXX(int parameterIndex, XXX value) 对 SQL 语句进行赋值操作。
分析:
1. PreparedStatement 包含 SQL 全部内容
2. PreparedStatement 是已知参数个数,才可以执行 setXXX(int parameterIndex, XXX value) 执行
断点分析
PreparedStatement 对象中有以下内容
4.2 update 方法封装
public int update(String sql, Object... parameterValues) throws SQLException {
Connection connection = JdbcUtils.getConnection();
PreparedStatement statement = null;
assert sql != null;
statement = connection.prepareStatement(sql);
int parameterCount = statement.getParameterMetaData().getParameterCount();
if (!(parameterCount != parameterValues.length || 0 == parameterCount)) {
for (int i = 0; i < parameterCount; i++) {
statement.setObject(i + 1, parameterValues[i]);
}
}
int i = statement.executeUpdate();
JdbcUtils.close(connection, statement);
return i;
}
4.3 结果集元数据
ResultSetMetaData metaData = resultSet.getMetaData();
int columnCount = metaData.getColumnCount();
String fieldName = metaData.getColumnName(i);
4.4 数据库查询结果为符合JavaBean规范对象方式
public <T> List<T> query(String sql, Class<T> cls, Object... parameterValues)
throws SQLException, NoSuchMethodException, IllegalAccessException,
InvocationTargetException, InstantiationException, NoSuchFieldException {
Connection connection = JdbcUtils.getConnection();
assert sql != null;
PreparedStatement statement = connection.prepareStatement(sql);
int parameterCount = statement.getParameterMetaData().getParameterCount();
if (!(0 == parameterCount || parameterCount != parameterValues.length)) {
for (int i = 0; i < parameterCount; i++) {
statement.setObject(i + 1, parameterValues[i]);
}
}
ResultSet resultSet = statement.executeQuery();
List<T> list = new ArrayList<>();
ResultSetMetaData metaData = resultSet.getMetaData();
int columnCount = metaData.getColumnCount();
while (resultSet.next()) {
T t = cls.getConstructor().newInstance();
for (int i = 1; i <= columnCount; i++) {
String fieldName = metaData.getColumnName(i);
String value = resultSet.getString(fieldName);
BeanUtils.setProperty(t, fieldName, value);
}
list.add(t);
}
return list;
}
5. 查询结果数据分析
5.1 查询数据对应 Java 数据存储形式
JavaBean 对象是大家可以接受的方式,但是不是所有的查询结构都可以做成对象方式来存储
例如:
多表查询结果
单表统计结果
单表某一个字段数据
有没有什么类型建议???
ResultSet ==> 方便 Java 操作的数据
ResultSet ==> JavaBean
ResultSet ==> Map
ResultSet ==> Object[]
六个方法:
T queryBean
List<T> queryBeanList
Map<String, Object> queryMap
List<Map<String, Object>> queryMapList
Object[] queryArray
List<Object[]> queryArrayList
需要封装一个 ResultSetHandler 处理器接口,插件式编程
5.2 查询结果数据行对应 Map<String, Object> 双边队列
public Map<String, Object> queryMap(String sql, Object... parameterValues);
Map 双边队列
Key => String 类型 键 ==> 字段
Value = > Object 类型 值 ==> 字段对应数据 Object 类型包容万象,使用方便。
一行数据 ==> Map 双边队列
id |
name |
age |
gender |
1 |
段某某 |
16 |
男 |
Map<String, Object>
数据形式
"id"=1,
"name"="段某某",
"age"=16,
"gender"="男"
public Map<String, Object> queryMap(String sql, Object... parameterValues) throws SQLException {
Connection connection = JdbcUtils.getConnection();
assert sql != null;
PreparedStatement statement = connection.prepareStatement(sql);
int parameterCount = statement.getParameterMetaData().getParameterCount();
if (!(0 == parameterCount || parameterCount != parameterValues.length)) {
for (int i = 0; i < parameterCount; i++) {
statement.setObject(i + 1, parameterValues[i]);
}
}
ResultSet resultSet = statement.executeQuery();
ResultSetMetaData metaData = resultSet.getMetaData();
int columnCount = metaData.getColumnCount();
HashMap<String, Object> map = new HashMap<>();
if (resultSet.next()) {
for (int i = 1; i <= columnCount ; i++) {
map.put(metaData.getColumnName(i), resultSet.getObject(i));
}
}
return map;
}
5.3 查询结果数据行对应 Object[] 数组
id |
name |
age |
gender |
1 |
段某某 |
16 |
男 |
查询结果数据行,每一行数据值,对应一个 Object类型数组
Object[] values = {1, "段某某", 16, "男"};
查询结果解析到使用过程,完全明确字段的查询顺序,查询目标,直接按照数组下标使用。
public Object[] queryArray(String sql, Object... parameterValues) throws SQLException
public Object[] queryArray(String sql, Object... parameterValues) throws SQLException {
Connection connection = JdbcUtils.getConnection();
assert sql != null;
PreparedStatement statement = connection.prepareStatement(sql);
int parameterCount = statement.getParameterMetaData().getParameterCount();
if (!(0 == parameterCount || parameterCount != parameterValues.length)) {
for (int i = 0; i < parameterCount; i++) {
statement.setObject(i + 1, parameterValues[i]);
}
}
ResultSet resultSet = statement.executeQuery();
int columnCount = resultSet.getMetaData().getColumnCount();
Object[] arr = new Object[columnCount];
if (resultSet.next()) {
for (int i = 0; i < arr.length; i++) {
arr[i] = resultSet.getObject(i + 1);
}
}
JdbcUtils.close(connection, statement, resultSet);
return arr;
}
· 使用C#创建一个MCP客户端
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现