- 一 JavaWeb简介
- 什么是javaweb
- 二 数据库知识的回顾
- 数据库相关概念
- 常见的关系型数据库管理系统
- 三 JDBC
- JDBC简介
- JDBC本质
- JDBC好处:
- idea操作数据库操作步骤
- 四 JDBC详解
- DriverManager
- 非人话的官方介绍
- 注意两个方法
- 人性化总结
- 注册驱动详解
- 获取连接
- 栗子
- Connection
- 人性化总结
- 获取执行sql对象
- 管理事务
- 栗子
- Statement
- 栗子
- ResultSet
- 栗子
- 案例
- Preparedstatement
- SQL注入展示
- 原因总结
- Preparedstatement使用详解
- 栗子
- 再来一次sql注入
- Preparedstatement原理
- 五 数据库连接池
- 数据库连接池简介
- Druid数据库连接池
- 数据库连接池实现
- Web:World Wide Web(www) 全球广域网,万维网
- JavaWeb:是用Java来解决web互联网领域的技术栈
- 数据库
- 存储数据的仓库,数据是有组织的进行存储
- 英文:DataBase,简称DB
- 数据库管理系统
- 管理数据库的大型软件
- 英文:DataBaseManagementSystem,简称DBMS
- SQL
- 英文:StructuredQueryLanguage,简称SQL,结构化查询语言
- 操作关系型数据库的编程语言
- 定义操作所有关系型数据库的统一标准
- Oracle:收费的大型数据库,Oracle公司的产品
- MySQL:开源免费的中小型数据库。后来Sun公司收购了MySQL,而Sun公司又被Oracle收购
- SQLServer:MicroSoft公司收费的中型的数据库。C#、net等语言常使用
- PostgresQL:开源免费中小型的数据库
- DB2:IBM公司的大型收费数据库产品
- SQLite:嵌入式的微型数据库。如:作为Android内置数据库
- MariaDB:开源免费中小型的数据库
- 事务简介
- (Transaction)是一种机制、
- 数据库的事务一个操作序列,包含了一组数据库操作命令
- 事务把所有的命令作为一个整体一起向系统提交或撤销操作请求,即这一组数据库命令要么同
时成功,要么同时失败 - 事务是一个不可分割的工作逻辑单元
- 事务四大特征
- 原子性(Atomicity):事务是不可分割的最小操作单位,要么同时成功,要么同时失败
- 一致性(Consistency):事务完成时,必须使所有的数据都保持一致状态
- 隔离性(lsolation):多个事务之间,操作的可见性
- 持久性(Durability):事务一旦提交或回滚它对数据库中的数据的改变就是永久的
- MYSQL事务默认自动提交:
-- 查看事务的默认提交方式 SELECt @@autocommit; -- 1自动提交 0 手动提交 --修改事务提交方式 set @@autocommit=0;
三 JDBC JDBC简介MySQL的默认提交方式是自动的,Oracle是手动的
- JDBC就是使用JAVA语言操作关系型数据库的一套API
- 全称:(Java DataBase Connectivity)Java数据库连接
//1.注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//2.获取链接
String url="jdbc:mysql://127.0.0.1:3306/db1?useSSL=false";
String username="root";
String password="yqk.20021027";
Connection conn = DriverManager.getConnection(url, username, password);
//3.定义sql
String sql="update account set salary=2000+salary where id=1";
//4.获取执行sql的对象 Statement
Statement stmt = conn.createStatement();
//5.执行sql
int count = stmt.executeUpdate(sql);
//6.处理结果
System.out.println("受影响的行数为:"+count);
//7.释放资源[先释放后创建的,再释放之前创建的]
stmt.close();
conn.close();
- 同一套JAVA代码,操作不同的关系型数据库
- 官方(sun公司)定义的一套操作所有关系型数据库的规则,即接口 - 各个数据库厂商去实现这套接口,提供数据库驱动jar包 - 我们可以使用这套接口(JDBC)编程, - 真正执行的代码是驱动jar包中的实现类JDBC好处:
- 各数据库厂商使用相同的接口,Java代码不需要针对不同数据库分别开发
- 可随时替换底层数据库访问数据库的Java代码基本不变
- 创建一个空的项目
- 窗机成功后,再弹出的界面设置项目的SDK、项目语言级别
- 新建JAVA模块,设置模块名
- 再新的模块的mian目录下,新建Lib文件夹
- 在lib目录下,放入数据库的接口Jar包
导入驱动jar包
- 右键将jar包设置为 模块库
- 在src目录下新建Java类文件
DriverManager(驱动管理类)作用:
- 注册驱动
- 获取数据库连接
- 代码
Class.forName("com.mysql.jdbc.Driver");//旧版本5.x
Class.forName("com.mysql.cj.jdbc.Driver");//新版本8.x
- 查看Driver类源码
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
public Driver() throws SQLException {
}
static {
try {
DriverManager.registerDriver(new Driver());
} catch (SQLException var1) {
throw new RuntimeException("Can't register driver!");
}
}
}
- 查看Driver类源码的操作方法
- 提示:
- MYSQL5之后的驱动包,可以省略注册驱动的步骤。
- 自动加载jar包META-INF/services/java.sql.Driver文件中的驱动类
static Connection getConnection(String url, String user, String password)
- 参数:
-
- url:连接路径
-
- user:用户名
-
- password:密码
-
语法:jdbc:mysql://ip地址(域名localhost):端口号/数据库名称?参数值对1&初始值对2... 实例:jdbc:mysql://127.0.0.1:3306/db1 细节: 如果连接的是本机MYSQL服务器,并且MySQL服务器默认的端口号是3306,则url可以简写为:jdbc:mysql:///数据库名称?初始值对 配置useSSL=false参数,禁用安全连接方式,解决警告提示栗子
public static void main(String[] args) throws Exception {
//1.注册驱动
//5.0 版本之后可以不写 jar包中META-INFservicesJava.sql.Driver中有
Class.forName("com.mysql.cj.jdbc.Driver");
//2.获取链接
String url="jdbc:mysql://127.0.0.1:3306/db1";
String username="root";
String password="我的密码";
Connection conn = DriverManager.getConnection(url, username, password);
//3.定义sql
@SuppressWarnings("SqlResolve") //对sql语句禁行检查
String sql="update account set salary=2000+salary where id=1";
//4.获取执行sql的对象 Statement
Statement stmt = conn.createStatement();
//5.执行sql
int count = stmt.executeUpdate(sql);
//6.处理结果
System.out.println("受影响的行数为:"+count);
//7.释放资源[先释放后创建的,再释放之前创建的]
stmt.close();
conn.close();
}
Connection
人性化总结
- Connection(数据库连接对象)作用:
-
- 获取执行sql的对象
-
- 管理事务
-
- 普通执行sql对象
Statement createStatement()
- 预编译sql的执行sql对象:防止sql注入
PreparedStatement prepareStatement(sql)
- 执行存储过程的对象
CallableStatement prepareCall(sql)管理事务
- MYSQL事务管理
开启事务:BEGIN;/START TRANSACTION;
提交事务:COMMIT;
回滚事务:ROLLBACK;
MYSQL默认自动提交事务
- JDBC事务管理:Connetion接口中定义了三个对应的方法
栗子开启事务:setAutocommit(boolean autoCommit);
- true为自动提交事务;false为手动提交事务,即为开启事务
提交事务:commit()
回滚事务:rollback()
public static void main(String[] args) throws Exception {
//1.注册驱动
//5.0 版本之后可以不写 jar包中META-INFservicesJava.sql.Driver中有
//Class.forName("com.mysql.cj.jdbc.Driver");
//2.获取链接
String url="jdbc:mysql://127.0.0.1:3306/db1";
String username="root";
String password="我的密码";
Connection conn = DriverManager.getConnection(url, username, password);
//3.定义sql
String sql1="update account set salary=2000 where id=1";
String sql2="update account set salary=2000 where id=2";
//4.获取执行sql的对象 Statement
Statement stmt = conn.createStatement();
try {
//开启事务
conn.setAutoCommit(false);
//5.执行sql 受影响的行数
int count1 = stmt.executeUpdate(sql1);
//6.处理结果
System.out.println("受影响的行数为:"+count1);
//手动制造一个错误
int i=3/0;
//5.执行sql 受影响的行数
int count2 = stmt.executeUpdate(sql2);
//6.处理结果
System.out.println("受影响的行数为:"+count2);
//提交事务
conn.commit();
} catch (SQLException throwables) {
//回滚事务
conn.rollback();
throwables.printStackTrace();
}
//7.释放资源[先释放后创建的,再释放之前创建的]
stmt.close();
conn.close();
}
Statement
- Statement作用:
-
- 执行SQL语句
-
- 执行SQL语句
- int executeUpdate:执行DML,DDL语句
- 返回值:(1) DML语句影响的行数(2)DDL语句执行之后,执行成功也可能返回0
- ResultSet executeQuery:执行DQL语句
- 返回值:Result结果集对象
栗子DML可以通过受影响的行数,判断是否执行成功
DDL只要不报错,就说明执行成功
public static void main(String[] args) {
try {
System.out.println("请输入您的选项:");
System.out.println("1——执行DML语句");
System.out.println("2——执行DDL语句");
Scanner sc=new Scanner(System.in);
int select=sc.nextInt();
if(select==1) {
testDML();
}
else if(select==2) {
testDDL();
}
else {
System.out.println("请重新选择!");
}
} catch (Exception e) {
e.printStackTrace();
}
}
//测试 Data Manipulation Language, DML
public static void testDML() throws Exception {
//1.注册驱动
//5.0 版本之后可以不写 jar包中META-INFservicesJava.sql.Driver中有
//Class.forName("com.mysql.cj.jdbc.Driver");
//2.获取链接
String url="jdbc:mysql://127.0.0.1:3306/db1";
String username="root";
String password="秘密";
Connection conn = DriverManager.getConnection(url, username, password);
//3.定义sql
@SuppressWarnings("SqlResolve") //对此语句,禁用检查
String sql="update account set salary=2000 where id=1";
//4.获取执行sql的对象 Statement
Statement stmt = conn.createStatement();
//5.执行sql
int count = stmt.executeUpdate(sql);//执行DML完之后吗,受影响的函数
//6.处理结果
//System.out.println("受影响的行数为:"+count);
if(count>0) {
System.out.println("操作成功!");
}else {
System.out.println("操作失败!");
}
//7.释放资源[先释放后创建的,再释放之前创建的]
stmt.close();
conn.close();
}
public static void testDDL() throws Exception {
//1.注册驱动
//5.0 版本之后可以不写 jar包中META-INFservicesJava.sql.Driver中有
//Class.forName("com.mysql.cj.jdbc.Driver");
//2.获取链接
String url="jdbc:mysql://127.0.0.1:3306/db1";
String username="root";
String password="秘密";
Connection conn = DriverManager.getConnection(url, username, password);
//3.定义sql
@SuppressWarnings("SqlResolve") //对此语句,禁用检查
//String sql="create database db2";
String sql="drop database db2";
//4.获取执行sql的对象 Statement
Statement stmt = conn.createStatement();
//5.执行sql
int count = stmt.executeUpdate(sql);//执行DDL完之后吗,返回的可能是0
//6.处理结果
//System.out.println("受影响的行数为:"+count);
if(count>0) {
System.out.println("操作成功!");
}else {
System.out.println("操作失败!");
}
//7.释放资源[先释放后创建的,再释放之前创建的]
stmt.close();
conn.close();
}
ResultSet
- 使用步骤:
-
- 游标向下移动一行,并判断该行是否有数据:next()
-
- 获取数据:getXxx(参数)
-
栗子//循环判断游标是否是最后一行末尾
while(rs.next()) {
//获取数据
rs.getXxx(参数);
}
public static void main(String[] args) throws Exception {
testResultSet();
}
//测试 DQL:查询语句
public static void testResultSet() throws Exception {
//1.注册驱动
//5.0 版本之后可以不写 jar包中META-INFservicesJava.sql.Driver中有
//Class.forName("com.mysql.cj.jdbc.Driver");
//2.获取链接
String url="jdbc:mysql:///db1";
String username="root";
String password="秘密";
Connection conn = DriverManager.getConnection(url, username, password);
//3.定义sql
@SuppressWarnings("SqlResolve") //对此语句,禁用检查
String sql="select * from account";
//4.获取Statement对象
Statement stmt = conn.createStatement();
//5.执行sql
ResultSet rs = stmt.executeQuery(sql);
//6.处理结果
//6.1 光标向下移动一行,并且判断当前行是否有数据
while(rs.next()) {
//6.2 获取数据 getXxx
int id=rs.getInt("id");
String name=rs.getString("name");
int money=rs.getInt("salary");
System.out.print(id+"t");
System.out.print(name+"t");
System.out.print(money+"t");
System.out.println();
}
//7.释放资源
rs.close();
stmt.close();
conn.close();
}
案例
package com.yang.pojo;
public class Account {
private int id;
private String name;
private int salary;
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 double getSalary() {
return salary;
}
public void setSalary(int salary) {
this.salary = salary;
}
@Override
public String toString() {
return "Account{" +
" + id +
", name='" + name + ''' +
", salary=" + salary +
'}';
}
}
package com.yang.jdbc;
import com.yang.pojo.Account;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
public class ResultApplication {
public static void main(String[] args) throws Exception {
testResultSet();
}
public static void testResultSet() throws Exception {
//1.注册驱动
//5.0 版本之后可以不写 jar包中META-INFservicesJava.sql.Driver中有
//Class.forName("com.mysql.cj.jdbc.Driver");
//2.获取链接
String url="jdbc:mysql:///db1";
String username="root";
String password="密码";
Connection conn = DriverManager.getConnection(url, username, password);
//3.定义sql
@SuppressWarnings("SqlResolve") //对此语句,禁用检查
String sql="select * from account";
//4.获取Statement对象
Statement stmt = conn.createStatement();
//5.执行sql
ResultSet rs = stmt.executeQuery(sql);
//6.处理结果
//6.1 光标向下移动一行,并且判断当前行是否有数据
//创建集合
List list=new ArrayList<>();
while(rs.next()) {
Account account=new Account();
//6.2 获取数据 getXxx
int id=rs.getInt("id");
String name=rs.getString("name");
int money=rs.getInt("salary");
//赋值
account.setId(id);
account.setName(name);
account.setSalary(money);
//存入集合
list.add(account);
}
System.out.println(list);
//7.释放资源
rs.close();
stmt.close();
conn.close();
}
}
Preparedstatement
- Preparedstatement作用:
-
- 预编译SQL语句并执行:预防SQL注入问题
-
- SQL注入
- SQL注入是通过操作输入来修改事先定义好的SQL语句,用以达到执行代码对服务器进行攻击的方法
- 账户的用户名和密码
- 正常的登录
package com.yang.jdbc;
import java.sql.*;
public class JDBCDemo6_UserLogin {
//用户登录
public static void main(String[] args) throws SQLException {
//2.获取登录:如果连接的是本机mysql并且端口是默认的3306 可以简化书写
String url="jdbc:mysql:///db1";
String username="root";
String password="秘密";
Connection conn = DriverManager.getConnection(url, username, password);
//接受用户输入 用户名和密码
String name="张三";
String pwd="123456";
@SuppressWarnings("SqlResolve")
String sql="select * from tb_user where username='"+name+"' and password='"+pwd+"'";
//获取stmt对象
Statement stmt = conn.createStatement();
//执行sql
ResultSet rs=stmt.executeQuery(sql);
//判断是否成功
if(rs.next()) {
System.out.println("登陆成功!");
}else {
System.out.println("登陆失败!");
}
//释放资源
rs.close();
stmt.close();
conn.close();
}
}
- SQL注入展示
package com.yang.jdbc;
import java.sql.*;
public class JDBCDemo6_UserLogin_Inject {
//用户登录
public static void main(String[] args) throws SQLException {
//2.获取登录:如果连接的是本机mysql并且端口是默认的3306 可以简化书写
String url="jdbc:mysql:///db1";
String username="root";
String password="秘密";
Connection conn = DriverManager.getConnection(url, username, password);
//接受用户输入 用户名和密码
String name="张三";
String pwd="' or '1'='1";
@SuppressWarnings("SqlResolve")
String sql="select * from tb_user where username='"+name+"' and password='"+pwd+"'";
System.out.println(sql);
//获取stmt对象
Statement stmt = conn.createStatement();
//执行sql
ResultSet rs=stmt.executeQuery(sql);
//判断是否成功
if(rs.next()) {
System.out.println("登陆成功!");
}else {
System.out.println("登陆失败!");
}
//释放资源
rs.close();
stmt.close();
conn.close();
}
}
原因总结
- 因为登录的查询原理是字符串的拼接,输入的奇怪密码,使原来的sql语句改变了语义。
- 预编译SQL并执行SQL语句
- 获取Preparedstatement对象
//SQL语句中的参数值,使用?占位符代替 String sql="select * from user where username=? and password=?"; //通过Connection对象获取,并传入对应的sql语句 PrepareStatement pstmt=conn.prepareStatement(sql);
- 设置参数
PrepareStatement对象:setXxx(参数1,参数2):给?赋值 Xxx:数据类型:如setInt(参数1,参数2); 参数: 参数1:?的位置编号,从1开始 参数2:?的值
- 执行SQL
executeUpdate();/executeQuery();//不需要再传递SQL栗子
package com.yang.jdbc;
import java.sql.*;
public class JDBCDemo7_PreparedStatement {
public static void main(String[] args) throws SQLException {
//2.获取登录:如果连接的是本机mysql并且端口是默认的3306 可以简化书写
String url="jdbc:mysql:///db1";
String username="root";
String password="秘密";
Connection conn = DriverManager.getConnection(url, username, password);
//接受用户输入 用户名和密码
String name="张三";
String pwd="123456";
//定义sql
@SuppressWarnings("SqlResolve")
String sql="select * from tb_user where username=? and password=?";
//获取pStmt对象
PreparedStatement pStmt = conn.prepareStatement(sql);
//设置?的值
pStmt.setString(1,name);
pStmt.setString(2,pwd);
//执行sql
ResultSet rs=pStmt.executeQuery();
//判断是否成功
if(rs.next()) {
System.out.println("登陆成功!");
}else {
System.out.println("登陆失败!");
}
//释放资源
rs.close();
pStmt.close();
conn.close();
}
}
再来一次sql注入
package com.yang.jdbc;
import java.sql.*;
public class JDBCDemo7_PreparedStatement2 {
public static void main(String[] args) throws SQLException {
//2.获取登录:如果连接的是本机mysql并且端口是默认的3306 可以简化书写
//useServerPrepStmts=true 参数开启预编译功能
String url="jdbc:mysql:///db1?useSSL=false&useServerPrepStmts=true";
String username="root";
String password="秘密";
Connection conn = DriverManager.getConnection(url, username, password);
//接受用户输入 用户名和密码
String name="张三";
String pwd="' or '1'='1";
//定义sql
@SuppressWarnings("SqlResolve")
String sql="select * from tb_user where username=? and password=?";
//获取pStmt对象
PreparedStatement pStmt = conn.prepareStatement(sql);
//设置?的值
pStmt.setString(1,name);
pStmt.setString(2,pwd);
//执行sql
ResultSet rs=pStmt.executeQuery();
//判断是否成功
if(rs.next()) {
System.out.println("登陆成功!");
}else {
System.out.println("登陆失败!");
}
//释放资源
rs.close();
pStmt.close();
conn.close();
}
}
Preparedstatement原理
- Preparedstatement好处:
- 1.预编译SQL,性能更高
- 2.防止SQL注入:将敏感字符进行转义
①PreparedStatement预编译功能开启:useServerPrepStmts=true
②配置MySQL执行日志(重启mysaI服务后生效)
注意:文件的路径可以自己设置
log-output=FILE general-log=1 general_log_file="C:ProgramDataMySQLMySQL Server 8.0mysql.log" slow-query-log=1 slow_query_log_file="C:ProgramDataMySQLMySQL Server 8.0mysql_slow.log" long_query_time=2
- Preparedstatement原理:
-
- 在获取Preparedstatement对象时,将sql语句发送给mysql服务器进行检查,编译(这些步骤很耗时)
-
- 执行时就不用再进行这些步骤了,速度更快
-
- 如果sql模板一样,则只需要进行一次检查、编译
- 如果sql模板一样,则只需要进行一次检查、编译
-
- 日志文件中查看验证原理
- 对输入的文本进行转义,有效防止了sql注入
2022-07-01T13:34:00.895514Z 10 Query SET character_set_results = NULL 2022-07-01T13:34:00.895986Z 10 Query SET autocommit=1 2022-07-01T13:34:00.912890Z 10 Prepare select * from tb_user where username=? and password=? 2022-07-01T13:34:00.917551Z 10 Execute select * from tb_user where username='张三' and password='' or '1'='1' 2022-07-01T13:34:00.932721Z 10 Close stmt 2022-07-01T13:34:00.937886Z 10 Quit
- 和正常的登录对比
- 数据库连接池是个容器,负责分配、管理数据库连接(Connection)
- 它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个
- 释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据库连接遗漏
- 好处:
- 资源重用
- 提升系统响应速度
- 游免数据库连接遗漏
- 标准接口:DataSource
- 官方(SUN)提供的数据库连接池标准接口,由第三方组织实现此接口。
- 功能:获取连接
Connection getConnection()
- 常见的数据库连接池:
- DBCP
- C3PO
- Druid
- Druid(德鲁伊)
- Druid连接池是阿里巴巴开源的数据库连接池项目
- 功能强大,性能优秀,是Java语言最好的数据库连接池之一
Driud使用步骤
- 导入jar包druid-1.1.12.jar
- 定义配置文件
- 配置文件名:druid.properties
- 内容:
driverClassName=com.mysql.cj.jdbc.Driver url=jdbc:mysql:///db1?useSSL=false&useServerPrepStmts=true username=root password=Xxxxxx #初始化连接数量 initialSize=5 #最大连接数 maxActive=10 #最大等待时间 maxWait=3000
- 加载配置文件
新建文件
- 获取数据库连接池对象
- 获取连接
package com.yang.jdbc.druid;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.sql.Connection;
import java.util.Properties;
public class druidDemo {
public static void main(String[] args) throws Exception {
//1.导入jar包
//2.定义配置文件
//3.加载配置文件
Properties prop = new Properties();
prop.load(new FileInputStream("jdbc-demo/src/druid.properties"));
//4.获取连接池对象
DataSource dataSource = DruidDataSourceFactory.createDataSource(prop);
//5.获取数据库连接
Connection connection = dataSource.getConnection();
System.out.println(connection);
//System.out.println(System.getProperty("user.dir"));用来显示当前目录,解决第三步的报错
}
}



