JDBC的全称为:java database connectivity (java和 数据库的连接 )
JDBC六步
- 注册驱动
- 建立数据库的连接
- 通过con连接获取到Statement实例
- statement执行sql语句,查询到的结果集到ResultSet实例
- 从结果集中获取数据
- 释放资源
package JDBC;
import com.mysql.jdbc.Driver;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.*;
import java.util.Properties;
public class JDBCTest {
public static void main(String[] args) throws SQLException, ClassNotFoundException {
Connection con = null;
Statement statement = null;
try {
//获取连接
con = getConnection();
//创建statement
statement = con.createStatement();
// 执行添加语句
String sql = "insert into user values('116','zhaoliu',28)";
statement.executeUpdate(sql);
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
//释放资源
release(con, statement, null);
}
}
private static String driverClass = "";
private static String url = "";
private static String username = "";
private static String password = "";
//解析properties文件只需要执行一次,所以放在静态代码块中
static{
//首先创建Properties对象
Properties properties = new Properties();
//加载文件
try {
properties.load(new FileInputStream("src/jdbc.properties"));
//获取参数
driverClass = properties.getProperty("driverClass");
url = properties.getProperty("url");
username = properties.getProperty("username");
password = properties.getProperty("password");
Class.forName(driverClass);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static Connection getConnection(){
Connection con = null;
try {
//2.获取连接
con = DriverManager.getConnection(url, username, password);
} catch (SQLException e) {
e.printStackTrace();
}
return con;
}
//释放资源
public static void release(Connection con,Statement st,ResultSet rs){
//6.释放资源:先开后关,后开先关,最好放在finally代码块中
if(rs!=null){
try {
rs.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(st!=null){
try {
st.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(con!=null){
try {
con.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
driverClass=com.mysql.jdbc.Driver
url = jdbc:mysql://localhost:3306/mybd
username = root
password = root
2.sql注入问题
sql注入:由于没有对用户输入进行充分检查,而SQL又是拼接而成,在用户输入参数时,在参数中添加一些SQL 关键字,达到改变SQL运行结果的目的,也可以完成恶意攻击。
案例:模拟登陆
执行如下sql语句:创建一个day06数据库,创建一张user表,表中插入一条数据。
create table user(
id varchar(10) primary key,
username varchar(10),
password varchar(10)
);
insert into user values('1','admin','123456');
package JDBC;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class Login {
public static void main(String[] args) {
String username = "admin' -- ";
String password = "123";
Connection con = null;
Statement statement = null;
ResultSet resultSet = null;
try {
//首先获取连接
con = JDBCUtil.getConnection();
//创建statement对象
statement = con.createStatement();
//发送sql语句,查询数据库是否存在这个用户
String sql = "select count(*) from user where username='" + username + "' and password = '" + password + "'";
resultSet = statement.executeQuery(sql);
int count = 0;
while (resultSet.next()) {
count = resultSet.getInt(1);
}
//如果不存在
if (count == 0) {
System.out.println("用户登录失败");
} else {
//存在
System.out.println("用户登录成功");
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
JDBCUtil.release(con, statement, resultSet);
}
}
}
执行代码,可以发现用户名为 admin’ – ,密码为 1234 这样的用户是不存在的,但是尽然可以登录成功。那么这是什么原因造成的呢?
将字符串拼接后的sql语句放在可视化工具中查看发现如下情况:
可以发现,后面密码的验证被注释掉了。这样岂不是谁都可以登录了?
问题根本原因:
语句和用户输入的内容进行拼接,发送给数据库编译的时候,数据库将用户输入的内容当成sql语句编译了。
从而从根本上改变了我们开发者所期望sql语句原有的含义。导致程序受到sql攻击。那么原因知道了,我们怎么解决的?
问题的解决方案:
我们需要首先将sql语句所代表的含义确定下来,然后再向编译好的sql语句中填充用户输入的内容。这样用户输入的内容就不会再被编译了。所以需要我们学习preparedStatement向数据库发送预编译的sql语句。
2.1 PreparedStatement解决sql注入 重要步骤:
PreparedStatement是Statement的子接口,它的实例对象可以通过调用Connection.preparedStatement(sql)方法获得
注意:sql提前创建好的。sql语句中需要参数。使用?进行占位。
- conn.prepareStatement(sql);
需要你事先传递sql。如果sql需要参数,使用?进行占位。 - 设置参数(执行sql之前):prepStmt.setXXX(int index, 要放入的值)
根据不同类型的数据进行方法的选择。第一个参数表示的是?出现的位置。从1开始计数,有几个问号,就需要传递接个参数。方法的参数说明:
第一个参数:int index ;表示的是问号出现的位置。 问号是从1开始计数
第二个参数:要问号的位置传入的值。 - 执行,不需要在传递sql了。
prepStmt.executeQuery();—执行select
prepStmt.executeUpdate();—执行insert,delete,update
package JDBC;
import java.sql.*;
public class Login {
public static void main(String[] args) {
String username = "admin";
String password = "123456";
Connection con = null;
PreparedStatement prepareStatement = null;
ResultSet resultSet = null;
try {
//首先获取连接
con = JDBCUtil.getConnection();
String sql = "select COUNT(*) from user where username=? and password = ?";
//创建preparedStatement对象,对sql进行编译
prepareStatement = con.prepareStatement(sql);
// 设置参数
prepareStatement.setString(1, username);
prepareStatement.setString(2, password);
resultSet = prepareStatement.executeQuery();
int count = 0;
while (resultSet.next()) {
count = resultSet.getInt(1);
}
//如果不存在
if (count == 0) {
System.out.println("用户登录失败");
} else {
//存在
System.out.println("用户登录成功");
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
JDBCUtil.release(con, prepareStatement, resultSet);
}
}
}