JDBC步骤用的类和接口的简介
DriverManager类简介:
概述:
它是java.sql包下的类, 表示 驱动管理者.
作用:
1. 注册驱动. 实际开发不用这种方式.
2. 获取连接对象. 掌握, 常用.
涉及到的成员方法:
public static void registerDriver(Driver driver);
注册驱动, 但是这种方式会导致驱动注册两次, 所以实际开发中我们不用它, 而是通过反射的方式注册驱动.
参数解释: Driver 表示具体的要连接的数据库的 驱动类.
源码解读:
MySQL厂商提供的驱动类 Sun公司的JDBC规范之 驱动规范
public class com.mysql.jdbc.Driver implements java.sql.Driver {
}
public static Connection getConnection(String url, String username, String password); 获取连接对象.
参数1: 数据库连接字符串.
格式: 协议:子协议://要连接的数据库的ip或者主机名:端口号/具体的要操作的数据库
例如:
jdbc:mysql://127.0.0.1:3306/day16
jdbc:mysql://localhost:3306/day16
细节:
如果操作的是本机数据库, 且端口号也是默认的3306: 则可以省略ip地址 和 端口号.
--------------------------------------------------------------------------
Connection接口详解;
概述:
它是java.sql包下的接口, 表示: 连接对象.
作用:
1. 获取可以执行SQL语句的对象.
2. 可以管理事务.
涉及到的成员方法:
public Statement createStatement();
获取可以执行SQL语句的对象, 没有预编译功能, 不能解决SQL注入攻击问题.
public PreparedStatement prepareStatement(String sql);
获取可以执行SQL语句的对象, 有预编译功能, 能解决SQL注入攻击问题.
和事务相关的方法:
public void setTransactionIsolation(int level); 设置事务的隔离级别
public void commit(); 提交事务
public void rollback(); 事务回滚
public void setAutoCommit(boolean flag) 设置是否开启自动提交事务功能.
-----------------------------------------------------------------------------
Statement接口详解;
概述:
它是java.sql包下的接口, 表示: 可以执行SQL语句的对象.
作用:
1. 执行SQL语句, 获取结果集.
2. 可以进行批处理.
涉及到的成员方法:
public ResultSet executeQuery(sql); 执行查询语句, 获取结果集.
public int executeUpdate(sql); 执行更新语句, 获取结果集(注意: 增删改语句统称为更新语句)
和批处理相关的方法: 了解, 批处理只针对于更新语句有效.
public void addBatch(String sql); 添加SQL语句到批处理队列中.
public int[] executeBatch(); 执行批处理.
public void clearBatch(); 清空批处理.
---------------------------------------------------------------------
ResultSet接口详解;
概述:
它是java.sql包下的接口, 表示: 执行完查询语句后返回的结果集对象.
涉及到的成员方法:
public boolean next(); 判断结果集中是否有下一个条数据, 类似于: Iterator#hasNext()
public Xxx getXxx(int columnNumber);
根据列的编号, 获取该列的值, 编号从1开始, Xxx表示数据类型, 例如: getInt(), getString()
public Xxx getXxx(String columnName); 推荐掌握.
根据列的名称, 获取该列的值.
/*
案例: 通过单元测试演示JDBC的CURD操作.
JDBC的核心操作步骤:
1. 注册驱动.
2. 获取连接对象.
3. 根据连接对象, 获取可以执行SQL语句的对象.
4. 执行SQL语句, 获取结果集.
5. 操作结果集.
6. 释放资源.
*/
public class JDBCDemo03 {
/**
* 查询所有数据
* @throws Exception
*/
@Test
public void select() throws Exception {
//1.注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2.获取数据库连接对象
Connection conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1/afu01", "root", "root");
//3.根据连接对象, 获取可以执行SQL语句的对象.
Statement stat = conn.createStatement();
//4执行sql语句获取结果集
String sql = "select * from user;";
//5.操作结果集
ResultSet rs = stat.executeQuery(sql); //executeQuery 只针对于查询操作
while (rs.next()){
System.out.println(rs.getInt("id") + ".." + rs.getString("username")+ "..." + rs.getString("password"));
}
//6.释放资源
rs.close();
stat.close();
conn.close();
}
/**
* 添加数据
* @throws Exception
*/
@Test
public void add() throws Exception {
//1.注册数据库驱动
Class.forName("com.mysql.jdbc.Driver");
//2.获取数据库连接对象
Connection conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1/afu01", "root", "root");
//3.根据连接对象,获取乐意操作的sql语句
Statement stat = conn.createStatement();
String sql = "insert into user values (null,'胡嘻嘻','123456');"; //字段包裹单引号
//4.执行sql语句 获取结果集
int num = stat.executeUpdate(sql);
//5.操作结果集
System.out.println(num > 0 ? "增加成功" : "增加失败");
//6.释放资源
stat.close();
conn.close();
}
/**
* 修改数据
* @throws Exception
*/
@Test
public void update() throws Exception{
//1.注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2.创建数据库连接对象
Connection conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1/afu01", "root", "root");
//3.获取可以执行的sql语句
Statement stat = conn.createStatement();
String sql = "update user set username = '迪丽热巴',password = '88888888' where id = 2;";
//4.执行sql语句 获取结果集
int num = stat.executeUpdate(sql);
//5. 打印输出
System.out.println(num > 0 ? "修改成功": "修改失败" );
//6. 释放资源
stat.close();
conn.close();
}
/**
* 删除数据
*/
@Test
public void delete() throws Exception{
//1.注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2.获取数据库连接
Connection conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1/afu01", "root", "root");
//3.获取执行sql语句对象
Statement stat = conn.createStatement();
String sql = "delete from user where id = 2;";
//4.操作sql语句
int num = stat.executeUpdate(sql);
//5.输出
System.out.println(num > 0 ? "删除成功": "删除失败");
//6.释放资源
stat.close();
conn.close();
}
}
JDBC 工具类的使用
//自定义JDBCUtils工具类
public class JDBCUtils {
//构造方法私有化
private JDBCUtils() {
}
//工具类的成员都是静态的 //静态代码块在访问该类的时候回优先执行 随着类的加载而存在
static {
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
//对外提供成员方法, 用来获取连接对象
public static Connection getConnection() {
//具体的 返回操作连接对象
try {
return DriverManager.getConnection("jdbc:mysql://127.0.0.1/afu01", "root", "root");
} catch (SQLException e) {
e.printStackTrace();
}
//走到这里说明有问题
return null;
}
//释放资源
//复杂版
public static void release(Connection conn, Statement stat, ResultSet rs) {
try {
if (rs != null) {
rs.close();
rs = null; //做一个非空判断 GC会优先回收Null对象 这个代码值500块 哈哈哈........
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if (stat != null) {
stat.close();
stat = null; //GC优先回收null
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if (conn != null) {
conn.close();
conn = null;
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
//简单版
/* public static void release(Connection conn, Statement stat, ResultSet rs) {
try {
rs.close();
stat.close();
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}*/
}
public class TestJDBCUtils {
public static void main(String[] args) throws Exception{
//需求: 抽取JDBCUtils工具类, 并完成JDBC的核心操作步骤.
//1. 注册驱动.
//2. 获取连接对象.
Connection conn = JDBCUtils.getConnection();
//3. 根据连接对象, 获取可以执行SQL语句的对象
Statement stat = conn.createStatement();
String sql = "select * from user;";
//操作sql语句
ResultSet rs = stat.executeQuery(sql);
while (rs.next()){
//获取ID
/* rs.getInt("ID");
//获取用户名
rs.getString("username");
//获取密码
rs.getString("password");*/
//输出
System.out.println( rs.getInt("ID") + "..." + rs.getString("username") + rs.getString("password"));
}
//释放资源
JDBCUtils.release(conn,stat,rs);
}
}
关于Sql的注入攻击问题
public class JDBCMysql {
public static void main(String[] args) throws Exception{
// method01();
//需求2: 通过Java代码 解决SQL注入攻击
//1. 提示用户录入账号和密码, 并接收.
System.out.println("请输入账号:");
String username = new Scanner(System.in).nextLine();
System.out.println("请输入密码:");
String password = new Scanner(System.in).nextLine();
//2.获取数据库连接对象
Connection conn = JDBCUtils.getConnection();
//3.获取执行sql语句的对象
String sql = "select * from user where username = ? and password = ?;"; //需要用户录入内容的地方, 都用占位符?表示.
PreparedStatement ps = conn.prepareStatement(sql); //这里已经对SQL格式预编译了, 即: 之后不管传入什么内容, 都只是当做普通字符处理.
//细节 给占位符设置值
ps.setString(1,username);
ps.setString(2,password);
//4.执行sql 获取结果集.
ResultSet rs =ps.executeQuery(); //因为已经预编译过SQL语句了, 所以这里不需要传入.
System.out.println(rs.next()? "登录成功" : "登录失败");
//5.释放资源
JDBCUtils.release(conn,ps,rs);
//PreparedStatement 继承了 Statement
}
public static void method01() throws SQLException {
//演示sql注入问题
//1. 提示用户录入账号和密码, 并接收.
System.out.println("请输入账号信息:");
String username = new Scanner(System.in).nextLine();
System.out.println("请输入密码信息:");
String password = new Scanner(System.in).nextLine();
//2. 通过JDBC代码, 判断是否登录成功, 并提示.
//1. 注册驱动.
//2. 获取连接对象.
Connection conn = JDBCUtils.getConnection();
//3. 根据连接对象, 获取可以执行SQL语句的对象.
Statement stat = conn.createStatement();
//4.执行sql的语句
String sql = "select * from user where username = '"+ username + " 'and password= ' " + password+";'";
//5.操作sql语句
ResultSet rs = stat.executeQuery(sql);
System.out.println(rs.next()? "登录成功" : " 登录失败");
//6.释放资源
JDBCUtils.release(conn,stat,rs);
}
}
演示PreparedStatement接口的 CURD操作
/*
案例: 演示PreparedStatement接口的 CURD操作. 掌握 JDBC工具类操作 实际开发用的比较多 因为防止可sql注入攻击问题
*/
public class PrepareStatmentCRUD {
/**
* 查询
* @throws Exception
*/
@Test
public void select() throws Exception{
//1.获取数据库连接对象
Connection conn = JDBCUtils.getConnection();
//2.获取可以执行的sql语句对象
String sql = "select * from user where id = ?;";
PreparedStatement ps = conn.prepareStatement(sql);
//3. 执行SQL语句, 获取结果集.
//细节: 给占位符填充值.
ps.setInt(1,3); //表示第一个 ? 给ID为 3 的人
ResultSet rs = ps.executeQuery(); //查询用executeQuery()
; //4.操作结果集
while (rs.next()){
System.out.println(rs.getInt("id") + ".." + rs.getString("username") + ".." + rs.getString("password"));
}
//5.释放资源
JDBCUtils.release(conn,ps,rs);
}
/**
* 增加
* @throws Exception
*/
@Test
public void add() throws Exception{
//1.获取数据库连接对象
Connection conn = JDBCUtils.getConnection();
//2.获取可以执行sql语句的对象
String sql = "insert into user values(null,?,?);"; //这一步已经确定了SQL语句的格式, 之后不管传入什么, 都当做普通字符处理.
PreparedStatement ps = conn.prepareStatement(sql);
//3.操作结果集的sql语句
ps.setString(1,"张学友");
ps.setString(2,"123456");
int num = ps.executeUpdate();
//4. 输出
System.out.println( num>0? "操作成功":"操作失败");
//5.释放资源
JDBCUtils.release(conn,ps,null);
}
/**
* 修改
*/
@Test
public void update() throws Exception{
//1.获取数据库连接对象
Connection conn = JDBCUtils.getConnection();
//2.创建执行操作sql语句的对象
String sql = "update user set username = ? where id = ?";
PreparedStatement ps = conn.prepareStatement(sql);
//3.获取操作的结果集
//细节 给占位符设置值
ps.setString(1,"阿福哥哥啊");
ps.setInt(2,1);
int num = ps.executeUpdate();
//4.输出
System.out.println(num > 0? "操作成功":"操作失败");
//释放资源
JDBCUtils.release(conn,ps,null);
}
/**
* 删除
*/
@Test
public void delete() throws Exception{
//1.获取连接对象
Connection conn = JDBCUtils.getConnection();
//2.创建执行sql语句的对象
String sql = "delete from user where id = ?";
PreparedStatement ps = conn.prepareStatement(sql);
//3.操作执行sql
//细节 给 ? 赋值
ps.setInt(1,1);
int num = ps.executeUpdate();
//4.输出
System.out.println(num > 0?"操作成功":"操作失败");
//5.释放资源
JDBCUtils.release(conn,ps,null);
}
}
数据库连接池
/*
案例: 演示数据库连接池入门.
数据库连接池简介:
概述:
实际开发中, 当我们需要使用大量生命周期短的连接对象时, 频繁的创建和销毁连接对象是非常消耗资源的, 针对于这种情况,
我们可以创建一个池子出来, 里边放一些连接对象, 用的时候从里边拿, 用完之后再放回去. 这样做的好处是: 节约资源, 提高效率.
这个池子就叫: 数据库连接池. Data Base Connection Pool
分类:
DBCP: 属于Apache软件基金组织的, 不能自动回收空闲连接.
C3P0: 属于Apache软件基金组织的, 能自动回收空闲连接.
Java的框架中, 底层但凡是涉及到数据库连接池部分了, 用的都是 C3P0.
Druid: 属于阿里巴巴的产品, 采用分布式事务的方式实现的, 能够更好的兼容大数据框架.
步骤:
1. 导入连接池的jar包.
2. 从连接池中获取连接对象.
3. 正常的JDBC的操作.
4. 正常的释放资源即可.
因为如果Connection对象是我们自己创建的, 当调用 conn.close()的时候, 就是销毁资源.
如果Connection对象是我们从数据库连接池中获取的, 当调用 conn.close()的时候, 就是 归还连接对象到连接池中.
为什么同样的代码, 效果却不同呢?
因为数据库连接池的底层已经 重构了Connection#close()方法.
遇到的问题:
我们这样写代码, 效率还没有以前不用数据库连接池的代码效率高.
产生原因:
1. 此时的数据库连接池并不是共享的, 而是每个代码都有自己的数据库连接池对象.
2. 此时我们创建的数据库连接池对象, 默认的连接对象的个数是5个, 但是我们就用了1个, 其他4个处于闲置状态.
解决方案:
抽取C3P0Utils工具类, 设置 数据库连接池对象 为大家共享的.
*/
//自定义的C3P0Utils工具类, 用来操作 C3P0 数据库连接池的.
public class C3P0Utils {
//1. 构造方法私有化.
private C3P0Utils() {
}
//2. 定义个公共的静态的常量, 表示数据库连接池对象.
public static final ComboPooledDataSource cpds = new ComboPooledDataSource();
//3. 对外提供一个公共的成员方法, 用来获取: 数据库连接池对象.
public static ComboPooledDataSource getDataSource() {
return cpds;
}
//4. 对外提供一个公共的成员方法, 用来从 连接池中 获取连接对象.
public static Connection getConnection() {
try {
return cpds.getConnection(); //从 连接池中 获取连接对象, 并返回.
} catch (SQLException throwables) {
throwables.printStackTrace();
}
//如果有问题, 则返回null
return null;
}
//5. 释放资源.
public static void release(Connection conn, Statement stat, ResultSet rs) {
try {
if (rs != null) {
rs.close();
rs = null;
}
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
try {
if (stat != null) {
stat.close();
stat = null;
}
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
try {
if (conn != null) {
conn.close();
conn = null;
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
}
/*
案例: 演示 C3P0Utils工具类的定义和使用.
*/
public class CP30Test {
public static void main(String[] args) throws Exception{
//1.从池子中获取连接对象
Connection conn = C3P0Utils.getConnection();
//2. 根据连接对象, 获取可以执行SQL语句的对象.
String sql = "select * from user;";
PreparedStatement ps = conn.prepareStatement(sql);
//3. 执行SQL语句, 获取结果集.
//细节: 给占位符填充值.
ResultSet rs = ps.executeQuery();
while (rs.next()){
System.out.println(rs.getInt("id") + ".." + rs.getString("username") + ".." +rs.getString("password"));
}
//4.释放资源
C3P0Utils.release(conn,ps,rs);
}
}
mysql事物
事务简介:
概述:
专业版: 事务指的是逻辑上的一组操作, 组成该逻辑操作的各个逻辑单元, 要么全部执行成功, 要么全部执行失败.
大白话版: 同生同死.
案例:
逻辑操作: 阿福 给 凤姐 转1000
逻辑单元:
动作1: 阿福的账号 - 1000
动作2: 凤姐的账号 + 1000
特点: ACID
原子性(Atomicity) 原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
一致性(Consistency)事务前后数据的完整性必须保持一致。
隔离性(Isolation) 事务的隔离性是指多个用户并发访问数据库时,一个用户的事务不能被其它用户的事务所干扰,多个并发事务之间数据要相互隔离。
持久性(Durability) 持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响。
如果不考虑事务的隔离性, 可能会出现问题:
关于读:
脏读: 也叫: 读-未提交
指的是一个事务读取到了另一个事务还没有来得及提交的数据, 导致多次查询结果不一致.
不可重复读: 也叫: 读-已提交(更新)
指的是一个事务读取到了另一个事务已经提交的(更新, 修改)数据, 导致多次查询结果不一致.
虚读(幻读): 也叫: 读-已提交(插入)
指的是一个事务读取到了另一个事务已经提交的(插入)数据, 导致多次查询结果不一致.
//事物只针对更新语句有效
关于写:
丢失更新(了解).
其实所谓的不考虑隔离性, 就是不考虑事务的隔离级别, 那么事务的隔离级别有哪些呢? 具体如下:
隔离级别从小到大分别是:
read uncommitted < read committed < repeatable read < serializable
隔离级别安全从低到高分别是:
read uncommitted < read committed < repeatable read < serializable
隔离级别效率从高到第分别是:
read uncommitted > read committed > repeatable read > serializable
总结:
1. 四大隔离级别可能发生以及已经解决的问题.
read uncommitted:
会发生: 3, 脏读, 不可重复读, 虚读.
已解决: 0
read committed:
会发生: 2, 不可重复读, 虚读.
已解决: 1, 脏读.
repeatable read:
会发生: 1, 虚读.
已解决: 2, 脏读, 不可重复读
serializable:
会发生: 0
已解决: 3, 脏读, 不可重复读, 虚读.
2. MySQL数据库默认的隔离级别是: repeatable read Oracle数据库默认的隔离级别是:read committed
3. MySQL数据库默认开启了事务的自动提交功能, 每一个SQL语句都是一个事务.
4. Oracle数据库默认没有开启事务的自动提交功能, 需要我们手动开启.
5. 和事务相关的SQL语句.
select @@tx_isolation; 查看事务的隔离级别
show variables like '%commit%'; 查看是否开启事务的自动提交功能
start transaction; 或者 begin; 开启事务
commit; 提交事务
rollback; 事务回滚, 即: 把数据还原到事务还没有执行之前的状态.
set session transaction isolation level 隔离级别; 临时的设置事务的隔离级别为指定的级别.
案例 模拟转账演示事物
/*
案例: 模拟转账, 来演示事务, 掌握
涉及到的成员方法: Connection接口中的成员方法.
概述:
它是java.sql包下的接口, 表示: 连接对象.
作用:
1. 获取可以执行SQL语句的对象.
2. 可以管理事务.
涉及到的成员方法:
public Statement createStatement();
获取可以执行SQL语句的对象, 没有预编译功能, 不能解决SQL注入攻击问题.
public PreparedStatement prepareStatement(String sql);
获取可以执行SQL语句的对象, 有预编译功能, 能解决SQL注入攻击问题.
和事务相关的方法:
public void setTransactionIsolation(int level); 设置事务的隔离级别
public void commit(); 提交事务
public void rollback(); 事务回滚
public void setAutoCommit(boolean flag) 设置是否开启自动提交事务功能.
*/
public class Demo02 {
public static void main(String[] args) {
Connection conn = null;
Statement stat = null;
try {
//1.获取数据库连接对象
conn = C3P0Utils.getConnection();
//2.执行sql语句的对象
stat = conn.createStatement();
//3.执行sql语句
String sql2 = "update account set money = money - 100 where id = 1;";
String sql1 = "update account set money = money + 100 where id = 2;";
//开启事务, 关闭事务的自动提交功能, 相当于开启了我们自己的事务, 只要不提交或者回滚, 这下述的SQL语句都是同一个事务的.
conn.setAutoCommit(false);
//4. 操作sql语句
int rs1 = stat.executeUpdate(sql1);
// System.out.println(1 / 0);
int rs2 = stat.executeUpdate(sql2);
//5.操作结果集
if (rs1 == 1 && rs2 == 1) {
//转账成功
conn.commit(); //提交事务
System.out.println("转账成功");
}
} catch (SQLException e) {
try {
//事物回滚
conn.rollback();
System.out.println("转账失败");
} catch (SQLException ex) {
ex.printStackTrace();
}
} finally {
//6.释放资源
C3P0Utils.release(conn, stat, null);
}
}
}