JDBC
JDBC 的简介
JDBC概念:![](https://ucc.alicdn.com/notfound.png?x-oss-process=image/resize,w_1400/format,webp)
- JDBC就是使用java语言操作关系型数据库的一套API
- 全称:java database conectivity Java数据库连接
JDBC本质:
- 官方(sun公司)定义一套操作所有关系型数据库的规则,也就是接口
- 各个数据库厂商去实现这套接口,提供数据库驱动jar包
- 我们可以使用这套接口编程,真正执行的代码是驱动jar包中的实现类
JDBC的好处:
- 各数据库厂商使用相同的接口,java代码不需要针对不同的数据库分别开发
- 可以随时替换底层数据库,访问数据库的java代码基本不变
JDBC 的快速入门
- 创建工程,导入驱动jar包
- 注册驱动:Class.forName("com.mysql.jdbc.Driver");
- 获取连接:Connection conn = DriverManager.getConnection(url,username,password);
- 定义sql语句:String sql = "Update...";
- 获取执行sql对象:Statement stmt = conn.createStatement();
- 执行sql:stmt.executeUpdate(sql);
- 处理返回结果
- 释放资源
JDBC API详解
DriverManager
- DriverManager(驱动管理类)作用:注册驱动,获取数据库的连接
注册驱动
Class.forName("com.mysql.jdbc.Driver");
- 为什么没有使用Driver manager注册驱动呢?
来查看一下Driver源码:
publicclassDriverextendsNonRegisteringDriverimplementsjava.sql.Driver {
publicDriver() throwsSQLException {
}
static {
try {
DriverManager.registerDriver(newDriver());
} catch (SQLExceptionvar1) {
thrownewRuntimeException("Can't register driver!");
}
}
}
- 其实执行Driver类时他的静态代码块会跟随一同加载而DriverManager是不是在静态代码块里面呀!!
- 小知识:Mysql5之后的驱动包,可以省略注册驱动的步骤,会自动加载jar包中META-INF/services/java.sql.Driver文件中的驱动类获取连接
- static Connection getConnection(String url,username,String password)
- 参数
- url:连接路径语法:jdbc:mysql://ip地址:端口号/数据库名称?参数键值对1&参数键值对2...示例:jdbc:mysql://127.0.0.1:3306/db1细节:1如果连接的是本机mysql服务器,并且mysql服务器端口号是3306,则url可以简写为:jdbc:mysql:///数据库名称?参数键值对2配置useSSL=false(=前后不要有空格)参数,禁用安全连接方式,解决警告提示
- 案例
解除安全连接方式
Connection
- 获取执行sql的对象
- 普通执行sql对象:Statement createStatment()
- (重点)预编译sql的执行sql对象:防止sql注入:PrepareStatement prepareStatement(sql)
- 执行存储过程的对象:CallableStatement prepareCall(sql)
- 事物管理
- mysql事务管理开启事务:begin:start transaction;提交事务:commit回滚事务:rollback
- JDBC事务管理:Connection接口中定义了3个对应的方法开启事务:setAutoCommit(boolean autoCommit):true为自动提交事务,false为手动提交事务提交事务:commit()回滚事务:rollback()
- 案例
Statement
- Statement作用:执行sq语句
- 执行sql语句int executeUpdate(sql):执行DML,DDL语句返回值:(1)DML语句影响的行数
(2)DDL语句执行后,执行成功也可能返回0
ResultSet
- ResultSet(结果集对象)作用:封装了DQL查询语句的结果ResultSet stmt.executeQuery(sql):执行DQL语句,返回ResultSet对象
- 获取查询结果
- boolean neat():(1)将光标从当前位置向前移动一行(2)判断当前行是否为有效行返回值:true:有效行,当前行有数据 false :无效行,当前行无数据
- 数据类型 get数据类型(参数):获取数据例如:int getint(参数)参数:int:列的编号,从1开始(与我们认为的从0开始不同),String:列的名称
- 使用步骤:
- 1.游标向下移动一行并判断是否有数据:next()
- 2.获取数据:get数据类型(参数)
- while(rs.next()){
- //获取数据
- rs.get数据类型(参数)
- }
- 案例写一写
- 需求:查询account账户表数据,封装Account对象中,并且存储到ArrayList集合中
packagecom.wang.jdbc;
importcom.wang.pojo.Account;
importorg.junit.Test;
importjava.sql.*;
importjava.util.ArrayList;
importjava.util.List;
publicclassJDBCDemo4_ResultSet2 {
/**
*
*1.需求:查询account账户表数据,封装Account对象中,并且存储到ArrayList集合中
*2.查询数据,封装到Account对象中
*3.将Account对象存入ArrayList集合中
*/
@Test
publicvoidtestResultSet() throwsSQLException {
//2.获取连接:如果是连接的本地mysql服务器并且端口是3306的可以简化
Stringurl="jdbc:mysql:///db1?useSSL=false";
Stringusername="root";
Stringpassword="1234";
Connectionconn=DriverManager.getConnection(url, username, password);
//3.定义sql
Stringsql="select * from account";
//4.获取statemet对象
Statementstmt=conn.createStatement();
//5.执行sql
ResultSetrs=stmt.executeQuery(sql);
//创建集合
List<Account>list=newArrayList<>();
//6.处理结果,遍历
while (rs.next()){
Accountaccount=newAccount();
intid=rs.getInt("id");
Stringname=rs.getString("name");
doublemoney=rs.getDouble("money");
account.setId(id);
account.setName(name);
account.setMoney(money);
//存入集合
list.add(account);
}
System.out.println(list);
//7.释放资源
stmt.close();
rs.close();
conn.close();
}
}
PreparedStatement
- preparedStatement作用:
- 预编译SQL语句并执行:防止SQL注入问题
publicclassJDBCDemo5_UserLogin {
/**
*用户登录
*/
@Test
publicvoidtestResultSet() throwsSQLException {
//2.获取连接:如果是连接的本地mysql服务器并且端口是3306的可以简化
Stringurl="jdbc:mysql:///db1?useSSL=false";
Stringusername="root";
Stringpassword="1234";
Connectionconn=DriverManager.getConnection(url, username, password);
//接收用户输入 用户名和密码
Stringname="zhangsan";
Stringpwd="123";
Stringsql="select * from tb_user where username = '"+name+"' and password = '"+pwd+"'";
//获取stmt对象
Statementstmt=conn.createStatement();
//执行sql
ResultSetrs=stmt.executeQuery(sql);
//判断登录是否成功
if (rs.next()){
System.out.println("登入成功");
}else{
System.out.println("登入失败");
}
//7.释放资源
stmt.close();
rs.close();
conn.close();
}
}
- SQL注入:SQL注入是通过操作来修改事先定义好的SQL语句,用以达到执行代码对服务器进行攻击的方法。
/**
* 演示sql注入
* @throws SQLException
*/
@Test
publicvoidtestLogin_Inject() throwsSQLException {
//2.获取连接:如果是连接的本地mysql服务器并且端口是3306的可以简化
Stringurl="jdbc:mysql:///db1?useSSL=false";
Stringusername="root";
Stringpassword="1234";
Connectionconn=DriverManager.getConnection(url, username, password);
//接收用户输入 用户名和密码
Stringname="sdafdfa";
Stringpwd="'or '1'= '1";
Stringsql="select * from tb_user where username = '"+name+"' and password = '"+pwd+"'";
System.out.println(sql);
//获取stmt对象
Statementstmt=conn.createStatement();
//执行sql
ResultSetrs=stmt.executeQuery(sql);
//判断登录是否成功
if (rs.next()){
System.out.println("登入成功");
}else{
System.out.println("登入失败");
}
//7.释放资源
stmt.close();
rs.close();
conn.close();
}
- 解决sql注入问题
- 获取PreparedStatement 对象
//SQL语句中的参数值,使用?占位符替代
Stringsql="select * from user where usename = ? and password = ?";
//通过Connection 获取对象,并传入对应的sql语句
PreparedStatementpstmt=conn.prepareStatement(sql);
- 设置参数值
PreparedStatement对象:set数据类型(参数1,参数2):给?赋值
数据类型:例如setint(参数1,参数2)
参数:
参数1:?的位置编号,从1开始的(别记错了)
参数2:?的值
- 执行sql
executeUpdate();或者executeQuery();里面不需要在传递sql
- 来看
//接收用户输入用户名和密码
Stringname="zhangsan";
Stringpwd="'or'1' = '1";
//定义sql
Stringsql="select * from tb_user where username = ? and password = ?";
//获取pstmt对象
PreparedStatementpstmt=conn.prepareStatement(sql);
//设置?的值
pstmt.setString(1,name);
pstmt.setString(2,pwd);
//执行sql
ResultSetrs=pstmt.executeQuery();
//判断登入是否成功
if (rs.next()){
System.out.println("登录成功");
}else {
System.out.println("登录失败");
}
//7.释放资源
pstmt.close();
rs.close();
conn.close();
}
- 这里的
select*fromtb_userwhereusername='sdafdfa'andpassword=''or'1'='1'
- 会当成文本自动转义
select*fromtb_userwhereusername='sdafdfa'andpassword='\'or \'1'=\'1'
数据库连接池
数据库连接池简介
- 数据库连接池是个容器,负责分配,管理数据库连接
- 它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个
- 释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据库连接遗漏
- 好处:资源重用,提升系统响应速度,避免面数据库连接遗漏
数据库连接池实现
- 标准接口:DataSource
- 官方提供的数据库连接池标准接口,由第三方组织实现此接口
- 功能:获取连接
Connection getConnection();
- 常见的数据库连接池:
- DBCP
- C3P0
- Druid
- Druid(德鲁伊)
- Druid连接池是阿里巴巴开源的数据库连接池项目
- 功能强大,性能优秀,是java语言最好的数据库连接池之一
- Druid使用步骤
- 导入jar包druid-1.1.12.jar
- 定义配置文件
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql:///db1?useSSL=false&useServerPrestmts=true&characterEncoding=UTF-8
username=root
password=1234
#初始化连接数量
initialSize=5
#最大连接数
maxActive=10
#最大等待时间
maxWait=3000
- 加载配置文件
- 获取数据库连接池对象
- 获取连接
- 演示
packagecom.wang.druid;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
/**
* Druid数据库连接池演示
*/
public class DruidDemo1 {
public static void main(String[] args) throws Exception {
// 1. 导入jar包druid-1.1.12.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);
}
}
```
- 案例写写商品品牌数据的增删改查操作
sql语句
-- 删除tb_brand表
droptableifexists tb_brand;
-- 创建tb_brand表
createtable tb_brand
(
-- id主键
id intprimarykeyauto_increment,
-- 品牌名称
brand_name varchar(30),
-- 企业名称
company_name varchar(30),
-- 排序字段
ordered int,
-- 描述信息
description varchar(100),
-- 状态:0:禁用 1:启用
status int
);
-- 添加数据
insertinto tb_brand(brand_name,company_name,ordered,description,status) values('三只松鼠','三只松鼠股份有限公司',5,'好吃不上火',0),('华为','华为技术有限公司',100,'华为致力于把数字世界带入每个人,每个家庭',1),('小米','小米科技有限公司',50,'are you ok',1);
select * from tb_brand;
- Account类