JDBC (java语言连接数据库)是sun公司制定的一套接口
JDBC 代码编写的六步
1、注册驱动
2、获取连接
3、获取数据库操作对象
4、执行sql语句
5、处理查询结果集
6、释放资源
1、注册驱动
有两种方式:
//第一种
Driver driver=new com.mysql.jdbc.Driver();
DriverManager.registerDriver(driver);
//第二种注册方式(常用)
Class.forName("com.mysql.jdbc.Driver");
在编写JDBC的时候可以通过使用资源绑定器(xxx.properties) 绑定属性配置文件和工具类的创建去让代码看起来更简介方便。
将连接数据库的所有信息配置到文件当中xxx.properties,因为实际开发中不建议把连接数据库的信息 写死到java程序中
案例:模拟用户登录
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.*;
/**
* 实现功能
* 1. 需求:模拟用户登录功能的实现
* 2. 业务描述:
* 程序运行的 时候提供一个输入的入口 让用户可以输入用户名和密码
* 用户输入后提交信息 java程序收集到用户信息
* java程序验证用户名和密码是否合法
* 合法:显示登录成功
* 不合法:显示登录失败
* 3. 数据的准备
* 实际开发中表的设计会使用专业的建模工具
* +----+----------+-----------+----------+
* | id | loginPwd | loginName | realName |
* +----+----------+-----------+----------+
* | 1 | 111 | 1 | 1 |
* | 2 | 222 | 2 | 2 |
* +----+----------+-----------+----------+
* 4.当前程序存在的问题
* 用户名:fdsa
* 密码:fdsa' or '1'='1
* 登录成功!
* 这种现象叫做SQL注入(安全隐患)(黑客经常使用)
* 5.导致SQL注入的主要原因
* where loginPwd = 'fdsa' or '1'='1'
* 这样会导致sql 的代码变成上面那样 因为1=1 恒成立 所以会查询到所有字段
* 用户输入的信息含有sql语句的关键字,
* 而最重要的是这些关键字参与了sql语句 的编译过程
* 导致sql语句原意扭曲 进而达到sql注入
*/
public class JDBC_Test06 {
public static void main(String[] args) {
//初始化一个界面
/* 该方法要返回一个容器 既可以存用户名 又可以存密码*/
Map<String,String> userLoginInfo = initUI();
//验证用户名loginName和密码loginPwd
boolean loginResult = login(userLoginInfo);
System.out.println(loginResult ? "登录成功!":"登录失败!");
}
/**
* 初始化用户界面
* @return loginName loginPwd 等登录信息
*/
private static Map<String,String> initUI(){
Scanner scanner=new Scanner(System.in);
System.out.print("用户名:");
String loginName = scanner.nextLine();
System.out.print("密码:");
String loginPwd = scanner.nextLine();
Map<String,String> userLoginInfo = new HashMap<>();
userLoginInfo.put("loginName",loginName);
userLoginInfo.put("loginPwd",loginPwd);
return userLoginInfo;
}
/**
*
* @param userLoginInfo
* @return boolean
*/
private static boolean login(Map<String,String> userLoginInfo){
//打标记
boolean loginResult = false;
/*
P_JDBC_Test01.properties中的代码
driver = com.mysql.jdbc.Driver
url = jdbc:mysql://localhost:3306/bjpowernode
user = root
password = 020826
*/
//JDBC代码
Connection connection = null;
Statement statement=null;
ResultSet resultSet=null;
ResourceBundle resourceBundle = ResourceBundle.getBundle("P_JDBC_Test01");
String driver = resourceBundle.getString("driver");
String url = resourceBundle.getString("url");
String user = resourceBundle.getString("user");
String password = resourceBundle.getString("password");
try {
//1. 注册驱动
Class.forName(driver);
//2. 获取连接
connection = DriverManager.getConnection(url,user,password);
//3. 获取数据操作对象
statement = connection.createStatement();
//4. 执行sql
/* String sql = "select * from t_user where loginName = 'xxx' and loginPwd = ''";*/
/*
变量拼到一个字符串 重点
loginName = '"+userLoginInfo.get("loginName")+"'
*/
String sql = "select * from t_user where loginName = '"+userLoginInfo.get("loginName")+"' and loginPwd = '"+userLoginInfo.get("loginPwd")+"'";
//上面这行进行了sql语句的拼接 下面这行发送sql语句到DBMS DBMS 进行sql 编译
//正好将用户提供的非法信息编译进去 导致了原sql语句含义改变
resultSet = statement.executeQuery(sql);
//5. 处理查询结果集
/* 这个例子中 用户不匹配查不到记录 用户匹配了也只能查到1条记录 所以不需要用while 用if就可与*/
if (resultSet.next()){
//如果为true 说明结果集中又数据 这个例子中 有数据代表用户匹配
loginResult = true;
}
}catch (Exception e){
e.printStackTrace();
}finally {
//6. 释放资源
try {
if (resultSet!=null){
resultSet.close();
}
}catch (Exception e){
e.printStackTrace();
}
try {
if (statement != null) {
statement.close();
}
}catch (Exception e){
e.printStackTrace();
}
try {
if (connection != null) {
connection.close();
}
}catch (Exception e){
e.printStackTrace();
}
}
return loginResult;
}
}
上述案例存在sql注入现象
解决方法
使用java.sql.preparedStatement(预编译的数据库操作对象)
原理:
preparedStatement预先对sql语句进行了编译 然后再给SQL语句传"值"
用preparedStatement后第三步获取数据库操作对象和第四步执行sql会发生变化
//3. 获取预编译的数据操作对象
// ? 占位符 1个?将来会接收1个值
// SQL语句的框架
String sql = "select * from t_user where loginName = ? and loginPwd = ? ";
//程序执行到此会发送sql语句到DBMS 然后DBMS会进行sql语句的预编译
/*prepareStatement 这个没有 d */
preparedStatement = connection.prepareStatement(sql);
//给 占位符 ? 传值 第一个 ? 下标是1 第二个是2 JDBC中下标从1开始
preparedStatement.setString(1,userLoginInfo.get("loginName"));
preparedStatement.setString(2,userLoginInfo.get("loginPwd"));
//4. 执行sql
resultSet = preparedStatement.executeQuery();
对比 Statement 和 preparedStatement
Statement 存在SQL注入问题
Statement 编译一次 执行一次
preparedStatement 编译一次 执行n次
preparedStatement大多情况使用 ,只有极少数情况使用Statement,当业务必须需要sql语句拼接 或sql注入的时候。