01.反馈-复习-学习目标
反馈
内容 |
---|
今天的内容还挺好理解的,自己多练练就行了 |
idea里面写sql语句不太习惯 |
要是面试宝典背熟了,实际代码瞧不出来。 |
天天新知识,经常不用的知识已经有点忘记了,有时候碰到了不会的还得去之前的笔记里翻翻,没有题目测试我也不知道自己还能记得多少,建议老师隔断时间就出些综合案列给我们自己放假休息的时候测试测试;加深复习思路 |
宇哥可以讲讲这个嘛有点不懂 InputStream inputStream = JdbcUtils.class.getResourceAsStream("/jdbc.properties"); 获取类路径下jdbc.properties文件的输入流 |
javase项目:运行的代码是在输出路径的类路径下面
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KexhT9Aw-1606228060175)(assets/image-20200520090605867.png)]
javaweb项目
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1B6a0HZq-1606228060178)(assets/image-20200520090712997.png)]
复习
-
能够理解JDBC的概念
Java Data Base Connectivity java数据库连接技术,是一套操作不同数据库的接口,接口由不同的数据库厂商去实现,所以以后只需要导入不同厂商的数据库驱动包,操作JDBC接口即可;
-
能够使用Connection接口
数据库连接对象,通过注册驱动后获取
DriverManager.getConnection(url,user,password);
-
能够使用Statement接口
通过连接创建运输器(小货车),将sql语句命令发送给数据库去执行,并装载数据返回给java的ResultSet
-
能够使用ResultSet接口
运输器将数据库返回的数据给到ResultSet封装查询
-
能够使用JDBC实现对单表数据增、删、改、查
增删改使用statement.executeUpdate(sql语句) 返回影响的行数
查询使用statement.executeQuery(sql语句) 返回的是ResultSet结果集
-
能够使用JDBC操作事务
try{ connection.setAutoCommit(false); //开启手动事务 //执行业务多条sql语句 //没有发生异常,提交事务 connection.commit(); //提交事务 }catch{ //发生异常,回滚事务 connection.rollback(); }finally{ //释放资源 }
注意:一个事务必须在一个connection内操作,2个connection就是2个事务
-
能够编写JDBC工具类
提供2个方法
获取连接的方法
释放资源的方法
注意:配置参数使用配置文件
-
能够完成JDBC实现登录案例
9.面试题:请说出JDBC操作的步骤
1、导入数据库驱动包
2、注册驱动DriverManager
3、使用驱动获取数据库连接Connection
4、根据连接创建运输器Statement
5、执行增删改查名命令,如果查询获取结果集ResultSet
6、结果集通过游标指针读取数据
7、释放资源
考试范围
前端、web后端(servlet,request,response,session)、数据库(DQL)、JDBC
学习目标
- 能够通过PreparedStatement完成增、删、改、查
- 能够完成PreparedStatement改造登录案例
- 会使用连接池
02.JDBC:SQL注入攻击【理解】
目标
理解什么是SQL注入攻击
为什么讲解SQL注入攻击
证明Statement执行sql命令时不安全,会导致被SQL注入攻击,执行命令有可能返回不是我们想要的结果
SQL注入攻击介绍
statement执行的sql语句是通过拼接字符串的形式,最后将拼接好的字符串发送给数据库执行;
拼接字符串由很大的问题: 可以拼接sql恶意命令进来,导致最后sql执行的结果是错误的
例子:
String sql = "select * from user where name='"+name+"' and password='"+password+"'";
// 这里的name与password是用户输入的数据
// 假设 password的数据为:' or 1=1 -- 就可以绕过用户名与密码校验的过程
// 最后拼接的sql语句结果:
sql = "select * from user where name='"+name+"' and password='' or 1=1 -- '";
// 结论:执行上面的sql,由于1=1是恒等式,所以前面的用户名与密码错误了,也可以查到数据
模拟SQL注入攻击案例-登录
效果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1QNhkIfl-1606228060180)(assets/image-20200520093512155.png)]
环境搭建
创建数据库
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DrH0CJBr-1606228060183)(assets/image-20200520092525983.png)]
数据准备
CREATE TABLE USER (
id INT AUTO_INCREMENT PRIMARY KEY,
NAME VARCHAR(50),
PASSWORD VARCHAR(50)
);
INSERT INTO USER (NAME, PASSWORD) VALUES('admin', '123'), ('test', '123'), ('gm', '123');
导入mysql驱动包与jdbc.properties配置文件
修改jdbc.properties连接的数据库
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NME7ra8R-1606228060185)(assets/image-20200520092722286.png)]
导入JdbcUtils.java
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qMnncvCR-1606228060188)(assets/image-20200520092744980.png)]
登录操作代码
package _08_登录案例;
import _06_编写JDBC工具类.JdbcUtils;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Scanner;
/**
* @author 黑马程序员
*/
public class LoginTest {
public static void main(String[] args) throws Exception {
/*
* 目标:模拟用户登录案例
* */
//1、创建控制台输入流
Scanner sc = new Scanner(System.in);
//2、进行输出与获取用户名与密码
System.out.println("请输入账号:");
String name = sc.nextLine();
System.out.println("请输入密码:");
String password = sc.nextLine();
//3、执行数据库操作查询数据
String sql = "select * from user where name='"+name+"' and password='"+password+"'";
Connection conn = JdbcUtils.getConnection();
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql);
//4、校验登录结果
//分析:rs移动一次游标指针如果有数据就代表用户名与密码正确
if(rs.next()){
//登录成功
System.out.println("欢迎您,"+name);
}else{
//登录失败
System.out.println("账号或密码错误...");
}
//5、释放资源
JdbcUtils.close(conn,stmt,rs);
}
}
分析SQL注入的原理
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hzjxN0fp-1606228060189)(assets/image-20200520093745019.png)]
小结
理解什么是SQL注入攻击
执行sql的命令运行通过拼接字符串拼接了恶意的sql命令到数据库去执行; 这个问题是由于Statment导致的;
03.JDBC:预编译运输器介绍【理解】
目标
- 为什么要使用PreparedStatement
- PreparedStatement的原理
为什么要学习预编译运输器PreparedStatement
1、防止SQL注入攻击
2、提高执行sql的效率
PreparedStatement类结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-A9qYYUia-1606228060190)(assets/image-20200520094358151.png)]
PreparedStatement接口继承了Statement, 扩展功能更加强大
PreparedSatement的执行原理
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZrWVG60B-1606228060191)(assets/image-20200520095743388.png)]
结论: 1、preparedStatement执行效率高于Statement
2、采用占位符设置参数可以防止sql注入攻击(将拼接的恶意sql命令进行转义后,只是普通字符串)
Connection创建PreparedStatement对象
Connection接口中的方法 | 描述 |
---|---|
PreparedStatement prepareStatement(String sql) | 传入sql语句模板,创建PreparedStatement预编译对象 |
接口中的方法
PreparedStatement接口中的方法 | 描述 |
---|---|
int executeUpdate() | 执行增、删、改的方法,返回影响行数 |
ResultSet executeQuery() | 执行查询的方法,返回ResultSet |
设置占位符的方法
PreparedStatement的方法 | 说明 |
---|---|
void set数据类型(int 参数1,参数2) | 给sql语句模板中指定位置占位符“?”设置参数值,位置从1开始 |
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BPKfaOQF-1606228060192)(assets/1562316815441.png)]
PreparedSatement的小结
-
为什么要使用PreparedStatement
1、防止sql注入攻击
2、执行效率高
-
PreparedStatement的原理、
将sql语句模板给到数据库去编译,以后执行每条sql语句只要设置模板中的参数值即可,以后每次执行sql语句就不需要编译了
04.JDBC:预编译执行DML操作【应用】
目标
使用PreparedStatement
效果
插入效果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UG1d1HpA-1606228060193)(assets/image-20200520102503445.png)]
更新效果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LGIfhiST-1606228060194)(assets/image-20200520102848199.png)]
删除效果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Hh9nvuAx-1606228060196)(assets/image-20200520103643444.png)]
准备数据
create table student(
id int primary key auto_increment,
name varchar(200),
gender CHAR(1),
birthday date
);
-- 写插入4条sql语句
insert into student
values(null,'孙悟空','1','1992/7/17'),
(null,'沙僧','0','1993/10/13'),
(null,'猪八戒','1','1994/1/23')
增删改代码
package _02_使用预编译PreparedStatement执行DML操作;
import com.itheima.util.JdbcUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
/**
* @author 黑马程序员
*/
public class Demo1 {
public static void main(String[] args) throws Exception {
//insert();
//update();
delete();
}
//插入数据的方法
private static void insert() throws Exception {
//1、定义sql语句模板
String sql = "insert into student values(null,?,?,?)";
//2、获取连接
Connection conn = JdbcUtils.getConnection();
//3、获取预编译对象PreparedStatement
PreparedStatement pstmt = conn.prepareStatement(sql);
//4、设置sql语句模板中占位符的值
pstmt.setString(1,"播仔"); //给name赋值
pstmt.setString(2,"1"); //给gender赋值
//第二种方法给char(1)赋值: pstmt.setBoolean(2,true); true代表1,false代表0
pstmt.setString(3,"2006-01-01"); //birthday赋值
//5、执行命令
int count = pstmt.executeUpdate();
//6、释放资源
JdbcUtils.close(conn,pstmt);
System.out.println("插入成功");
}
//修改数据的方法: 将id=4的name修改为“播妞”
private static void update() throws Exception {
//1、定义sql语句模板
String sql = "update student set name=? where id=?";
//2、获取连接
Connection conn = JdbcUtils.getConnection();
//3、获取预编译对象PreparedStatement
PreparedStatement pstmt = conn.prepareStatement(sql);
//4、设置sql语句模板中占位符的值
pstmt.setString(1,"播妞"); //name赋值
pstmt.setInt(2,4); //id赋值
//5、执行命令
int count = pstmt.executeUpdate();
//6、释放资源
JdbcUtils.close(conn,pstmt);
System.out.println("更新成功");
}
//删除数据的方法: 将id=1的数据删除
private static void delete() throws Exception {
//1、定义sql语句模板
String sql = "delete from student where id=?";
//2、获取连接
Connection conn = JdbcUtils.getConnection();
//3、获取预编译对象PreparedStatement
PreparedStatement pstmt = conn.prepareStatement(sql);
//4、设置sql语句模板中占位符的值
pstmt.setInt(1,1);
//5、执行命令
int count = pstmt.executeUpdate();
//6、释放资源
JdbcUtils.close(conn,pstmt);
System.out.println("删除成功");
}
}
小结
PreparedStatement操作sql语句相关方法
创建预编译对象:connection.prepareStatement(sql); 注意:这里传入sql语句
设置占位符参数:preparedStatement.set数据类型(占位符位置,参数值); 位置从1开始
执行DML命令:preparedStatement.executeUpdate(); 注意:这里不传sql语句
05.JDBC:预编译执行DQL【应用】
目标
- 数据库表与java对象之间的封装关系
- 能够将多条记录封装成java对象List<T>集合
如何在java中方便的操作从数据库查询到的数据?
java操作的数据都在内存对象中,所以我们要将数据库查询到的数据封装到内存的java对象中
java对象:具有面向对象,可以通过对象的setxxx()/getXXXX()进行设置与获取数据
数据库表与java对象之间的关系
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CxAb7cC3-1606228060197)(assets/1562317396108.png)]
java对象与表之间对应关系:
1、java的类与数据库表是一一对应的
java的类是抽象的概念,Student类,代表学生类,不代表具体哪一个学生
数据库表Student表,代表学生表,不代表具体哪一个学生
2、 类实例一个java对象与数据库表中一条记录一一对应
java对象:Student zhangsanStu = new Student() , 代表具体的一个学生:张三同学
数据库表中的一条记录:代表具体一个学生
3、java对象的属性与表中字段一一对应
student对象(张三同学)里面的id属性与数据库表中(张三同学的一条记录)中id字段一一对应
案例:查询多条记录使用java对象封装
需求:
查询所有的学生类,封装成List<Student>返回
分析
一条记录对应一个Student对象,多条记录就代表多个Student对象,所有这里使用List进行封装
执行效果
控制台打印List里面的数据
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kfD5QUHQ-1606228060198)(assets/image-20200520110318795.png)]
开发步骤:
1、定义Student类来封装student表数据
2、使用预编译对象查询学生表所有数据封装到List集合中
实现代码
定义Student类
package com.itheima.entity;
import java.util.Date;
/**
* @author 黑马程序员
*/
public class Student {
private int id;
private String name;
private boolean gender; //与数据库中存储的值对应关系:true代表1,false代表0
private Date birthday;
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", gender=" + gender +
", birthday=" + birthday +
'}';
}
public Student() {
}
public Student(int id, String name, boolean gender, Date birthday) {
this.id = id;
this.name = name;
this.gender = gender;
this.birthday = birthday;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean isGender() {
return gender;
}
public void setGender(boolean gender) {
this.gender = gender;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
}
查询封装代码
package _03_使用预编译PreparedStatementh执行DQL操作;
import com.itheima.entity.Student;
import com.itheima.util.JdbcUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;
/**
* @author 黑马程序员
*/
public class Demo2 {
public static void main(String[] args) throws Exception {
/*
* 目标:从数据库查询多条记录的数据封装到List<Student>集合中
* */
//1、定义存储数据的集合
List<Student> studentList = new ArrayList<>();
//2、定义sql语句模板
String sql="select * from student";
//3、获取连接
Connection conn = JdbcUtils.getConnection();
//4、创建预编译对象
PreparedStatement pstmt = conn.prepareStatement(sql);
//5、执行命令获取结果集
ResultSet rs = pstmt.executeQuery();
//6、遍历结果集游标循环读取数据并封装到集合中
while(rs.next()){
//读取每一条数据封装到Student对象中
Student student = new Student();
student.setId(rs.getInt("id"));
student.setName(rs.getString("name"));
student.setGender(rs.getBoolean("gender"));
student.setBirthday(rs.getDate("birthday"));
//将student对象追加到集合中
studentList.add(student);
}
//7、打印集合数据
studentList.forEach(System.out::println);
//8、释放资源
JdbcUtils.close(conn,pstmt,rs);
}
}
小结
数据表与java对象之间的对应关系是怎样的?
java的类与数据库表一一对应
java类的一个对象与表中一条记录一一对应
对象中的属性成员 与 每条记录中每个字段一一对应
05.案例:预编译优化登录【应用】
目标
改写前面的登录案例,解决SQL注入攻击的问题
执行效果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QdH1GMRy-1606228060199)(assets/1562317207959.png)]
sql注入攻击无效
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pwxI8HJR-1606228060200)(assets/image-20200520112350468.png)]
代码
package _04_使用预编译优化登录案例防止SQL注入攻击;
import com.itheima.util.JdbcUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Scanner;
/**
* @author 黑马程序员
*/
public class LoginTest2 {
public static void main(String[] args) throws Exception {
/*
* 目标:模拟用户登录案例
* */
//1、创建控制台输入流
Scanner sc = new Scanner(System.in);
//2、进行输出与获取用户名与密码
System.out.println("请输入账号:");
String name = sc.nextLine();
System.out.println("请输入密码:");
String password = sc.nextLine();
//3、执行数据库操作查询数据
String sql = "select * from user where name=? and password=?";
Connection conn = JdbcUtils.getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1,name);
pstmt.setString(2,password);
ResultSet rs = pstmt.executeQuery();
//4、校验登录结果
//分析:rs移动一次游标指针如果有数据就代表用户名与密码正确
if(rs.next()){
//登录成功
System.out.println("欢迎您,"+name);
}else{
//登录失败
System.out.println("账号或密码错误...");
}
//5、释放资源
JdbcUtils.close(conn,pstmt,rs);
}
}
小结
预编译语句可以有效的避免SQL注入的问题
07.连接池:介绍【理解】
目标
理解数据库连接池的好处
用户直接创建Connection对象的问题
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Snu49dee-1606228060201)(assets/1562377031462.png)]
1、每个用户每一次执行sql语句都会去创建新的连接
如果有1亿个用户,那么就会创建1亿个连接,导致服务器资源内存溢出,无法创建
2、每个连接使用就会释放,导致连接无法重用
连接池的介绍
系统创建连接池的时候会默认初始化一定数量的连接对象,当用户需要连接池的时候,就从连接池中获取使用不需要创建,当用户量大的时候,连接池里面的连接全部都被使用了,连接池会继续创建连接对象直到达到最大连接数为止;当前用户使用连接完成后会将连接放回到连接池中,供其他用户再次使用;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-I6M7g0fq-1606228060203)(assets/1562377203244.png)]
小结:连接池的好处
1、创建连接池的时候默认就初始化了一些连接,用户不需要创建
2、连接池有最大连接数限制,所以会控制资源的使用量,不会导致服务器崩溃
3、用户使用后的连接还可以放回连接池,连接对象可以重用
08.连接池:JDBC连接池的API【理解】
目标
- 理解连接池的接口名
- 能够从连接池中得到连接的方法
数据源接口
接口名:javax.sql.DataSource
叫连接池接口, 也叫数据源接口
数据源接口中的方法
DataSource接口中的方法 | 描述 |
---|---|
Connection getConnection() | 从连接池中得到一个连接对象 |
常用连接池参数
常用参数 | 描述 |
---|---|
初始连接数 | 连接池创建的时候会默认创建一些连接对象,连接的数量就是初始连接数 |
最大连接数 | 并发请求的时候,连接池中最多连接数量,连接数量的上限 |
最长等待时间 | 连接池里面的连接全部都被使用了,新的用户请求连接对象就需要等待,这里设置的就是最长等待时间,超过这个时间系统就会抛出异常 |
最长空闲回收时间 | 这个参数不是所有连接池支持的,一个连接处于长时间处于空闲状态(没有被使用),那么这里可以设置空闲回收时间; |
小结
-
连接池的接口名是什么?
javax.sql.DataSource
也叫数据源
-
从连接池中得到连接的方法是什么?
getConnection();
10.连接池:C3P0连接池的使用【应用】
目标
掌握C3P0连接池获取连接的使用步骤
常用第三方连接池组件的介绍
- 阿里巴巴-德鲁伊druid连接池:Druid是阿里巴巴开源平台上的一个项目
- DBCP(DataBase Connection Pool)数据库连接池,是Apache上的一个Java连接池项目,也是Tomcat使用的连接池组件。
- C3P0是一个开源的JDBC连接池,目前使用它的开源项目有Hibernate,Spring等。C3P0有自动回收空闲连接功能。
C3P0的jar包资源介绍
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UPHTIaf7-1606228060204)(assets/image-20200520114952143.png)]
C3P0的API的介绍
构造方法 | 描述 |
---|---|
ComboPooledDataSource() | 创建c3p0连接池对象,这个类是javax.sql.DataSource的实现类 |
C3P0的配置文件使用要求
文件名:c3p0-config.xml
配置文件的名字不可以改,必须叫这个名字,因为内部默认自动解析这个文件
存放位置:必须src根目录下
实现步骤
1、创建项目
2、导入c3p0的jar包
3、导入c3p0的配置文件
4、使用c3p0连接池对象获取连接测试
实现代码
导入资源的结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jWWZxbdA-1606228060205)(assets/image-20200520140804820.png)]
注意:c3p0的jar包+数据库驱动包
配置文件(修改相关信息)
<c3p0-config>
<!-- 默认方案:使用默认的配置读取连接池对象 -->
<default-config>
<!-- 数据库连接参数 -->
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/day24?characterEncoding=utf8</property>
<property name="user">root</property>
<property name="password">root</property>
<!-- 连接池参数 -->
<!--初始化连接数-->
<property name="initialPoolSize">5</property>
<!--最大连接数-->
<property name="maxPoolSize">10</property>
<!--用户等待最长时间:毫秒-->
<property name="checkoutTimeout">3000</property>
</default-config>
<!--命名配置的方案:可以配置连接池参数的另一个方案-->
<named-config name="otherc3p0">
<!-- 连接参数 -->
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/day24?characterEncoding=utf8</property>
<property name="user">root</property>
<property name="password">root</property>
<!-- 连接池参数 -->
<property name="initialPoolSize">6</property>
<property name="maxPoolSize">10</property>
<property name="checkoutTimeout">5000</property>
</named-config>
</c3p0-config>
使用默认配置方案的Java代码
package _使用c3p0连接池测试;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import java.sql.Connection;
import java.sql.SQLException;
/**
* @author 黑马程序员
*/
public class DemoTest {
public static void main(String[] args) throws SQLException {
/*
* 目标:使用c3p0连接池获取连接
* */
//1.根据配置文件默认方案创建c3p0连接池对象
ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
//分析:会自动解析c3p0-config.xml文件来创建连接池对象
//2.获取连接12次,打印输出每一个连接
for (int i = 0; i < 12; i++) {
Connection conn = comboPooledDataSource.getConnection();
System.out.println(conn); //打印
//当i==4的时候,关闭当前连接放回到连接池
if(i==4){
conn.close();
}
}
}
}
使用命名配置方案代码
package _使用c3p0连接池测试;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import java.sql.Connection;
import java.sql.SQLException;
/**
* @author 黑马程序员
*/
public class DemoTest2 {
public static void main(String[] args) throws SQLException {
/*
* 目标:使用c3p0连接池获取连接
* */
//1.根据配置文件命名配置方案创建c3p0连接池对象
ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource("otherc3p0");
//分析:会自动解析c3p0-config.xml文件来创建连接池对象
// otherc3p0是配置文件中 <named-config name="otherc3p0"> 配置的方案
//2.获取连接12次,打印输出每一个连接
for (int i = 0; i < 12; i++) {
Connection conn = comboPooledDataSource.getConnection();
System.out.println(conn); //打印
//当i==4的时候,关闭当前连接放回到连接池
if(i==4){
conn.close();
}
}
}
}
运行效果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2DKMqFPA-1606228060206)(assets/image-20200520141745325.png)]
小结
c3p0使用步骤:
1、导入c3p0的2个jar包和数据库驱动包
2、导入c3p0的配置文件,文件名c3p0-config.xml ,位置src根目录下
3、根据自己环境配置c3p0-config.xml里面数据库连接信息、最大连接数、初始连接数、用户等待超时时间
4、实例ComboPooledDataSource连接池对象,通过getConnection()获取连接
11.连接池:使用Druid的连接池【应用】
目标
能够使用druid连接池
DRUID简介
Druid是阿里巴巴开发的号称为监控而生的数据库连接池,在功能、性能、扩展性方面,都超过其他数据库连接池。Druid已经在阿里巴巴部署了超过600个应用,经过一年多生产环境大规模部署的严苛考验。如:一年一度的双十一活动,每年春运的抢火车票。都能扛得住。
Druid的jar包资源介绍
Druid的下载地址:https://github.com/alibaba/druid
DRUID连接池使用的jar包:druid-1.0.9.jar
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ILtS1GIy-1606228060207)(assets/image-20200520142835159.png)]
配置文件常用的配置参数
参数 | 说明 |
---|---|
url | 数据库连接字符串:jdbc:mysql:///数据库名?参数1=参数值 |
username | 数据库的用户名 |
password | 数据库密码 |
driverClassName | 数据库驱动类:com.mysql.jdbc.Driver |
initialSize | 初始化连接数 |
maxActive | 最大连接数 |
maxWait | 用户等待连接最大超时时间 |
DRUID连接池API介绍
读取配置文件
Class类中的方法 | 说明 |
---|---|
InputStream getResourceAsStream(String path) | druid组件默认没有解析配置文件,需要自己读取配置文件的输入流 |
创建一个Properties对象,读取上面的输入流:load(输入流)
DruidDataSourceFactory的方法 | 方法 |
---|---|
public static DataSource createDataSource(Properties properties) | 根据Properties对象创建Druid连接池对象 |
案例演示
需求
使用druid创建连接池,从连接池中得到连接对象,输出得到的连接对象。
实现步骤
1、创建一个项目
2、导入druid的jar包和数据库驱动包
3、配置druid的配置文件
4、编写代码创建druid的连接池对象并获取连接打印
实现代码
导入的jar包截图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Kf7ojCQg-1606228060208)(assets/image-20200520143352056.png)]
在src目录下新建一个DRUID配置文件,命名为:druid.properties
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xNwgd5dW-1606228060209)(assets/image-20200520143528555.png)]
url=jdbc:mysql://localhost:3306/day24?characterEncoding=utf8
username=root
password=root
driverClassName=com.mysql.jdbc.Driver
initialSize=5
maxActive=10
maxWait=3000
Java代码
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.util.Properties;
/**
* @author 黑马程序员
*/
public class DemoTest {
public static void main(String[] args) throws Exception {
/*
* 目标:使用druid连接池获取连接
* */
//1.读取配置文件输入流
InputStream inputStream = DemoTest.class.getResourceAsStream("/druid.properties");
//注意路径中的前面“/”是不可以省略的
//2.创建Properties对象解析输入流
Properties properties = new Properties();
properties.load(inputStream);
//3、创建Druid连接池对象
DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
//2.获取连接12次,打印输出每一个连接
for (int i = 0; i < 12; i++) {
Connection conn = dataSource.getConnection();
System.out.println(conn); //打印
//当i==4的时候,关闭当前连接放回到连接池
if(i==4){
conn.close();
}
}
}
}
效果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vay7ioRa-1606228060210)(assets/image-20200520144109761.png)]
小结
Druid连接池使用步骤
1、导入druid的jar包和数据库驱动包
2、配置druid的配置文件
3、创建druid连接池对象
3.1 获取druid的配置文件输入流
3.2 创建Properties对象加载解析配置文件输入流
3.3 使用DruidDataSourceFactory.createDataSource(properties)创建连接池对象
12.连接池:优化JdbcUtils与增删改查【扩展】
环境搭建
1、创建项目
2、导包:druid的包和数据库驱动包
3、导入druid的配置文件
4、导入之前我们的JdbcUtils.java的工具类
搭建的项目结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DM8HZEzz-1606228060211)(assets/image-20200520144947895.png)]
JdbcUtils使用Druid优化代码
package com.itheima.util;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Properties;
/**
* @author 黑马程序员
*/
public class JdbcUtils {
//定义druid连接池对象
private static DataSource dataSource = null;
static {
try {
//1.读取配置文件输入流
InputStream inputStream = JdbcUtils.class.getResourceAsStream("/druid.properties");
//注意路径中的前面“/”是不可以省略的
//2.创建Properties对象解析输入流
Properties properties = new Properties();
properties.load(inputStream);
//3、创建Druid连接池对象
dataSource = DruidDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
e.printStackTrace();
}
}
//1、对外提供获取数据库连接的方法
public static Connection getConnection() throws Exception {
//从druid连接池中获取连接返回
return dataSource.getConnection();
}
//2、对外提供释放资源的方法
//2.1 提供查询释放3个资源的方法
public static void close(Connection conn, Statement stmt, ResultSet rs) throws Exception {
if(rs!=null){
rs.close();
}
if(stmt!=null){
stmt.close();
}
if(conn!=null){
conn.close();
}
}
//2.2 提供增删改释放2个资源的方法
public static void close(Connection conn, Statement stmt) throws Exception {
close(conn,stmt,null);
}
}
增删改查案例代码
package _使用JdbcUtils优化后进行CURD操作;
import com.itheima.entity.Student;
import com.itheima.util.JdbcUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;
/**
* @author 黑马程序员
*/
public class Demo1 {
public static void main(String[] args) throws Exception {
//insert();
//update();
//delete();
select();
}
//插入数据的方法
private static void insert() throws Exception {
//1、定义sql语句模板
String sql = "insert into student values(null,?,?,?)";
//2、获取连接
Connection conn = JdbcUtils.getConnection();
//3、获取预编译对象PreparedStatement
PreparedStatement pstmt = conn.prepareStatement(sql);
//4、设置sql语句模板中占位符的值
pstmt.setString(1,"传智"); //给name赋值
pstmt.setString(2,"1"); //给gender赋值
//第二种方法给char(1)赋值: pstmt.setBoolean(2,true); true代表1,false代表0
pstmt.setString(3,"2006-01-01"); //birthday赋值
//5、执行命令
int count = pstmt.executeUpdate();
//6、释放资源
JdbcUtils.close(conn,pstmt);
System.out.println("插入成功");
}
//修改数据的方法: 将id=4的name修改为“播妞”
private static void update() throws Exception {
//1、定义sql语句模板
String sql = "update student set name=? where id=?";
//2、获取连接
Connection conn = JdbcUtils.getConnection();
//3、获取预编译对象PreparedStatement
PreparedStatement pstmt = conn.prepareStatement(sql);
//4、设置sql语句模板中占位符的值
pstmt.setString(1,"播妞"); //name赋值
pstmt.setInt(2,4); //id赋值
//5、执行命令
int count = pstmt.executeUpdate();
//6、释放资源
JdbcUtils.close(conn,pstmt);
System.out.println("更新成功");
}
//删除数据的方法: 将id=1的数据删除
private static void delete() throws Exception {
//1、定义sql语句模板
String sql = "delete from student where id=?";
//2、获取连接
Connection conn = JdbcUtils.getConnection();
//3、获取预编译对象PreparedStatement
PreparedStatement pstmt = conn.prepareStatement(sql);
//4、设置sql语句模板中占位符的值
pstmt.setInt(1,1);
//5、执行命令
int count = pstmt.executeUpdate();
//6、释放资源
JdbcUtils.close(conn,pstmt);
System.out.println("删除成功");
}
public static void select() throws Exception {
/*
* 目标:从数据库查询多条记录的数据封装到List<Student>集合中
* */
//1、定义存储数据的集合
List<Student> studentList = new ArrayList<>();
//2、定义sql语句模板
String sql="select * from student";
//3、获取连接
Connection conn = JdbcUtils.getConnection();
//4、创建预编译对象
PreparedStatement pstmt = conn.prepareStatement(sql);
//5、执行命令获取结果集
ResultSet rs = pstmt.executeQuery();
//6、遍历结果集游标循环读取数据并封装到集合中
while(rs.next()){
//读取每一条数据封装到Student对象中
Student student = new Student();
student.setId(rs.getInt("id"));
student.setName(rs.getString("name"));
student.setGender(rs.getBoolean("gender"));
student.setBirthday(rs.getDate("birthday"));
//将student对象追加到集合中
studentList.add(student);
}
//7、打印集合数据
studentList.forEach(System.out::println);
/*for (Student student : studentList) {
System.out.println(student);
}*/
//8、释放资源
JdbcUtils.close(conn,pstmt,rs);
}
}
13.web的登录案例【扩展】
案例需求
操作数据库进行登录实现,需要使用jdbc(预编译运输器),druid完成web登录操作,符合MVC+三层架构
案例效果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SZA9c26R-1606228060212)(assets/image-20200520155953573.png)]
数据准备(已经有了,不用在执行)
CREATE TABLE USER (
id INT AUTO_INCREMENT PRIMARY KEY,
NAME VARCHAR(50),
PASSWORD VARCHAR(50)
);
INSERT INTO USER (NAME, PASSWORD) VALUES('admin', '123'), ('test', '123'), ('gm', '123');
环境搭建
登录页面login.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="login" method="post">
<table>
<tr>
<td>用户名</td>
<td><input type="text" name="username"/></td>
</tr>
<tr>
<td>密码</td>
<td><input type="password" name="password"/></td>
</tr>
<tr>
<td>验证码:</td>
<td><input type="text" name="checkcode"/></td>
</tr>
<tr>
<td></td>
<td><img id="img" src="CheckCode1Servlet"/></td>
</tr>
<tr>
<td></td>
<td><input type="submit" value="登录"/></td>
</tr>
</table>
</form>
<script type="text/javascript" src="js/jquery-3.3.1.min.js"></script>
<script type="text/javascript">
//给验证码图片注册点击事件
$("#img").click(function () {
$(this).attr("src","CheckCode1Servlet?time="+new Date().getTime());
});
</script>
</body>
</html>
CheckCode1Servlet
package com.itheima.web.servlet;
import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;
/**
* @author 黑马程序员
* 说明:古老的随机4个字符的验证码图片输出
*/
@WebServlet(name = "CheckCode1Servlet", urlPatterns = "/CheckCode1Servlet")
public class CheckCode1Servlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse
response) throws ServletException, IOException {
// 创建画布
int width = 120;
int height = 40;
BufferedImage bufferedImage = new BufferedImage(width, height,
BufferedImage.TYPE_INT_RGB);
// 获得画笔
Graphics g = bufferedImage.getGraphics();
// 填充背景颜色
g.setColor(Color.white);
g.fillRect(0, 0, width, height);
// 绘制边框
g.setColor(Color.red);
g.drawRect(0, 0, width - 1, height - 1);
// 生成随机字符
// 准备数据
String data =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890";
// 准备随机对象
Random r = new Random();
// 声明一个变量 保存验证码
String code = "";
// 书写4个随机字符
for (int i = 0; i < 4; i++) {
// 设置字体
g.setFont(new Font("宋体", Font.BOLD, 28));
// 设置随机颜色
g.setColor(new Color(r.nextInt(255), r.nextInt(255),
r.nextInt(255)));
String str = data.charAt(r.nextInt(data.length())) + "";
g.drawString(str, 10 + i * 28, 30);
// 将新的字符 保存到验证码中
code = code + str;
}
// 绘制干扰线
for (int i = 0; i < 6; i++) {
// 设置随机颜色
g.setColor(new Color(r.nextInt(255), r.nextInt(255),
r.nextInt(255)));
g.drawLine(r.nextInt(width), r.nextInt(height), r.nextInt(width),
r.nextInt(height));
}
// 将验证码 打印到控制台
System.out.println(code);
// 将验证码放到session中
request.getSession().setAttribute("code_session", code);
// 将画布显示在浏览器中
ImageIO.write(bufferedImage, "jpg", response.getOutputStream());
}
protected void doPost(HttpServletRequest request, HttpServletResponse
response) throws ServletException, IOException {
doGet(request, response);
}
}
druid的包、数据库驱动包(注意必须放在WEB-INF/lib目录内)、druid的配置文件、JdbcUtils
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BVnM4K3P-1606228060213)(assets/image-20200520152952461.png)]
导入jquery
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FARhjUQ2-1606228060214)(assets/image-20200520154835196.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9qdcP4Ni-1606228060215)(assets/image-20200520155024575.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yBxpZ0Ln-1606228060216)(assets/image-20200520155513430.png)]
实现步骤
实现代码
User类
package com.itheima.entity;
/**
* @author 黑马程序员
*/
public class User {
private int id;
private String name;
private String password;
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", password='" + password + '\'' +
'}';
}
public User() {
}
public User(int id, String name, String password) {
this.id = id;
this.name = name;
this.password = password;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
UserDao代码
package com.itheima.dao;
import com.itheima.entity.User;
import com.itheima.util.JdbcUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
/**
* @author 黑马程序员
*/
public class UserDao {
//根据用户名与密码查找用户数据
public User find(String username, String password)throws Exception{
//执行数据库操作查找用户对象数据并返回
//1.定义模板sql语句
String sql= "select * from user where name=? and password=?";
//2.执行数据库操作命令
Connection conn = JdbcUtils.getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1,username);
pstmt.setString(2,password);
ResultSet rs = pstmt.executeQuery();
User user = null;
//3.封装返回的结果
if(rs.next()){
//根据用户名与密码最后只会查询到一条记录数据,所以这里只需要rs.next()移动一次游标
//进入if说明用户名与密码正常查询到了用户对象
user = new User();
user.setId(rs.getInt("id"));
user.setName(rs.getString("name"));
user.setPassword(rs.getString("password"));
}
//释放资源放回连接池
JdbcUtils.close(conn,pstmt,rs);
//4.返回User对象
return user;
}
}
UserService
package com.itheima.service;
import com.itheima.dao.UserDao;
import com.itheima.entity.User;
/**
* @author 黑马程序员
*/
public class UserService {
//实例
private UserDao userDao = new UserDao();
//根据用户名与密码查找用户
public User find(String username, String password) throws Exception {
//调用数据库查找用户对象数据
return userDao.find(username,password);
}
}
LoginServlet代码
package com.itheima.web.servlet;
import com.itheima.entity.User;
import com.itheima.service.UserService;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author 黑马程序员
*/
@WebServlet(name = "LoginServlet", urlPatterns = "/login")
public class LoginServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
//实例业务类
private UserService userService = new UserService();
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.获取请求数据用户名、密码、验证码
String username = request.getParameter("username");
String password = request.getParameter("password");
String checkcode = request.getParameter("checkcode");
//2.校验验证码
//从session中获取验证码
String sessionCheckCode = (String) request.getSession().getAttribute("code_session");
//与用户输入的验证码进行校验
if(!checkcode.equalsIgnoreCase(sessionCheckCode)){
//存储错误信息“验证码错误”,跳转到登录页面显示错误消息
request.setAttribute("error","验证码错误");
request.getRequestDispatcher("/login.jsp").forward(request,response);
return;
}
try {
//3.调用业务方法根据用户名与密码查找用户
User loginUser = userService.find(username, password);
response.setContentType("text/html;charset=utf8");
if(loginUser!=null) {
//4.用户对象有效,说明登录成功
response.getWriter().print("登录成功");
}else {
//5.用户对象无效,说明登录失败
response.getWriter().print("登录失败");
}
} catch (Exception e) {
e.printStackTrace();
//抛出运行时异常,目的就是发生异常了通知客户
throw new RuntimeException(e);
}
}
}
login.jsp显示错误信息
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1sTixdeK-1606228060217)(assets/image-20200520160026943.png)]
13.学习总结
-
PreparedStatement优势
1、防止sql注入攻击
2、预编译执行效率高
-
能够通过PreparedStatement完成增、删、改、查
创建PreparedStatement: connection.createPrepareStatement(sql);
设置占位符参数: pstmt.setXxxx(位置,占位符值); 位置从1开始
执行查询命令:pstmt.executeQuery();
执行增、删、改命令:pstmt.executeUpdate();
-
能够完成PreparedStatement改造登录案例
-
会使用连接池
c3p0连接池操作步骤
导包:c3p0的2个jar包和数据库驱动包
导配置文件:c3p0-config.xml
修改配置文件(配置数据库连接信息、最大连接数、初始化连接数、用户等待最大超时时间)
使用ComboPooledDataSource连接池对象获取连接
druid连接池操作
导包:druid的1个jar包和数据库驱动包
导配置文件:druid.properties
修改配置文件(配置数据库连接信息、最大连接数、初始化连接数、用户等待最大超时时间)
自己读取配置文件的输入流,创建Properties对象解析输入流数据
根据DruidDataSourceFactory.createDataSource(properties)创建连接池对象
单词
单词 | 含义 | 音标 |
---|---|---|
DataSource | 数据源 | |
Druid | 德鲁伊,品牌名,阿里巴巴旗下开源数据库连接池组件 | |
Prepared | 准备的,预编译 | |
Combo | 连接,联合 | |
pool | 池 | |
initialSize | 初始化大小 | |
//4.用户对象有效,说明登录成功
response.getWriter().print("登录成功");
}else {
//5.用户对象无效,说明登录失败
response.getWriter().print("登录失败");
}
} catch (Exception e) {
e.printStackTrace();
//抛出运行时异常,目的就是发生异常了通知客户
throw new RuntimeException(e);
}
}
}
login.jsp显示错误信息
[外链图片转存中...(img-1sTixdeK-1606228060217)]
# 13.学习总结
1. PreparedStatement优势
1、防止sql注入攻击
2、预编译执行效率高
2. 能够通过PreparedStatement完成增、删、改、查
> 创建PreparedStatement: connection.createPrepareStatement(sql);
>
> 设置占位符参数: pstmt.setXxxx(位置,占位符值); 位置从1开始
>
> 执行查询命令:pstmt.executeQuery();
>
> 执行增、删、改命令:pstmt.executeUpdate();
2. 能够完成PreparedStatement改造登录案例
3. 会使用连接池
c3p0连接池操作步骤
> 导包:c3p0的2个jar包和数据库驱动包
>
> 导配置文件:c3p0-config.xml
>
> 修改配置文件(配置数据库连接信息、最大连接数、初始化连接数、用户等待最大超时时间)
>
> 使用ComboPooledDataSource连接池对象获取连接
druid连接池操作
> 导包:druid的1个jar包和数据库驱动包
>
> 导配置文件:druid.properties
>
> 修改配置文件(配置数据库连接信息、最大连接数、初始化连接数、用户等待最大超时时间)
>
> 自己读取配置文件的输入流,创建Properties对象解析输入流数据
>
> 根据DruidDataSourceFactory.createDataSource(properties)创建连接池对象
单词
| 单词 | 含义 | 音标 |
| ----------- | ----------------------------------------------- | ---- |
| DataSource | 数据源 | |
| Druid | 德鲁伊,品牌名,阿里巴巴旗下开源数据库连接池组件 | |
| Prepared | 准备的,预编译 | |
| Combo | 连接,联合 | |
| pool | 池 | |
| initialSize | 初始化大小 | |
| | | |