JDBC简介
JDBC本质:
- 定义的一套关系型数据库的规则,即接口
- 数据库厂商实现这个接口,提供数据库驱动jar包
- 使用这套接口编程,真正执行代码是驱动jar包的实现类
JDBC的入门步骤:
1. 创建工程,导入驱动jar包。mysql.5.1.jar
2. 注册驱动
Class.forName("com.mysql.jdbc.Driver");
3. 获取连接
Connection conn = DriverManager.getConnection(url, username, password);
4. 定义SQL语句
String sql = " update ... ";
5. 获取执行SQL对象
Statement stmt = conn.createStatement();
6. 执行SQL
stmt.executeUpdate(sql);
7. 处理返回结果
8. 释放资源
操作步骤:
1. 创建工程
【New Project】--> 【Empty Project】--> 【Project SDK 1.8】
2. 工程配置:
3. 创建模块。
4. 创建项目。
com.itheima.jdbc.JDBCDome;
/**
JDBC快速入门
*/
public class JDBCDome {
public static void main(String[] args) throws Exception{
//1. 注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2. 获取连接
String url = "jdbc:mysql://127.0.0.1:3306/db1";
String username= "root";
String password= "root";
Connection conn = DriverManager.getConnection(url, username, password);
//3. 定义sql
String sql = "update account set money = 2000 where id = 1";
//4. 获取执行sql对象Statement
Statement stmt = conn.createStatement();
//5. 执行sql
int count = stmt.excuteUpdate(sql); //受影响的行数返回值
//6. 处理结果
System.out.println(count);
//7. 释放资源
stmt.close();
conn.close();
}
}
JDBC 的API详解
1. DriverManager
DriverManager(驱动管理类)作用:
- 注册驱动
- 获取数据库连接
注册驱动
Class.forName("com.mysql.jdbc.Driver");
查看Driver类源代码
提示:
- MySQL 5之后驱动包,可以省略注册驱动的步骤
- 自动加载jar包META-INF/services/java.sql.Driver文件的驱动类
获取连接
DriverManager.getConnection(url, username, password);
参数:
1. url: 连接路径
语法:jdbc:mysql://ip地址:端口号/数据库名称?参数键值对1&参数键值对2...
示例:jdbc:mysql://127.0.0.1:3306/db1
细节:
如果连接的本机mysql服务器,并且mysql服务器默认端口3306,则url可以简写成:jdbc:mysql:///数据库名称?参数键值对配置 userSSL = false 参数禁用安全连接方式,解决警告提示。
2. username: 用户名
3. password: 密码
2. Connection
Connection数据库连接对象的作用:
- 获取执行sql的对象
普通执行SQL对象
Statement createStatement()
预编译SQL的执行SQL对象:防止SQL注入
PreparedStatement prepareStatement(sql)
执行存储过程的对象
CallableStatement prepareCall(sql)
- 管理事务
- MySQL事务管理
开启事务:BEGIN/START TRANSACTION;
提交事务:COMMIT;
回滚事务:ROLLBACK;
MySQL默认自动提交事务。
- JDBC事务管理:Connection接口定义了3个对应方法
开启事务:setAutoCommit(boolean addCommit); true为自动提交事务,false为手动提交事务
提交事务:commit()
回滚事务: rollback()
/**
JDBC API详解 Connection
*/
public class JDBCDome3_Connection {
public static void main(String[] args) throws Exception{
//1. 注册驱动
//Class.forName("com.mysql.jdbc.Driver");
//2. 获取连接
String url = "jdbc:mysql://127.0.0.1:3306/db1?userSSL=false";
String username= "root";
String password= "root";
Connection conn = DriverManager.getConnection(url, username, password);
//3. 定义sql
String sql1 = "update account set money = 3000 where id = 1";
String sql2 = "update account set money = 3000 where id = 2";
//4. 获取执行sql对象Statement
Statement stmt = conn.createStatement();
/*快捷键ctrl+alt+T try...catch...*/
try {
//开启事务
conn.setAutoCommit(false);
//5. 执行sql
int count1 = stmt.excuteUpdate(sql1); //受影响的行数返回值
//6. 处理结果
System.out.println(count1);
//5. 执行sql
int count2 = stmt.excuteUpdate(sql2); //受影响的行数返回值
//6. 处理结果
System.out.println(count2);
int i = 3/0;
//提交事务
conn.commit();
} catch (Exception throwables) {
//回滚事务
conn.rollback();
throwables.printStackTrace();
}
//7. 释放资源
stmt.close();
conn.close();
}
}
3. Statement
Statement作用:执行SQL语句
int executeUpdate(sql): 执行DML, DDL语句
返回值:(1)DML语句影响的行数 (2)DDL语句执行后,执行成功后也可能返回0
ResultSet executeQuery(sql): 执行DQL语句
返回值:ResultSet结果集对象
/**
JDBC API详解 Statement
*/
public class JDBCDome4_Statement {
/*
执行DML语句
*/
@Test
public void testDML() throws Exception {
public static void main(String[] args) throws Exception{
//1. 注册驱动
//Class.forName("com.mysql.jdbc.Driver");
//2. 获取连接
String url = "jdbc:mysql://127.0.0.1:3306/db1?userSSL=false";
String username= "root";
String password= "root";
Connection conn = DriverManager.getConnection(url, username, password);
//3. 定义sql
String sql = "create database db2"; //执行DDL语句
String sql1 = "update account set money = 3000 where id = 1";
String sql2 = "update account set money = 3000 where id = 2";
//4. 获取执行sql对象Statement
Statement stmt = conn.createStatement();
/*快捷键ctrl+alt+T try...catch...*/
try {
//开启事务
conn.setAutoCommit(false);
//5. 执行sql
int count1 = stmt.excuteUpdate(sql1); //受影响的行数返回值
//6. 处理结果
System.out.println(count1);
//5. 执行sql
int count2 = stmt.excuteUpdate(sql2); //受影响的行数返回值,执行DDL语句返回值可能是0
//6. 处理结果
//System.out.println(count2);
if (count > 0) {
System.out.println("修改成功");
} else {
System.out.println("修改失败");
}
int i = 3/0;
//提交事务
conn.commit();
} catch (Exception throwables) {
//回滚事务
conn.rollback();
throwables.printStackTrace();
}
//7. 释放资源
stmt.close();
conn.close();
}
}
}
3. ResultSet
作用:封装了DQL查询语句的结果。
ResultSet stmt.executeQuery(sql); 执行DQL语句,返回ResultSet对象。
获取查询结果
boolean next(); (1) 将光标从当前位置向前移动一行 (2)判断当前行是否为有效行
返回值:true: 有效行,当前行数据 false: 无效行,当前行没有数据
xxx getXxx(参数):获取参数
xxx: 数据类型:如:int getInt(参数)
int: 列的编号,从1开始
string: 列的名称
使用步骤
1. 游标向下移动一行,并判断该行是否有数据:next()
2. 获取数据:getXxx(参数)
//循环判断游标游标是否是最后一行末尾
while (rs.next()) {
//获取数据
rs.getXxx(参数);
}
/**
JDBC API详解 ResultSet
*/
public class JDBCDome5_ResultSet {
/*
执行DML语句
*/
@Test
public void testDML() throws Exception {
public static void main(String[] args) throws Exception{
//1. 注册驱动
//Class.forName("com.mysql.jdbc.Driver");
//2. 获取连接
String url = "jdbc:mysql://127.0.0.1:3306/db1?userSSL=false";
String username= "root";
String password= "root";
Connection conn = DriverManager.getConnection(url, username, password);
//3. 定义sql
String sql = "select * from account";
//4. 获取Statement对象
Statement stmt = conn.createStatement();
//5. 执行sql
ResultSet rs = stmt.executeQuery(sql);
//6. 处理结果,遍历rs中所有数据
//6.1 光标向下移动一行,并且判断当前行是否为有效数据
while (rs.next()) {
//6.2 获取数据
int id = rs.getInt(columnIndex:1);
int name = rs.getString(columnIndex:2);
int money = rs.getDouble(columnIndex:3);
System.out.println(id);
System.out.println(name);
System.out.println(money);
}
//7. 释放资源
rs.close();
stmt.close();
conn.close();
}
}
案例分析自我练习
需求:查询account账户表数据,封装为Accout对象中,并且存储到ArrayList集合中。
1. 创建实体类
public class Account {
private int id;
private String name;
private double money;
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 getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
}
思路分析:
查询account账户表数据,封装为Account对象中,并且定义到ArrayList集合中
1. 定义实体类Account
2. 查询数据,封装到Account对象中
3. 将Account对象存入ArrayList集合中
//创建集合
List<Account> list = new ArrayList();
//6.1 光标向下移动一行,并且判断当前行是否为有效行
while (rs.next()) {
Account account = new Account();
//6.2 获取数据
int id = rs.getInt(columnLabel:"id");
int name= rs.getString(columnLabel:"name");
int money= rs.getDouble(columnLabel:"money");
//赋值
account.setId(id);
account.setName(name);
account.setMoney(money);
//存入集合
List.add(account);
}
4. PreparedStatement
作用:预编译SQL语句并执行:预防SQL注入问题
SQL注入:通过操作输入来修改事先定义好的SQL语句,用以达到执行代码对服务器进行攻击的方法。
SQL注入演示
需求:完成用户登录
select * from tb_user where username = 'zhangsan' and password = '123';
/**
用户登录案例
*/
public class JDBCDome6_UserLogin {
@Test
public void testLogin() throws Exception {
//2. 获取连接
String url = "jdbc:mysql://127.0.0.1:3306/db1?userSSL=false";
String username= "root";
String password= "root";
Connection conn = DriverManager.getConnection(url, username, password);
//接收用户名和密码
String name = "zhangsan";
String pwd = "123";
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("登录失败...");
}
//7. 释放资源
rs.close();
stmt.close();
conn.close();
}
/**
演示SQL注入问题
*/
@Test
public void testLogin_Inject() throws Exception {
//2. 获取连接
String url = "jdbc:mysql://127.0.0.1:3306/db1?userSSL=false";
String username= "root";
String password= "root";
Connection conn = DriverManager.getConnection(url, username, password);
//接收用户名和密码
String name = "hkehie";
String pwd = "' or ' 1 ' = '1'";
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("登录失败...");
}
//7. 释放资源
rs.close();
stmt.close();
conn.close();
}
解决SQL问题:
PreparedStatement作用:预编译SQL语句并执行
- 获取PreparedStatement对象
#SQL语句中参数值,使用 ? 占位符 替代
String sql = "select * from user where username = ? and password = ?";
#通过Connection对象获取,并传入对应sql语句
PreparedStatement pstmt = conn.preparedStatement(sql);
- 设置参数值
PreparedStatement 对象:setXxx(参数1,参数2):给 ?赋值
Xxx:数据类型:setInt(参数1,参数2)
参数:参数1:?的位置编号,从1开始
参数2:?的值
- 执行SQL
executeUpdate(); executeQuery(); 不需要传送sql
/**
API详解 : PreparedStatement
*/
public class JDBCDome7_UserLogin {
@Test
public void testPreparedStatement() throws Exception {
//2. 获取连接
String url = "jdbc:mysql://127.0.0.1:3306/db1?userSSL=false";
String username= "root";
String password= "root";
Connection conn = DriverManager.getConnection(url, username, password);
//接收用户名和密码
String name = "zhangsan";
String pwd = "123";
//定义sql
String sql = "select * from tb_user where username = ? and password = ?";
//获取pstmt对象
PreparedStatement pstmt = conn.createStatement(sql);
//设置pstmt值
pstmt.setString(name);
pstmt.setString(pwd);
//执行sql
ResultSet rs = pstmt.executeQuery();
//判断是否登录成功
if (rs.next()) {
System.out.println("登录成功...");
} else {
System.out.println("登录失败...");
}
//7. 释放资源
rs.close();
pstmt.close();
conn.close();
}
}
PreparedStatement原理
1. 好处:
- 预编译SQL,性能更高
- 防止SQL注入,将敏感字符进行转义
PreparedStatement预编译功能开启:useServerPrepStmts = true
配置MySQL执行日志(重启mysql服务后生效)
log-output=FILE
general-log=1
general_log_file="D:\mysql.log"
slow_query-log=1
slow_query_log_file="D:\mysql_slow.log"
long_query_time=2
PreparedSratement原理
- 在获取PreparedStatement对象时,将sql语句发送给mysql服务器进行检查,编译
- 执行时就不用在进行这些步骤,速度更快
- 如果sql模板一样,则只需进行一次检查、编译
/*
PreparedStatement原理
*/
@Test
public void testPreparedStatement2() throws Exception {
//2. 获取连接
String url = "jdbc:mysql://127.0.0.1:3306/db1?userSSL=false"&useServerPrepStmts = true;
String username= "root";
String password= "root";
Connection conn = DriverManager.getConnection(url, username, password);
//接收用户名和密码
String name = "zhangsan";
String pwd = "123";
//定义sql
String sql = "select * from tb_user where username = ? and password = ?";
//获取pstmt对象
PreparedStatement pstmt = conn.createStatement(sql);
//设置pstmt值
pstmt.setString(name);
pstmt.setString(pwd);
//执行sql
ResultSet rs = pstmt.executeQuery();
//判断是否登录成功
if (rs.next()) {
System.out.println("登录成功...");
} else {
System.out.println("登录失败...");
}
//7. 释放资源
rs.close();
pstmt.close();
conn.close();
}
数据库连接池
简介:
- 数据库连接池是个容器,负责分配,管理数据库连接(Connection);
- 它允许应用程序重复使用一个现有的数据库连接,而不是重新建立一个;
- 释放空余时间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据库连接遗漏。
好处:
- 资源重用
- 提升系统响应速度
- 避免数据库连接遗漏
数据库连接池实现
标准接口:DataSource
官方(SUN)提供的数据库连接池标准接口,由第三方组织实现此接口。
功能:获取连接
Connection getConnection()
常见的数据库连接池
- DBCP
- C3P0
- Druid
Druid(德鲁伊)
- Druid连接池是阿里开源数据库连接池项目
- 功能强大,性能优秀,是java语言最好的数据库连接池之一
Druid使用步骤
- 导入jar包 druid - 1.1.12.jar
- 定义配置文件
- 加载配置文件
- 获取数据库连接池对象
- 获取连接
/*
Druid数据库连接池演示
*/
public class DruidDemo {
public static void main(String[] args) throws Exception {
//1. 导入jar包
//2. 定义配置文件
//3. 加载配置文件
Properties prop = new Properties();
prop.load(new FileInputStream("jdbc-dome/src/druid.properties"));
//4. 获取连接池对象
DataSource dataSource = DruidDataSourceFactory.createDataSource(prop);
//5. 获取数据库连接Connection
Connection connection = dataSource();
//找properties路径
//System.out.println(System.getProperties("user.dir"));
}
}
自我练习:
需求:完成商品品牌数据的增删改查
查询:查询所有数据
添加:添加品牌
修改:根据id修改
删除:根据id删除
准备数据库表tb_brand
DROP TABLE IF EXISTS tb_brand;
-- 创建tb_brand表
CREATE TABLE tb_brand (
id INT PRIMARY KEY auto_increment, -- 主键
brand_name VARCHAR(20), -- 品牌名称
company_name VARCHAR(20), -- 企业名称
ordered INT, -- 排序字段
description VARCHAR(100), -- 描述信息
status INT -- 状态 0:禁用 1:启用
);-- 添加数据
INSERT INTO tb_brand (brand_name, company_name, ordered, description, status)
VALUES
('三只松鼠', '三只松鼠有限公司', 5, '好吃不上火', 0),
('华为', '华为技术有限公司', 100, '华为致力于把数字世界带入每个人,每个家庭,构建万物互联的智能世界', 1),
('小米', '小米技术有限公司', 50, 'are you ok', 1);SELECT * FROM tb_brand;
创建brand实体类
/*
品牌
快捷键:alt + 鼠标左键 整列编辑
在实体类中基本数据类型用对应的包装类型
*/
public class Brand {
// id主键
private Integer id ;
// 品牌名称
private String brandName;
// 企业名称
private String companyName;
// 排序字段
private int ordered;
// 描述信息
private String description;
// 状态 0:禁用 1:启用
private Integer status;
//快捷键 alt + insert生成封装
}
测试用例:
package com.itheima.example;
/**
品牌数据的增删改查测试
*/
public class BrandTest {
/*
查询所有:
1. SQL:select * from tb_brand;
2. 参数:不需要
3. 处理结果
*/
@Test
public void testSelectAll() throws Exception{
//1. 获取Connection
//3. 加载配置文件
Properties prop = new Properties();
prop.load(new FileInputStream("jdbc-dome/src/druid.properties"));
//4. 获取连接池对象
DataSource datasource = DruidDataSourceFactory.createDataSource(prop);
//5. 获取数据库连接 Connection
Connection conn = dataSource.getConnection();
//2. 定义SQL
String sql = "select * from tb_brand";
//3. 获取pstmt对象
PreparedStatement pstmt = conn.prepareStatement(sql);
//4. 设置参数
//5. 执行sql
ResultSet rs = pstmt.executeQuery();
//6. 处理结果
Brand brand = null;
List<Brand> brands = new ArrayList<>();
while (rs.next()) {
//获取数据
int id = rs.getInt(columnLabel: "id");
String brandName = rs.getString(columnLabel: "brand_name");
//封装Brand对象
brand = new Brand();
brand.setId(id);
brand.setBrandName(brandName);
//装载集合
brands.add(brand);
}
System.out.println(brands);
//7. 释放资源
rs.close();
pstmt.close();
conn.close();
}
}
添加需求:
1. 编写SQ语句:insert into brand(brand_name, company_name, ordered, description, status) values (?,?,?,?,?);
2. 是否需要参数?除id之外的参数
3. 返回结果如何封装?boolean
/*
添加数据:
1. SQL:insert into brand(brand_name, company_name, ordered, description, status) values (?,?,?,?,?);
2. 参数:除id之外的参数值
3. 处理结果
*/
@Test
public void testAdd() throws Exception{
// 接收页面提交的参数
String brandName = "香飘飘";
String companyName = "香飘飘";
int ordered = 1;
//1. 获取Connection
//3. 加载配置文件
Properties prop = new Properties();
prop.load(new FileInputStream("jdbc-dome/src/druid.properties"));
//4. 获取连接池对象
DataSource datasource = DruidDataSourceFactory.createDataSource(prop);
//5. 获取数据库连接 Connection
Connection conn = dataSource.getConnection();
//2. 定义SQL
String sql = "insert into brand(brand_name, company_name, ordered, description, status) values (?,?,?,?,?);";
//3. 获取pstmt对象
PreparedStatement pstmt = conn.prepareStatement(sql);
//4. 设置参数
pstmt.setString(parameterIndex 1,brandName);
pstmt.setString(parameterIndex 2,companyName);
pstmt.setInt(parameterIndex 3,ordered);
//5. 执行sql
int count = pstmt.executeUpdate();
//6. 处理结果
System.out.println(count > 0);
//7. 释放资源
rs.close();
pstmt.close();
conn.close();
}
根据id来修改操作
1. 编写SQL语句:
update tb_brand set brand_name=?, company_name=?, ordered=?, description=?, status=? where id = ?;
2. 是否需要参数?Brand对象所有数据
3. 返回结果如何封装?boolean
/*
修改数据:
1. SQL:update tb_brand set brand_name=?, company_name=?, ordered=?, description=?, status=? where id = ?;
2. 参数:除id之外的参数值
3. 处理结果 boolean
*/
@Test
public void testUpdate() throws Exception{
// 接收页面提交的参数
String brandName = "香飘飘";
String companyName = "香飘飘";
int ordered = 1;
int id = 4;
//1. 获取Connection
//3. 加载配置文件
Properties prop = new Properties();
prop.load(new FileInputStream("jdbc-dome/src/druid.properties"));
//4. 获取连接池对象
DataSource datasource = DruidDataSourceFactory.createDataSource(prop);
//5. 获取数据库连接 Connection
Connection conn = dataSource.getConnection();
//2. 定义SQL
String sql = "update tb_brand set brand_name=?, company_name=?, ordered=?, description=?, status=? where id = ?;";
//3. 获取pstmt对象
PreparedStatement pstmt = conn.prepareStatement(sql);
//4. 设置参数
pstmt.setString(parameterIndex 1,brandName);
pstmt.setString(parameterIndex 2,companyName);
pstmt.setInt(parameterIndex 3,ordered);
pstmt.setInt(parameterIndex 4,id;
//5. 执行sql
int count = pstmt.executeUpdate();
//6. 处理结果
System.out.println(count > 0);
//7. 释放资源
rs.close();
pstmt.close();
conn.close();
}
根据id来删除操作
1. 编写SQL语句
delete from tb_brand where id = ?;
2. 是否需要参数?id
3. 返回结果如何封装? boolean
/*
删除数据:
1. SQL:delete from tb_brand where id = ?;
2. 参数:id
3. 处理结果 boolean
*/
@Test
public void testDelete() throws Exception{
// 接收页面提交的参数
int id = 4;
//1. 获取Connection
//3. 加载配置文件
Properties prop = new Properties();
prop.load(new FileInputStream("jdbc-dome/src/druid.properties"));
//4. 获取连接池对象
DataSource datasource = DruidDataSourceFactory.createDataSource(prop);
//5. 获取数据库连接 Connection
Connection conn = dataSource.getConnection();
//2. 定义SQL
String sql = "delete from tb_brand where id = ?;";
//3. 获取pstmt对象
PreparedStatement pstmt = conn.prepareStatement(sql);
//4. 设置参数
pstmt.setInt(parameterIndex 1,id;
//5. 执行sql
int count = pstmt.executeUpdate();
//6. 处理结果
System.out.println(count > 0);
//7. 释放资源
rs.close();
pstmt.close();
conn.close();
}