JDBC
Java DataBase Connectivity(Java语言连接数据库)
JDBC开发前准备工作:从数据库官网下载驱动jar包,将其配置在环境变量classpath当中。(使用IDEA工具,不需要配置)
·······································································································
JDBC编程六步(需背会)
第一步:注册驱动(告诉Java程序连接哪个品牌的数据库)
第二步:获取连接(JVM进程和数据库进程之间的通道打开了,这属于进程之间的通信,重量级,使用完一定要关闭)
第三步:获取数据库操作对象(专门执行sql语句的对象)
第四步:执行SQL语句(DQL DML…)
第五步:处理查询结果集(只有当第四步执行的是select语句的时候,才会有这第五步处理查询结果集。)
第六步:释放资源(Java和数据库之间是进程通信,使用完一定要关闭。)
把Connection和Statement移到try外面定义 方便之后finally语句块进行关闭操作。
注册驱动的第二种方式(常用)
Class.forName(“com.mysql.jdbc.Driver”);
结果集参与 执行图如下:
JDBC中所有下标从1开始,取第一行数据如下:
显示所有的数据:
此括号中1、2、3也可以替换成列名,如"empno",“ename”,"sal"使程序更健壮(此处列名为查询结果的列名,而不一定是数据库的列名
)。
使用IDEA开发JDBC配置驱动
1.在官网https://dev.mysql.com/downloads/connector/j/下载驱动包。
2.在IDEA的项目模块中右击,选择Open Module Settings
点击libraries,添加java库
然后在路径中找到下载的jar包导入后即可。
·····························································································································
测试:
package com.tospo.test;
public class JdbcTest {
public static void main (String[] args){
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
报错:
Loading class
com.mysql.jdbc.Driver'. This is deprecated. The new driver class is
com.mysql.cj.jdbc.Driver’. The driver is
automatically registered via the SPI and manual loading of the driver
class is generally unnecessary.
错误原因:提示信息表明数据库驱动com.mysql.jdbc.Driver’已经被弃用了、应当使用新的驱动com.mysql.cj.jdbc.Driver,更改后执行成功!
··································································································································
案例:
代码如下:
```java
package com.tospo.test;
import java.sql.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
public class JdbcTest {
public static void main(String[] args) {
//初始化界面
Map<String,String> userLoginInfo = initUI();
//验证用户名和密码
boolean loginSuccess = login(userLoginInfo);
//输出最后结果
System.out.println(loginSuccess? "登录成功":"登录失败");
}
/**
* 用户登录
* @param userLoginInfo 用户登录信息
* @return false表示失败,true表示成功
*/
private static boolean login(Map<String, String> userLoginInfo) {
boolean loginSuccess =false;
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
String loginName = userLoginInfo.get("loginName");
String loginPwd = userLoginInfo.get("loginPwd");
try {
//1.注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//2.获取连接
conn= DriverManager.getConnection("jdbc:mysql://localhost:3306/firstdatabase","root","root");
//3.获取数据库操作对象
stmt= conn.createStatement();
//4.sql语句
String sql = "select * from t_user where username ='"+loginName+"' and password= '"+loginPwd+"'";
//5处理查询结果集
rs = stmt.executeQuery(sql);
if(rs!=null){
//结果集里面有数据,登录成功
loginSuccess=true;
}
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
} finally {
//6.释放资源
if(rs!=null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(stmt!=null){
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn!=null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
return loginSuccess;
}
/**
* 初始化用户界面
* @return 用户输入的用户名和密码等登录信息
*/
private static Map<String,String> initUI() {
Scanner s = new Scanner (System.in);
System.out.println("请输入用户名");
String loginName = s.nextLine();
System.out.println("请输入密码");
String loginPwd = s.nextLine();
Map<String,String> userLoginInfo = new HashMap<>();
userLoginInfo.put("loginName",loginName);
userLoginInfo.put("loginPwd",loginPwd);
return userLoginInfo;
}
}
数据库中无数据的情况下,控制台输入123 123 ,结果仍然为登录成功。
异常原因:5.处理结果集时,判断rs集合中是否有数据语句错误,应该改成
if(rs.next())
更改完后,正常运行!
··································································································································上方程序存在SQL注入问题,即在工作台随意输入 sfa sfa’ or ‘1’=1 也能登录成功,如下图所示(黑客经常使用):
SQL注入
用户输入的信息中含有sql语句的关键字 ,并且这些关键字参与sql语句的编译过程,导致sql语句的原意被扭曲,进而达到sql注入。
解决这个问题:只要用户提供的信息不参与SQL语句的编译过程,问题就解决了。
即使用户提供的信息中含有SQL语句的关键字,但是没有参与编译,不起作用。
所以使用预编译:java.sql.PreparedStatement
PreparedStatement接口继承了java.sql.Statement
PreparedStatement属于预编译的数据库操作对象
代码如下:
package com.tospo.test;
import java.sql.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
public class JdbcTest {
public static void main(String[] args) {
//初始化界面
Map<String,String> userLoginInfo = initUI();
//验证用户名和密码
boolean loginSuccess = login(userLoginInfo);
//输出最后结果
System.out.println(loginSuccess? "登录成功":"登录失败");
}
/**
* 用户登录
* @param userLoginInfo 用户登录信息
* @return false表示失败,true表示成功
*/
private static boolean login(Map<String, String> userLoginInfo) {
boolean loginSuccess =false;
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
String loginName = userLoginInfo.get("loginName");
String loginPwd = userLoginInfo.get("loginPwd");
try {
//1.注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//2.获取连接
conn= DriverManager.getConnection("jdbc:mysql://localhost:3306/firstdatabase","root","root");
//3.获取预编译的数据库操作对象
//其中一个?表示一个占位符 ,一个“?”将来接收“值” 注意:占位符不能使用单引号括起来!
String sql = "select * from t_user where username =? and password=?";
//程序执行到此处,会发送sql语句框子给DBMS,然后DBMS进行SQL语句的预先编译
ps = conn.prepareStatement(sql);
//给占位符?传值(第一个?下标为1,第二个?为2)
ps.setString(1,loginName);
ps.setString(2,loginPwd);
//4.sql语句
rs= ps.executeQuery();
//5处理查询结果集
if(rs.next()){
//结果集里面有数据,登录成功
loginSuccess=true;
}
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
} finally {
//6.释放资源
if(rs!=null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(ps!=null){
try {
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn!=null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
return loginSuccess;
}
/**
* 初始化用户界面
* @return 用户输入的用户名和密码等登录信息
*/
private static Map<String,String> initUI() {
Scanner s = new Scanner (System.in);
System.out.println("请输入用户名");
String loginName = s.nextLine();
System.out.println("请输入密码");
String loginPwd = s.nextLine();
Map<String,String> userLoginInfo = new HashMap<>();
userLoginInfo.put("loginName",loginName);
userLoginInfo.put("loginPwd",loginPwd);
return userLoginInfo;
}
}
Statement对比Preparedstatement
·Statement存在sql注入问题,Preparedstatement解决了SQL注入问题 。
·Statement是编译一次执行一次。Preparedstatement是编译一次,可执行N次。Preparedstatement效率会高一些。
·Preparedstatement会在编译阶段做类型的安全检查。
Preparedstatement使用较多,当业务方面要求SQL注入、拼接时用Statement(升序、降序功能)