「这是我参与2022首次更文挑战的第35天,活动详情查看:2022首次更文挑战」
四、ResultSet
ResultSet(结果集对象)作用:
1.封装了DQL查询语句的结果
ResultSet stmt.executeQuery(sql):执行DQL语句,返回ResultSet对象
获取查询结果
boolean next():(1)将光标从当前位置向前移动一行(2)判断当前行是否是有效行
返回值:当前行有数据返回true,当前没数据返回false。
xxx getXxx(参数):获取数据
解释:xxx表示数据类型;如int getInt(参数);String getString(参数);
参数:对于int是列的编号,从1开始,对于String是列的名称。
使用步骤:
1、游标向下移动一行,并判断该行是否有数据:next()
2、获取数据:getXxx(参数)
示例:
while(rs.next()){
rs.getXxx(参数);
}
实例:
package com.jdbc;
import com.mysql.jdbc.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
public class JDBCDemo3_ResultSet {
public static void main(String[] args) throws Exception {
//1、注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2、获取连接
//url的格式是:"jdbc:mysql://mysql的ip:端口号/操作的数据库"
String url="jdbc:mysql://127.0.0.1:3306/kc_db01";
//username是你的mysql用户名
String username="root";
//password是你的mysql密码
String password="123456";
Connection conn= (Connection) DriverManager.getConnection(url, username, password);
//3、定义sql
String sql="select *from emp";
//4、获取执行sql的Statement对象
Statement stmt=conn.createStatement();
//5、执行sql语句
ResultSet rs=stmt.executeQuery(sql);
//6处理结果
while(rs.next()){
//获取数据 getXxx();括号中可以写所在行,也可以写列名
int id=rs.getInt(1);
String ename=rs.getString(2);
int salary=rs.getInt(3);
//另一种写法:写行的名称
// int id=rs.getInt("id");
// String ename=rs.getString("ename");
// int salary=rs.getInt("salary");
System.out.println(id);
System.out.println(ename);
System.out.println(salary);
System.out.println("-----------");
}
//7、释放资源(先开后释放)
rs.close();
stmt.close();
conn.close();
}
}
数据库中emp表
运行之后:
ResultSet案例
需求:查询account账户数据,封装为Account对象中,并且存储到ArrayList集合中
创建一个pojo包,用来存放对象的。
创建了一个类,提供getSet方法
package com.pojo;
public class Account {
private int id;
private String ename;
private int salary;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getEname() {
return ename;
}
public void setEname(String ename) {
this.ename = ename;
}
public int getSalary() {
return salary;
}
public void setSalary(int salary) {
this.salary = salary;
}
//为了更好的显示,重写toString()方法
@Override
public String toString() {
return "Account{" +
"id=" + id +
", ename='" + ename + '\'' +
", salary=" + salary +
'}'+"\n";
}
}
jdbc包下创建的类中
package com.jdbc;
import com.mysql.jdbc.Connection;
import com.pojo.Account;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
public class JDBCDemo4_ResultSet {
public static void main(String[] args) throws Exception {
//1、注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2、获取连接
//url的格式是:"jdbc:mysql://mysql的ip:端口号/操作的数据库"
String url="jdbc:mysql://127.0.0.1:3306/kc_db01";
//username是你的mysql用户名
String username="root";
//password是你的mysql密码
String password="123456";
Connection conn= (Connection) DriverManager.getConnection(url, username, password);
//3、定义sql
String sql="select *from emp";
//4、获取执行sql的Statement对象
Statement stmt=conn.createStatement();
//5、执行sql语句
ResultSet rs=stmt.executeQuery(sql);
//6处理结果
//创建集合对象
List<Account> list=new ArrayList<>();
while(rs.next()){
//创建Account对象
Account account=new Account();
//获取数据 getXxx();括号中可以写所在行,也可以写列名
int id=rs.getInt(1);
String ename=rs.getString(2);
int salary=rs.getInt(3);
//赋值数据
account.setId(id);
account.setEname(ename);
account.setSalary(salary);
list.add(account);
}
System.out.println(list);
//7、释放资源(先开后释放)
rs.close();
stmt.close();
conn.close();
}
}
运行结果:
五、PreparedStatement
PreparedStatement作用:
1、预编译SQL语句并执行:预防SQL注入问题
SQL注入
SQL注入是通过操作输入来修改事先定义好的SQL语句,用以达到执行代码对服务器进行攻击的方法。
演示普通登录:
首先数据录kc_db1下的emp表为:
package com.jdbc;
import com.mysql.jdbc.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
public class JDBCDemo5_UserLogin {
public static void main(String[] args) throws Exception {
//2、获取连接
//url的格式是:"jdbc:mysql://mysql的ip:端口号/操作的数据库"
String url="jdbc:mysql://127.0.0.1:3306/kc_db01";
//username是你的mysql用户名
String username="root";
//password是你的mysql密码
String password="123456";
Connection conn= (Connection) DriverManager.getConnection(url, username, password);
String name="zhangsan";
String pwd="1233";
//3、定义sql
String sql="select *from emp where ename='"+name+"'and password='"+pwd+"'" ;
System.out.println("这条SQL语句是:"+sql);
//4、获取执行sql的Statement对象
Statement stat=conn.createStatement();
//5、执行sql语句
ResultSet rs = stat.executeQuery(sql);
//6处理结果,rs有值说明查找成功
if(rs.next()){
System.out.println("登录成功");
}else{
System.out.println("登录失败");
}
//7、释放资源(先开后释放)
rs.close();
stat.close();
conn.close();
}
}
运行结果:
输入其他(不成功的原因是数据库中没有账号密码为这个的):
sql注入演示:
对于这条sql语句来说不点在于密码,账号任意
String name="随便写的名";
String pwd=" ' or '1'='1";
运行结果:
这条SQL语句是:select *from emp where ename='随便写的名'and password=' ' or '1'='1'
sql注入的本质就是改变原有的SQL语句,加入or之后1=1恒为真,所以这条语句就是true
PreparedStatement解决SQL注入
①获取PreparedStatement对象
//sql语句中的参数,使用?占位符代替
String sql="select *from user where username=? and password=?";
//通过Connection对象获取,并传入对应的sql语句
PreparedStatement pstmt=conn.prepareStatement(sql);
②设置参数
PreparedStatement对象:setXxx(参数1,参数2):表示给参数1(?的位置)赋值为参数2
Xxx:数据类型;任意setInt(参数1,参数2)
参数:
- 参数1:表示?的位置编号,从1开始
- 参数2: ?的值
③执行sql
executeUpdate();/excuteQuery();括号内不需要传递sql。
创建类:
package com.jdbc;
import com.mysql.jdbc.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Scanner;
public class JDBCDemo5_UserLogin {
public static void main(String[] args) throws Exception {
//2、获取连接
//url的格式是:"jdbc:mysql://mysql的ip:端口号/操作的数据库"
String url="jdbc:mysql://127.0.0.1:3306/kc_db01";
//username是你的mysql用户名
String username="root";
//password是你的mysql密码
String password="123456";
Connection conn= (Connection) DriverManager.getConnection(url, username, password);
String name="随便写的名";
String pwd=" ' or '1'='1";
//3、定义sql
String sql="select *from emp where ename=? and password=?" ;
//4、获取的PreparedStatement对象
PreparedStatement pstmt = conn.prepareStatement(sql);
//设置参数
pstmt.setString(1, name);
pstmt.setString(2, pwd);
//5、执行sql语句
ResultSet rs =pstmt.executeQuery();
System.out.println("这条SQL语句是:"+sql);
//6处理结果,rs有值说明查找成功
if(rs.next()){
System.out.println("登录成功");
}else{
System.out.println("登录失败");
}
//7、释放资源(先开后释放)
rs.close();
pstmt.close();
conn.close();
}
}
运行结果:
这样就防止了sql注入,setXxx会对传入的参数会进行转义,不会拼接成字符串而是\' or\ ' 1\' = \' 1\'
输入正确的:
PrepareStatement原理
PrepareStatement好处:
1、预编译SQL,性能更高
2、防止sql注入。
my.ini配置文件可以看到日志
log-output=FILE
general-log=1
general_log_file="D:\mysql.log"
slow-query-log=1
slow_query_log_file="D:\mysql_slow.log"
long_query_time=2
预编译功能默认关闭
①:PreparedStatement预编译功能开启:userServerPrepStmts=true
在sql语句?之后书写参数
String url="jdbc:mysql://127.0.0.1:3306/kc_db01"?userServerPrepStmts=true;
开启就会要prepare预编译:
关闭之后就没有Prepare阶段