第一节 JDBC概述
1、JDBC概念
JDBC就是使用java语言操作关系数据库的一套API
全称:(Java DataBase Connectivity)Java数据库连接
2、JDBC本质
官方(sun公司)定义的一套操作所有关系型数据库的规则,即接口
各个数据库厂商去实现这套接口,提供数据库驱动jar包,其实就是实现类
我们可以使用这套接口(JDBC)编程,真正执行的代码是驱动jar包中的实现类
3、JDBC好处
各数据厂商使用相同的接口,Java代码不需要针对不同数据库分别开发
可随时替换底层数据库,访问数据库的Java代码基本不变
第二节 JDBC快速入门
1、Java操作数据库的流程
2、编写代码步骤
A、创建工程,导入驱动jar包
B、注册驱动:Class.forName("com.mysql.jdbc.Driver");//把这个类加载进内存
C、获取连接:Connection conn = DriverManager.getConnection(url,username,password);//Java代码需要发送SQL给MySQL服务端,就需要先建立连接
D、定义SQL语句:String sql = "update.....";
E、获取执行SQL对象:statement stmt = conn.createStatement();//执行SQL语句需要SQL执行对象,而这个执行对象就是Statement对象
F、执行SQL:stmt.executeUpdate(sql);
G、处理返回结果
H、释放资源
示例
/** * JDBC快速入门 */ public class JDBCDemo { 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 = "1234"; 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.executeUpdate(sql);//受影响 的行数 //6. 处理结果 System.out.println(count); //7. 释放资源 stmt.close(); conn.close(); } }
第三节 JDBC-API详解
1、DriverManager(驱动管理类)
A、注册驱动
Class.forName("com.mysql.jdbc.Driver");
之前的入门案例,注册驱动是上面这么写的,但其实DriverManager类也提供了一个方法可以注册驱动:static void registerDriver(Driver driver)--使用DriverManager注册给定的驱动程序
B、获取数据库连接--参数说明
static Connection getConnection(String url,String user,String password)--尝试建立与给定数据库URL的连接
url:连接路径
user:用户名
password:密码
2、Connection(数据库连接对象)
A、Connection作用
获取执行SQL的对象
管理事务
B、获取执行对象
普通执行对象:Statement createStatement()
预编译SQL的执行SQL对象,防止SQL注入:PreparedStatement prepareStatement(sql)
执行存储过程的对象:CallableStatement prepareCall(sql)
C、事务管理
JDBC事务管理的方法:
void setAutoCommit(boolean autoCommit)--参与autoCommit表示是否自动提交事务,true表示自动提交事务,false表示手动提交事务。所以开启事务需将参数设置为false
void commit()--提交事务
void rollback()--回滚事务
/** * JDBC API 详解:Connection */ public class JDBCDemo3_Connection { public static void main(String[] args) throws Exception { //1. 注册驱动 //Class.forName("com.mysql.jdbc.Driver"); //2. 获取连接:如果连接的是本机mysql并且端口是默认 的 3306 可以简化书写 String url = "jdbc:mysql:///db1? useSSL=false"; String username = "root"; String password = "1234"; 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(); 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 (Exception e) { // ============回滚事务========== //程序在出现异常时会执行到这个地方,此时就需要回 滚事务 conn.rollback(); e.printStackTrace(); } //7. 释放资源 stmt.close(); conn.close(); } }
3、Statement对象:执行SQL语句
A、执行DDL、DML语句:int executeUpdate(String sql)
B、执行DQL语句:ResultSet executeQuery(String sql)
执行DML语句
/** * 执行DML语句 * @throws Exception */ @Test public void testDML() throws Exception { //1. 注册驱动 //Class.forName("com.mysql.jdbc.Driver"); //2. 获取连接:如果连接的是本机mysql并且端口是默认的 3306 可以简化书写 String url = "jdbc:mysql:///db1? useSSL=false"; String username = "root"; String password = "1234"; Connection conn = DriverManager.getConnection(url, username, password); //3. 定义sql String sql = "update account set money = 3000 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(); }
执行DQL语句
/** * 执行DDL语句 * @throws Exception */ @Test public void testDDL() throws Exception { //1. 注册驱动 //Class.forName("com.mysql.jdbc.Driver"); //2. 获取连接:如果连接的是本机mysql并且端口是默认的3306 可以简化书写 String url = "jdbc:mysql:///db1? useSSL=false"; String username = "root"; String password = "1234"; Connection conn = DriverManager.getConnection(url, username, password); //3. 定义sql 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); //7. 释放资源 stmt.close(); conn.close(); }
4、ResultSet
A、作用:封装了SQL查询语句的结果:ResultSet executeQuery(sql)--执行SQL语句,返回ResultSet对象
B、ResultSet对象提供的操作查询结果的方法:
C、实现代码
/** * 执行DQL * @throws Exception */ @Test public void testResultSet() throws Exception { //1. 注册驱动 //Class.forName("com.mysql.jdbc.Driver"); //2. 获取连接:如果连接的是本机mysql并且端口是默认的 3306 可以简化书写 String url = "jdbc:mysql:///db1?useSSL=false"; String username = "root"; String password = "1234"; 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 获取数据 getXxx() int id = rs.getInt(1); String name = rs.getString(2); double money = rs.getDouble(3); System.out.println(id); System.out.println(name); System.out.println(money); System.out.println("--------------"); }*/ // 6.1 光标向下移动一行,并且判断当前行是否有数据 while (rs.next()){ //6.2 获取数据 getXxx() int id = rs.getInt("id"); String name = rs.getString("name"); double money = rs.getDouble("money"); System.out.println(id); System.out.println(name); System.out.println(money); System.out.println("--------------"); } //7. 释放资源 rs.close(); stmt.close(); conn.close(); }
5、PreparedStatement
A、PreparedStatement作用:
预编译SQL语句并执行:预防SQL注入问题
B、SQL注入
SQL注入是通过操作输入来修改事先定义好的SQL语句,用以达到执行代码对服务器进行攻击的方法
C、代码模拟SQL注入问题
@Test public void testLogin() throws Exception { //2. 获取连接:如果连接的是本机mysql并且端口是默认的 3306 可以简化书写 String url = "jdbc:mysql:///db1?useSSL=false"; String username = "root"; String password = "1234"; Connection conn = DriverManager.getConnection(url, username, password); // 接收用户输入 用户名和密码 String name = "sjdljfld"; String pwd = "' or '1' = '1"; 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(); }
D、获取PreparedStatement对象
// SQL语句中的参数值,使用?占位符替代 String sql = "select * from user where username = ? and password = ?"; // 通过Connection对象获取,并传入对应的sql语句 PreparedStatement pstmt = conn.prepareStatement(sql);
E、设置参数值
F、执行SQL语句
G、使用PreparedStatement改进
@Test public void testPreparedStatement() throws Exception { //2. 获取连接:如果连接的是本机mysql并且端口是默认的 3306 可以简化书写 String url = "jdbc:mysql:///db1?useSSL=false"; String username = "root"; String password = "1234"; Connection conn = DriverManager.getConnection(url, username, password); // 接收用户输入 用户名和密码 String name = "zhangsan"; String pwd = "' or '1' = '1"; // 定义sql 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("登录失败~"); } //7. 释放资源 rs.close(); pstmt.close(); conn.close(); }
执行上面语句就可以发现不会出现SQL注入漏洞问题了。那么PreparedStatement又是如何解决的呢?它是将特殊字符进行了转义,转义的SQL如下
H、PreparedStatement原理
上图解释:
将sql语句发送到MySQL服务器端
MySQL服务端会对sql语句进行如下操作
检查SQL语句,检查SQL语句的语法是否正确
编译SQL语句。将SQL语句编译成可执行的函数。检查SQL和编译SQL花费的时间比执行SQL的时间还要长。如果我们只是重新设置参数,那么检查SQL语句和编译SQL语句将不需要重复执行,这样就提高了性能。
执行SQL语句
I、可通过查询日志看一下原理
测试代码如下:
/** * PreparedStatement原理 * @throws Exception */ @Test public void testPreparedStatement2() throws Exception { //2. 获取连接:如果连接的是本机mysql并且端口是默认的 3306 可以简化书写 // useServerPrepStmts=true 参数开启预编译功能 String url = "jdbc:mysql:///db1? useSSL=false&useServerPrepStmts=true"; String username = "root"; String password = "1234"; Connection conn = DriverManager.getConnection(url, username, password); // 接收用户输入 用户名和密码 String name = "zhangsan"; String pwd = "' or '1' = '1"; // 定义sql String sql = "select * from tb_user where username = ? and password = ?"; // 获取pstmt对象 PreparedStatement pstmt = conn.prepareStatement(sql); Thread.sleep(10000); // 设置?的值 pstmt.setString(1,name); pstmt.setString(2,pwd); ResultSet rs = null; // 执行sql rs = pstmt.executeQuery(); // 设置?的值 pstmt.setString(1,"aaa"); pstmt.setString(2,"bbb"); // 执行sql rs = pstmt.executeQuery(); // 判断登录是否成功 if(rs.next()){ System.out.println("登录成功~"); }else{ System.out.println("登录失败~"); } //7. 释放资源 rs.close(); pstmt.close(); conn.close(); }
第四节 数据库连接池
1、数据库连接池简介
数据库连接池是个容器,负责分配、管理数据库连接(Connection)
它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个
释放空闲时间长超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据库连接遗漏
好处:1.资源重用 2.提升系统响应速度 3.避免数据库连接遗漏
2、数据库连接池实现
3、常见的数据库连接池
DBCP
C3P0
Druid--德鲁伊(目前使用更多,性能比上面两个要好)
Druid连接池是阿里巴巴开源的数据库连接池项目
功能强大,性能优秀,是Java语言最好的数据库连接池之一
4、Druid使用
导入jar包 druid-1.1.12.jar
定义配置文件
加载配置文件
获取数据库连接池对象
获取连接
配置文件如下
driverClassName=com.mysql.jdbc.Driver url=jdbc:mysql:///db1? useSSL=false&useServerPrepStmts=true username=root password=1234 # 初始化连接数量 initialSize=5 # 最大连接数 maxActive=10 # 最大等待时间 maxWait=3000
/**标签:DriverManager,JDBC,String,--,sql,mysql,SQL,password,conn From: https://www.cnblogs.com/Flower--Dance/p/16723282.html
* Druid数据库连接池演示
*/
public class DruidDemo { public static void main(String[] args) throws Exception { //1.导入jar包 //2.定义配置文件 //3. 加载配置文件 Properties prop = new Properties(); prop.load(new FileInputStream("jdbcdemo/src/druid.properties")); //4. 获取连接池对象 DataSource dataSource = DruidDataSourceFactory.createDataSource(prop); //5. 获取数据库连接 Connection Connection connection = dataSource.getConnection(); System.out.println(connection); //获取到了连接 后就可以继续做其他操作了 //System.out.println(System.getProperty("user.dir") ); } }