一、JDBC的概述
1.JDBC为访问不同的数据薛是供了统一的接口,为使用者屏蔽了细节问题。
2. Java程序员使用JDBC,可以连接任何提供了JDBC驱动程序的数据库系统,从而完成对数据库的各种操作。
二。JDBC带来的好处
java程序可以直接对数据库进行调用,但是没有很好的移植性(对用于不同的数据库),所以不推荐
JDBC带来的好处:JDBC是java提供一套用于数据库操作的接口API,Java程序员只需要面向这套接口编程即可。不同的数据库厂商,需要针对这套接口,提供不同实现。
JDBC API:JDBC API 是一系列的接口,它统一和规范了应用程序与数据库的连接,执行SQL语句,并得到返回结果等各类操作;
JDBC程序编写步骤
1、注册驱动 --- 加载Driver类
2、获取连接 --- 得到Connection
3、执行增删改查 --- 发送Sql给mysql执行
4、释放资源 --- 关闭相关连接
获取数据库连接的5种方式:
方式1:使用(com.mysql.jdbc.Driver)com.mysql.cj.jdbc.Driver,属于静态加载,灵活性差,依赖性强
方式2:使用反射机制,属于静态加载
方式3:使用DriverManager替代driver进行统一管理
方式4:使用Class.forName自动完成注册驱动,简化代码
这种方式连接是使用得最多的(推荐使用)
在底层会完成注册:
提示:
(1)mysql驱动5.1.6可以无需Class.forName(“com.mysql.jdbc.Driver”);
(2)从jdk1.5以后使用了jdbc4,不再需要显示调用class.forName()注册驱动而是自动调用驱动jar包下META-INF\servicesljava .sql.Driver文本中的类名称去注册
(3)建议加上Class.forName(“com.mysql.jdbc.driver”),能够更加明确的感受到程序流程,利于掌握;
方式5:利用properties文件配置数据库需要的相关的配置
更加的灵活,可以直接通过properties配置文件修改数据库连接相关的数据
对方式5的实例:
//properties配置文件 driver=com.mysql.cj.jdbc.Driver url=jdbc:mysql://localhost:3306/test_db?useSSL=false&characterEncoding=utf8&serverTimezone=UTC user=root password=123456 //连接数据库的相关操作 import java.io.*; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Statement; import java.util.Properties; public class jdbc02 { public static void main(String[] args) throws ClassNotFoundException, IOException, SQLException { //创建一个properties对象 Properties properties = new Properties(); //加载和获取创建的properties的相关数据 properties.load(new FileInputStream("src\\db.properties")); String driver = properties.getProperty("driver"); String url = properties.getProperty("url"); String user = properties.getProperty("user"); String password = properties.getProperty("password"); String sql = "insert into actor values (null,'王五','男','1975-11-23','110')"; //driver驱动,也可以不加,但是加上更容易读懂流程 Class.forName(driver); //连接和执行操作 Connection connection = DriverManager.getConnection(url, user, password); Statement statement = connection.createStatement(); statement.executeUpdate(sql); //关闭连接 System.out.println("连接"+connection); statement.close(); connection.close(); } }
三、RestSet【结果集】
基本介绍:
1.表示数据库结果集的数据表,通常通过执行查询数据库的语句生成
2. ResultSet对象保持一个光标指向其当前的数据行。最初,光标位于第一行之前
3. next方法将光标移动到下一行,并且由于在ResultSet对象中没有更多行时返回
false,因此可以在while循环中使用循环来遍历结果集
简单测试:
四、Statement:
基本介绍:
1.Statement对象用于执行静态SQL语句并返回其生成的结果的对象
2.在连接建立后,需要对数据库进行访问,执行命名或是SQL语句,可以通过
Statement【存在SQL注入】
PreparedStatement【预处理】
CallableStatement【存储过程】
3. Statement对象执行SQL语句,存在SQL注入风险
4.SQL注入是利用某些系统没有对用户输入的数据进行充分的检查,而在用户输入数据中注入非法的SQL语句段或命令,恶意攻击数据库。
5.要防范SQL注入,只要用 PreparedStatement(从Statement扩展而来)取
代 Statement就可以了
import java.io.*; import java.sql.*; import java.util.Properties; public class jdbc02 { public static void main(String[] args) throws ClassNotFoundException, IOException, SQLException { //创建一个properties对象 Properties properties = new Properties(); //加载和获取创建的properties的相关数据 properties.load(new FileInputStream("src\\db.properties")); String driver = properties.getProperty("driver"); String url = properties.getProperty("url"); String user = properties.getProperty("user"); String password = properties.getProperty("password"); String sql = "insert into actor values (null,'三毛','男','1975-11-23','110')"; String sql1 = "select id,name from actor"; //driver驱动,也可以不加,但是加上更容易读懂流程 Class.forName(driver); //连接和执行操作 Connection connection = DriverManager.getConnection(url, user, password); Statement statement = connection.createStatement(); // statement.executeUpdate(sql); // String sql1 = "select id,name from actor"; ResultSet resultSet = statement.executeQuery(sql1); while (resultSet.next()) { int i = resultSet.getInt(1); String string1 = resultSet.getString(2); System.out.println(i+"=="+string1); } //关闭连接 resultSet.close(); System.out.println("连接"+connection); statement.close(); connection.close(); } }
PrepareStatement:
1. PreparedStatement 执行的SQL语句中的参数用问号(?)来表示,调用
PreparedStatement对象的setXxx()方法来设置这些参数. setXxx()方法有两个参数,第一个参数是要设置的SQL语句中的参数的索引(从1开始),第二个是设置的SQL语句中的参数的值
2.调用executeQuery),返回ResultSet 对象
3.调用executeUpdate):执行更新,包括增、删、修改
预处理的好处:
1.不再使用+拼接sql语句,减少语法错误
2.有效的解决了sql注入问题!
3.大大减少了编译次数,效率较高
对preparestatement的测试代码,配置文件还是上面的配置文件
import java.io.FileInputStream; import java.io.FileNotFoundException; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.util.Properties; public class PrepareStatement { public static void main(String[] args) throws Exception { Properties properties = new Properties(); properties.load(new FileInputStream("src\\db.properties")); String drive = properties.getProperty("driver"); String url = properties.getProperty("url"); String user = properties.getProperty("user"); String password = properties.getProperty("password"); //更新操作 // String sql = "update actor set phone = ? where name = ?"; //删除操作 // String sql = "delete from actor where name = ?"; // 添加操作 // String sql = "insert into actor values(null,?,?,?,?)"; //查询操作 String sql = "select * from actor"; Class.forName(drive); Connection connection = DriverManager.getConnection(url, user, password); PreparedStatement preparedStatement = connection.prepareStatement(sql); //更改操作 // preparedStatement.setString(1, "123456"); // preparedStatement.setString(2, "三毛"); //删除操作 // preparedStatement.setString(1, "二狗"); //添加操作 // preparedStatement.setString(1, "张三"); // preparedStatement.setString(2, "男"); // preparedStatement.setString(3, "1975-11-23 00:00:00"); // preparedStatement.setString(4,"456789"); // int i = preparedStatement.executeUpdate(); // System.out.println(i >0 ? "成功" : "失败"); //查询操作 ResultSet resultSet = preparedStatement.executeQuery(); while (resultSet.next()) { System.out.println(resultSet.getString(2)); } preparedStatement.close(); connection.close(); } }
总结:
五、事务:将多行代码当做SQL事务来处理,主要流程需要的关键代码:
connection.setAutoCommit(false);//取消自动提交的操作,取消之后,会在提及之后完成事务 connection.commit();提交事务
connection.rollback();//用于回退事务 批处理: 基本介绍:
1、当需要成批插入或者更新记录时。可以采用Java的批量更新机制,这一机制允
许多条语句一次性提交给数据库批量处理。通常情况下比单独提交处理更有效率。
2、JDBC的批量处理语句包括下面方法:
addBatch():添加需要批量处理的SQL语句或参数executeBatch(:执行批量处理语句;
clearBatch():清空批处理包的语句
3、 JDBC连接MySQL时,如果要使用批处理功能,请再url中加参
数?rewriteBatchedStatements=true
4、批处理往往和PreparedStatement一起搭配使用,可以既减少编译次数,又减少运行次数,效率大大提高
过程:
(1)将sql语句加入到批处理包中
preparedStatement.addBatch(); preparedStatement.addBatch(sql);
(2)执行sql和清空sql
//批量执行sql语句 preparedStatement.executeBatch(); //清除批量的sql语句 preparedStatement.clearBatch();
将 SQL加入到批处理包中源码所做的事情:
六、数据库连接池
传统connection问题分析:
1.传统的JDBC数据库连接使用 DriverManager来获取,每次向数据库建立
连接的时候都要将Connection 加载到内存中,再验证IP地址,用户名和密码(0.05s~1s时间)。需要数据库连接的时候,就向数据库要求一个,频繁的进行数据库连接操作将占用很多的系统资源,容易造成服务器崩溃。2每一次数据库连接,使用完后都得断开,如果程序出现异常面未能关闭,将
导致数据库内存泄漏,最终将导致重启数据库。
3.传统获取连接的方式,不能控制创建的连接数量,如连接过多,也可能导致
内存泄漏,MySQL崩溃。
4.解决传统开发中的数据库连接问题,可以采用数据库连接池技术
(connection pool).
数据库连接池基本介绍:
1.预先在缓冲池中放入一定数量的连接,,当需
要建立数据库连接时,只需从“缓冲池”中取出一个,,使用完毕之后再放回去。
2.数据库连接池负责分配、管理和释放数据库
连接,它允许应用程序重复使用一个现有的数据库连接,而不是重新建立一个。
3.当应用程序向连接池请求的连接数超过最大
连接数量时,这些请求将被加入到等待队列中
数据库连接池示意图:
数据库连接池种类:
1.JDBC的数据库连接池使用javax.sql.DataSource来表示,DataSource
只是一个接口,该接口通常由第三方提供实现
2.C3PO 数据库连接池,速度相对较慢,稳定性不错(hibernate, spring)
3.DBCP数据库连接池,速度相对c3p0较快,但不稳定
4. Proxool数据库连接池,有监控连接池状态的功能,稳定性较c3p0差一点
5.BoneCP数据库连接池,速度快
6.Druid(德鲁伊)是阿里提供的数据库连接池,集DBCP、C3P0、Proxool
优点于身的数据库连接池
测试:C3P0连接池的连接(需要导入第三方库:c3p0的jar包)
第一个C3P0连接的配置文件还是上面的配置文件内容
第二个C3P0的连接需要有一个xml配置文件
import com.mchange.v2.c3p0.ComboPooledDataSource; import org.junit.jupiter.api.Test; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.sql.Connection; import java.sql.SQLException; import java.util.Properties; public class ConDatasource { public static void main(String[] args) throws Exception { Properties properties = new Properties(); properties.load(new FileInputStream("src\\db.properties")); String driver = properties.getProperty("driver"); String user = properties.getProperty("user"); String password = properties.getProperty("password"); String url = properties.getProperty("url"); //建立comboPooledDataSource管理连接池 ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource(); comboPooledDataSource.setDriverClass(driver); comboPooledDataSource.setJdbcUrl(url); comboPooledDataSource.setUser(user); comboPooledDataSource.setPassword(password); //设置初始化连接数和最大连接数 comboPooledDataSource.setInitialPoolSize(10); comboPooledDataSource.setMaxPoolSize(50); //这个方法就是从DataSource接口实现的 Connection connection = comboPooledDataSource.getConnection(); System.out.println("1连接成功"); connection.close(); } @Test public void ConDatasource02() throws SQLException { ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource("test_C3P0"); long start = System.currentTimeMillis(); System.out.println("测试连接5000次需要的时间"); for (int i = 0; i < 5000; i++) { Connection connection = comboPooledDataSource.getConnection(); // System.out.println("2连接成功"); connection.close(); } long end = System.currentTimeMillis(); System.out.println("花费时长:"+(end-start)); } } //第二种方式的xml文件 <c3p0-config> <named-config name="test_C3P0"> <!-- 驱动类 --> <property name="driverClass">com.mysql.cj.jdbc.Driver</property> <!-- url--> <property name="jdbcUrl">jdbc:mysql://localhost:3306/test_db?useSSL=false&characterEncoding=utf8&serverTimezone=UTC</property> <!-- 用户名 --> <property name="user">root</property> <!-- 密码 --> <property name="password">123456</property> <!-- 每次增长的连接数--> <property name="acquireIncrement">5</property> <!-- 初始的连接数 --> <property name="initialPoolSize">10</property> <!-- 最小连接数 --> <property name="minPoolSize">5</property> <!-- 最大连接数 --> <property name="maxPoolSize">10</property> <!-- 可连接的最多的命令对象数 --> <property name="maxStatements">5</property> <!-- 每个连接对象可连接的最多的命令对象数 --> <property name="maxStatementsPerConnection">2</property> </named-config> </c3p0-config>
测试:Druid连接池的连接(需要第三方库druid的jar包)
package com.zjl.jdbc; 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 Druidsource { public static void main(String[] args) throws Exception { Properties properties = new Properties(); properties.load(new FileInputStream("src\\druid.properties"));//如下定义的配置文件 DataSource dataSource = DruidDataSourceFactory.createDataSource(properties); long start = System.currentTimeMillis(); for (int i = 0;i < 500000;i++ ) { Connection connection = dataSource.getConnection(); connection.close(); } long end = System.currentTimeMillis(); System.out.println("Druid连接5000 次需要的时间:" + (end - start));//Druid连接500000 次需要的时间:625 } } //连接中需要的一个配置文件
druid.properties:放在src目录下
#key=value driverClassName=com.mysql.cj.jdbc.Driver url=jdbc:mysql://localhost:3306/test_db?useSSL=false&characterEncoding=utf8&serverTimezone=UTC #url=jdbc:mysql://localhost:3306/girls username=root password=123456 #initial connection Size初始连接数 initialSize=10 #min idle connecton size最小等待连接数 minIdle=5 #max active connection size最大连接数 maxActive=20 #max wait time (5000 mil seconds) # 最大等待时间,时间超过就结束这次连接等待下一次的连接 maxWait=5000
注意:对于c3p0和Druid他们的close()是按照第三方库的连接方式,仅仅是将从连接池引用的数据库连接断开(放弃引用)
重新将连接放回到连接池中,和原生 的jdbc连接中的close方法断开连接不同;