第一章JDBC
1.1jdbc概述
JDBC (Java DataBase Connectivity) java数据库连接,是一种用于执行sql语句的java api。JDBC是java访问数据库的标准规范,可以为不同的数据库提供统一访问,由java语言编写的接口和类组成。
JDBC需要连接驱动,驱动是两个设备要进行通信,必须满足的通信数据格式,数据格式由数据库厂商决定,厂商提供驱动软件,通过软件可以与该设备进行通信。
JDBC规范(必须掌握四个核心对象):
-
DriverManager:用于注册驱动。
-
Connection:表示与数据库创建的连接。
-
Statement:操作数据库sql语句的对象。
-
ResultSet:结果集
1.2jdbc原理
java提供访问数据库规范为JDBC ,生产厂商提供规范的实现类称之为驱动。
JDBC是接口,驱动是接口的实现,没有驱动就不能完成数据库连接,也就不能操作数据库! 每一个数据库厂商都需要提供自己的驱动,用来连接自己公司的数据库,也就是说驱动都由生产商提供。
1.3JDBC入门案例
准备数据(在test数据库下)
create table category(
cid int PRIMARY key AUTO_INCREMENT,
cname varchar(100)
);
insert into category (cname) values ('家电');
insert into category (cname) values ('服装');
insert into category (cname) values ('化妆品');
在jdbc模块下新建一个lib包并导入mysql连接的jar包,下图为变jar包为工具类步骤
开发步骤
1.注册驱动
2.获取连接
3.获得执行sql语句的对象
4.执行sql语句,并且返回结果
5.处理结果
6.释放资源
案例实现
import java.sql.*;
public class Demo01 {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
//1.注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2.获取连接
String url ="jdbc:mysql://localhost:3306/test1?useSSL=false";
/** jdbc:mysql: 协议 连接的是mysql数据库
* localhost 本机 3306 mysql端口号
* test 数据库名 ?之后 连接配置
*/ //sql连接地址 用户名 密码
Connection connection = DriverManager.getConnection(url,"root","root");
//3.获得执行sql语句的对象
Statement statement =connection.createStatement();
//4.执行sql语句,并且返回结果
String sql ="select * from category";
ResultSet resultSet= statement.executeQuery(sql);
//5.处理结果
while(resultSet.next()){
String cid= resultSet.getString("cid");
String cname= resultSet.getString("cname");
System.out.println(cid+" + "+cname);
}
//6.释放资源
resultSet.close(); statement.close(); connection.close();
}
}
1.4API详解
注册驱动
Class.forName("com.mysql.jdbc.Driver");
//与DriverManager类调用registerDriver方法意义相同
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
解决了强依赖数据库驱动
获得连接
static Connection getConnection(String url,String user, String password)
建立给定数据库url的连接
参数说明:
-
url 需要连接数据库的地址
-
user 用户名
-
password 密码
url: jdbc:mysql://localhost:3306/test jdbc规范下mysql驱动实现,在本机端口号为3306,数据库名test下进行连接
sun公司与数据库厂商的协议规范。
创建statment
接口的实现在数据库驱动中,所有与数据库交互都是基于连接对象的。
Statement createStatement()// 创建操作sql语句的对象
操作sql语句,并返回结果
Statement statement=connection.createStatement();
String sql ="select * from category";
ResultSet resultSet= statement.executeQuery(sql);//执行sql语句并将查询语句结果放到ResultSet结果集中
-
ResultSet executeQuery(String sql); 执行select语句的,返回查询结果到结果集中;
-
int exectueUpdate(String sql); 执行 insert update delete语句的,返回执行该语句影响的表数据条数;
-
boolean exectue(String sql); 只执行select并且有结果返回true 无结果返回false;
处理结果集
ResultSet实际上就是一个表格,我们可以用boolean next()方法指向某行记录,开始时光标指向第一行(列名行),调用next方法后光标向下移动一行,并且判断当前行是否有数据,返回值为布尔类型。可以用ResultSet提供的方法getInt(int col)方法 来获取指定列的数据(与之前学习的索引col不同,此处从1开始);也可以用getString(String str)方法判断str与那一列列名相同,来获取该列数据。
rs.next(); //指向数据第一行
rs.getInt(1);//从第一行中获取第一列数据
常用方法:
-
Object getObject(int index) /Object getObject (String name) 获取任意对象(这样就不用纠结我们要获取字段的数据类型了)
-
String getString(int index)/(String name) 获得字符串
-
int getInt(int index)/(String name)
-
double getDouble(int index)/(String name)
释放资源
与io流相同,使用的东西都需要关闭,关闭的顺序 先得到的后关,后得到的 先关。
1.5JDBC工具类
获取数据库连接操作,将在以后的增删改查中都能存在,可以封装为一个工具类JDBCUtils。提供获取连接对象的方法、关闭资源的方法、从而达到代码服用的功能。
package jdbc;
import java.sql.*;
public class JdbcUtils {
//这样是为了以后有修改只改这里就行
private static String dirver="com.mysql.jdbc.Driver";
private static String url="jdbc:mysql://localhost:3306/test?useSSL=false";
private static String user="root";
private static String password="root";
//静态代码块 一使用这个类就自动加载了代码块中的内容
static {//1.注册驱动
try {
Class.forName(dirver);
}catch (ClassNotFoundException e){
throw new RuntimeException(e);
}
}
/**
* 2.获取连接
*/
public static Connection getConnection() throws SQLException {
// Connection connection= DriverManager.getConnection(url,user,password);
// return connection;
return DriverManager.getConnection(url,user,password);
}
/**
* 3.释放资源
*/
public static void closeResourse(Connection con, Statement st, ResultSet rs){
if (rs!=null){
try {
rs.close();
} catch (SQLException e) {
//空着是为了不让关资源异常打断后续代码执行,且也不抛出让我们去解决,没必要,关资源出现问题不会影响整个程序运行
}
}
if (st!=null){
try {
st.close();
} catch (SQLException e) {
}
}
if (con!=null){
try {
con.close();
} catch (SQLException e) {
}
}
}
}
执行sql的对象和其返回结果及结果的出来要根据对sql语句具体操作行为来写出不同代码,所以不能放在这个工具类中。
1.6JDBC增删改查操作
新增、修改、删除、查询所有、根据id查询
CRUD : CREATE (新增) RETRIEVE(查询) UPDATE(修改) DELETE(删除) 中国话叫 增删改查
新增
修改
删除
查询所有
根据id查询
package jdbc;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class Demo02 {
public static void main(String[] args) {
Connection con=null;
Statement st=null;
ResultSet rs=null;
//1.注册驱动 一使用JdbcUtils类就会自动注册成功
try {
con = JdbcUtils.getConnection();//2.获取数据库连接
st = con.createStatement();//3.获取执行sql语句的对象
//4.执行sql语句,并且返回结果 目前只有查询有返回值,因为不返回结果rs资源无法关闭
//5种操作一个一个运行试验,不然结果不明显
// insert(st);//新增数据
// update(st);//修改数据
// delete(st);//删除数据
// rs=findAll(st);//查询所有 执行sql语句,并且返回结果 5.方法中包含了处理结果
rs=findById(st);//根据id查询
} catch (SQLException e) {
e.printStackTrace();
}finally {//6.关闭资源
JdbcUtils.closeResourse(con,st,rs);
}
}
//根据id查询 返回一个结果
private static ResultSet findById(Statement st) throws SQLException {
ResultSet resultSet= st.executeQuery("select * from category where cid=2");
if (resultSet.next()){
System.out.println(resultSet.getString("cid")
+" @ "+resultSet.getString("cname"));
}
return resultSet;
}
//查询所有
private static ResultSet findAll(Statement st) throws SQLException {
ResultSet resultSet= st.executeQuery("select * from category");
while (resultSet.next()){
System.out.println(resultSet.getString("cid")
+" @ "+resultSet.getString("cname"));
}
return resultSet;
}
//新增
private static void insert(Statement statement) throws SQLException {
int i = statement.executeUpdate("insert into category (cname) values ('测试')");
System.out.println("i = " + i);//影响了几条数据
}
//修改
private static void update(Statement statement) throws SQLException{
int i = statement.executeUpdate("update category set cname='测试3' where cid=4");
System.out.println("i = " + i);//影响了几条数据
}
//删除
private static void delete(Statement statement) throws SQLException{
int i = statement.executeUpdate("delete from category where cid=4");
System.out.println("i = " + i);//影响了几条数据
}
}
第二章 解决sql注入问题
2.1PreparedStatement
preparedStatement作用:
1. 预编译sql语句并执行
2. 预防SQL注入问题
SQL注入:SQL注入是通过操作输入来修改事先定义好的SQL语句,用以达到执行代码对服务器进行攻击。简而言之就是用户输入的内容作为了sql语句语法的一部分,改变了原来sql真正的用处。
案例
package preparedStatement;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Scanner;
public class Demo {
public static void main(String[] args) throws SQLException {
//模拟登录 用Scanneer对象 模拟登录框输入的用户名和密码
//category表模拟用户表 cid作为用户名 cname作为密码
Scanner sc=new Scanner(System.in);
//录入
System.out.println("请输入用户名");
String username = sc.nextLine();//sc.next()会把空格当回车
System.out.println("请输入密码");
String pwd = sc.nextLine();
//获取连接
Connection connection = JdbcUtils2.getConnection();
//创建执行sql语句对象
Statement statement = connection.createStatement();
//编写sql
String sql = "select * from category where cid='"+username+"' and cname='"+pwd+"'";
//执行sql
ResultSet resultSet = statement.executeQuery(sql);
//处理结果集
if (resultSet.next()){//该结果集有数据就代表登录成功
System.out.println("登录成功");
}else{
System.out.println("登录失败");
}
//5.关闭资源
JdbcUtils2.closeResourse(connection,statement,resultSet);
}
}
此处需得正确输入才可正确
而此处我用户名随意乱输,密码输入 ' or 'x' = 'x 此等格式就会登录成功,这便是SQL注入问题
2.2SQL注入原因
原因如下:
String sql = "select * from category where cid='"+username+"' and cname='"+pwd+"'";f发现,我们将username用 ABCD 代替,pwd用 ' or 'a' = 'a 代替,得到sql语句为:String sql = "select * from category where cid='ABCD' and cname=' ' or 'a' = 'a' ";
此处(cid='ABCD' and cname=' ' )的结果为false,但是用 or 连接( 'a' = 'a' ),那这将是永真式,结果为true。
为了解决这一问题,我们选择用 ?占位符去替代sql语句中的条件字段,这样就不会有机会从单引号拼接上投机取巧,这就用到了preparedStatement类 来帮助我们。
2.3 API详解: 预处理对象
preparedStatement: 是预编译对象,是Statement对象的子类。
特点:
-
性能高
-
会把sql语句提前编译
-
能过滤掉用户输入的关键字
PreparedStatement预处理对象,处理每个sql语句中所有的实际参数,都必须用 ?占位符替换。
preparedStatement类使用步骤:
我们可以看到preparedStatement类和Statement的区别是:
preparedStatement类在获取对象的同时就将sql语句传入,起到一个预编译的作用,所以后续执行SQL的时候就不需要再传SQL语句。
现在用preparedStatement类来替换重写上述案例:
package preparedStatement;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Scanner;
public class Demo01 {
public static void main(String[] args) throws SQLException {
//模拟登录 用Scanneer对象 模拟登录框输入的用户名和密码
//category表模拟用户表 cid作为用户名 cname作为密码
Scanner sc=new Scanner(System.in);
//录入
System.out.println("请输入用户名");
String username = sc.nextLine();//sc.next()会把空格当回车
System.out.println("请输入密码");
String pwd = sc.nextLine();
//0.获取连接
Connection connection = JdbcUtils2.getConnection();
//1.编写sql
String sql = "select * from category where cid=? and cname=?";
//2.创建预处理对象
PreparedStatement preparedStatement = connection.prepareStatement(sql);
//3.设置参数 setObject可以设任何数据类型 当不清楚类型时用
preparedStatement.setObject(1,username);
preparedStatement.setObject(2,pwd);
//4.执行sql
ResultSet resultSet = preparedStatement.executeQuery();
//处理结果集
if (resultSet.next()){
System.out.println("登录成功");
}else{
System.out.println("登录失败");
}
//5.关闭资源
JdbcUtils2.closeResourse(connection,preparedStatement,resultSet);
}
}
此时再就不会有注入问题了
其实它能解决这个问题的原因是它会扫描?占位符传入的参数,扫描到 ' 就会将在其前加 \ 进行转义,如之前的例子: ' or 'a' = 'a 转义后为 \' or \' a \' = \'a 则传进原式子单引号中就是 '\' or \' a \' = \'a ' ,自然就不会有问题了。
2.4preparedStatement的使用
用preparedStatement类又进行新增、删除、更改例子:
package preparedStatement;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class Demo02 {
public static void main(String[] args) throws SQLException {
/**
* 新增数据
*/
//1.获取连接
Connection connection = JdbcUtils.getConnection();
//2.编写sql语句
String sql = "insert into category (cid,cname) values (?,?)";
//3.获取预处理对象
PreparedStatement preparedStatement = connection.prepareStatement(sql);
//4.设置参数
preparedStatement.setObject(1,"007");
preparedStatement.setObject(2,"ppp");
//5.执行sql
int i = preparedStatement.executeUpdate();
System.out.println("影响了" + i+"条语句");
//6.关闭资源
JdbcUtils.closeResourse(connection,preparedStatement,null);
}
}
package preparedStatement;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class Demo03 {
public static void main(String[] args) throws SQLException {
/**
* 删除数据
*/
//1.获取连接
Connection connection = JdbcUtils.getConnection();
//2.编辑sql
String sql = "delete from category where cid=?";
//3.创建预处理对象
PreparedStatement preparedStatement = connection.prepareStatement(sql);
//4.设置参数
preparedStatement.setObject(1,"3");
//5.执行sql
int i = preparedStatement.executeUpdate();
System.out.println("影响了" + i+"条语句");
//6.关闭资源
JdbcUtils.closeResourse(connection,preparedStatement,null);
}
}
package preparedStatement;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class Demo04 {
public static void main(String[] args) throws SQLException {
/**
* 更新数据
*/
//1.获取连接
Connection connection = JdbcUtils.getConnection();
//2.编写sql
String sql = "update category set cname=? where cid=?";
//3.创建预编译对象
PreparedStatement preparedStatement = connection.prepareStatement(sql);
//4.设置参数
preparedStatement.setObject(1,"食物");
preparedStatement.setObject(2,"007");
//5.执行sql语句
int i = preparedStatement.executeUpdate();
System.out.println("影响了" + i+"条语句");
//6.关闭资源
JdbcUtils.closeResourse(connection,preparedStatement,null);
}
}
第三章 dbutils
如果使用JDBC进行开发,我们会发现代码过多,为了简化 我们使用一个工具类 DBUtils DBUtils就是为了简化jdbc开发的工具包 ,需要导入commons-dbutils-1.6.jar才能那个使用。
3.1概述
DBUtils是java中数据库操作的工具,对jdbc进行了封装,简化了操作。
Dbutils三个核心功能介绍
-
QueryRunner :提供对sql语句操作的api
-
ResultSetHandler接口:用于定义select操作后,怎么封装结果集
-
DbUtils类,工具类,定义了关闭资源与事务处理的相关方法。
3.2QueryRunner核心类介绍
构造方法
-
QueryRunner() 创建核心类,需要手动提供Connection
普通方法
-
update(Connection conn,String sql , Object...params)
使用提供的连接,完成DML语句 也就是增删改。
-
query(Connection conn,String sql,ResultSetHandler rs,Object...params)
使用提供的连接,执行DQL语句,也就是查询,然后将结果封装到对象中。
3.3使用QueryRunner实现新增、修改、删除
-
update(Connection conn,String sql , Object...params)
使用提供的连接,完成DML语句 也就是增删改。
新增
package dbutils;
import org.apache.commons.dbutils.QueryRunner;
import preparedStatement.JdbcUtils;
import java.sql.Connection;
import java.sql.SQLException;
public class Demo01 {
public static void main(String[] args) throws SQLException {
//创建QueryRunner对象
QueryRunner qr = new QueryRunner();
//获取连接
Connection connection = JdbcUtils.getConnection();
//执行sql
qr.update(connection,
"insert into category (cid,cname) values (?,?)", "777", "下午测试");
//关闭资源
JdbcUtils.closeResource(connection, null, null);
}
}
可以看出其实就是把对象类换成QueryRunner,把设置参数和执行SQL语句操作一体化,就不用没一个?就要写一条设置参数语句,确实简化了代码。
修改
package dbutils;
import org.apache.commons.dbutils.QueryRunner;
import preparedStatement.JdbcUtils;
import java.sql.Connection;
import java.sql.SQLException;
public class Demo02 {
public static void main(String[] args) throws SQLException {
//创建qr
QueryRunner queryRunner = new QueryRunner();
Connection connection = JdbcUtils.getConnection();
queryRunner.update(connection,
"update category set cid =? ,cname =? where cid=?",999,"999",777);
JdbcUtils.closeResource(connection,null,null);
}
}
删除
package dbutils;
import org.apache.commons.dbutils.QueryRunner;
import preparedStatement.JdbcUtils;
import java.sql.Connection;
import java.sql.SQLException;
public class Demo03 {
public static void main(String[] args) throws SQLException {
//创建qr对象
QueryRunner queryRunner = new QueryRunner();
Connection connection = JdbcUtils.getConnection();
queryRunner.update(connection,"delete from category where cid =?",111);
JdbcUtils.closeResource(connection,null,null);
}
}
3.4QueryRunner实现查询操作
-
query(Connection conn,String sql,ResultSetHandler rs,Object...params)
使用提供的连接,执行DQL语句,也就是查询,然后将结果封装到对象中。
ResultSetHandler结果集
-
BeanHandler: 将结果集中第一条记录封装到一个指定的javaBean中。
-
BeanListHandler:将结果集中每一条记录封装到指定的javaBean中,将这些javaBean封装在List集合中。
-
ScalarHandler: 用于单数据的。例如:select count(*) from 表操作
-
ColumnListHandler:将结果集中指定的列的字段值,封装到一个List集合中。
注意: javabean中属性名必须和数据库中字段名 类型 名称完全一致。
JavaBean
JavaBean就是一个类,在开发中常用封装数据。具有以下特性。
1.提供私有字段 private 类型字段名;
2.提供get/set方法
3.提供无参构造
package javaBean;
public class Category {
private String cid;
private String cname;
@Override
public String toString() {
return "Category{" +
"cid='" + cid + '\'' +
", cname='" + cname + '\'' +
'}';
}
public Category(String cid, String cname) {
this.cid = cid;
this.cname = cname;
}
public Category() {
}
public String getCid() {
return cid;
}
public void setCid(String cid) {
this.cid = cid;
}
public String getCname() {
return cname;
}
public void setCname(String cname) {
this.cname = cname;
}
}
BeanHandler(获取第一条记录)
package dbutils;
import javaBean.Category;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import preparedStatement.JdbcUtils;
import java.sql.Connection;
import java.sql.SQLException;
public class Demo04_BeanHandler {
public static void main(String[] args) throws SQLException {
QueryRunner qr = new QueryRunner();
String sql = "select * from category";
Connection connection = JdbcUtils.getConnection();
Category category =
qr.query(connection, sql, new BeanHandler<Category>(Category.class));
System.out.println("query = " + query);//第一条数据记录
JdbcUtils.closeResourse(connection,null,null);
}
}
BeanListHandle(获取所以记录)
package dbutils;
import javaBean.Category;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import preparedStatement.JdbcUtils;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
public class Demo05_BeanListHandler {
public static void main(String[] args) throws SQLException {
QueryRunner qr=new QueryRunner();
String sql = "select * from category";
Connection connection = JdbcUtils.getConnection();
List<Category> query1 =
qr.query(connection, sql, new BeanListHandler<Category>(Category.class));
System.out.println("query1 = " + query1);//用[]将所以记录包裹了
for (Category category : query1) {//增强for循环,一条一条打印
System.out.println("category = " + category);
}
JdbcUtils.closeResourse(connection,null,null);
}
}
ScalarHandler(聚合查询)
package dbutils;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.ScalarHandler;
import preparedStatement.JdbcUtils;
import java.sql.Connection;
import java.sql.SQLException;
public class Demo06_ScalarHandler {
public static void main(String[] args) throws SQLException {
QueryRunner qr=new QueryRunner();
Connection connection = JdbcUtils.getConnection();
Object obj = qr.query(connection, "select count(*) from category",
new ScalarHandler());
System.out.println("obj = " + obj);//count(*)结果
JdbcUtils.closeResourse(connection,null,null);
}
}
ColumnListHandler(某列结果集查询)
package dbutils;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.ColumnListHandler;
import preparedStatement.JdbcUtils2;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
public class Demo07_ColumnListHandler {
public static void main(String[] args) throws SQLException {
QueryRunner qr=new QueryRunner();
Connection connection = JdbcUtils2.getConnection();
List<Object> list =
qr.query(connection, "select * from category", new ColumnListHandler("cname"));
//获取名为cname列的结果集 (想要那一列就在括号中写)也可在其中写列数,表示获取第几列
for (Object category : list) {
System.out.println("category = " + category);
}
JdbcUtils2.closeResourse(connection,null,null);
}
}
3.5Properties
概述
用于管理配置信息的类(JdbcUtils 工具类的又一种写法形式)
Properties 用于管理配置信息的类,存储在一个文本文件中,并在程序中读取这些信息。
package jdbc;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.*;
import java.util.Properties;
public class JdbcUtils1 {
//下列成员变量存在管理配置信息的类jdbc的文本文件properties中
private static String driver;
private static String url;
private static String user;
private static String password;
//静态代码块 一使用这个类就自动加载了代码块中的内容
static {//注册驱动
try {
//创建Propperties集合 以获取其中各变量值
Properties properties=new Properties();
properties.load(new FileInputStream("src\\jdbc.properties"));
url=properties.getProperty("url");
user=properties.getProperty("user");
password=properties.getProperty("password");
driver=properties.getProperty("driver");
Class.forName(driver);
}catch (Exception e){
throw new RuntimeException(e);
}
}
/**
* 获取连接
*/
public static Connection getConnection() throws SQLException {
// Connection connection= DriverManager.getConnection(url,user,password);
// return connection;
return DriverManager.getConnection(url,user,password);
}
/**
* 释放资源
*/
public static void closeResourse(Connection con, Statement st, ResultSet rs){
if (rs!=null){
try {
rs.close();
} catch (SQLException e) {
//空着是为了不让关资源异常打断后续代码执行,且也不抛出让我们去解决,没必要,关资源出现问题不会影响整个程序运行
}
}
if (st!=null){
try {
st.close();
} catch (SQLException e) {
}
}
if (con!=null){
try {
con.close();
} catch (SQLException e) {
}
}
}
}