一:什么是JDBC?
1.JDBC(Java DataBase Connectivity,java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口组成。JDBC提供了一种基准,据此可以构建更高级的工具和接口,使数据库开发人员能够编写数据库应用程序.
注:JDBC是java封装的对数据库的操作。
2.JDBC的 java api有哪些:
四个常用的接口:Connection接口、Statement接口、PreparedStatement接口、ResultSet接口
类:DriverManager类
2.1JDBC原生开发步骤:
(1)注册驱动(要引入驱动jar包)
(2)获得连接
(3)创建执行sql语句的对象
(4)执行sql语句,处理结果
(5)关闭资源
public static void main(String[] args) throws SQLException {
//1.注册驱动
DriverManager.registerDriver(new Driver());
String url = "jdbc:mysql://localhost:3306/test";
String user = "root";
String password = "123456";
//2.获得连接
Connection connection = DriverManager.getConnection(url, user, password);
//3.创建执行sql语句对象
Statement statement = connection.createStatement();
//4.执行sql,处理结果
String sql = "select *from user";
ResultSet resultSet = statement.executeQuery(sql);
while (resultSet.next()) {
System.out.println(resultSet.getObject(1));
System.out.println(resultSet.getObject(2));
System.out.println(resultSet.getObject(3));
System.out.println(resultSet.getObject(4));
}
//5.关闭资源
if(resultSet != null){
resultSet.close();
}
if(statement != null){
statement .close();
}
if(connection != null){
connection.close();
}
}
2.2JDBC工具类的抽取及使用。
2.2.1创建配置文件,配置文件在src目录下,扩展名是properties
配置文件:
driverClass=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/test
username=root
password=123456
- 工具类抽取:读取方式一
package com.xxxxx.utils;
import java.io.FileInputStream;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
//1.提供连接(1,2)
//2.释放资源(5)
public class JdbcUtils {
private static String driverClass;
private static String url;
private static String username;
private static String password;
//注册驱动只需要注册一次(静态代码块)
//配置文件,只需要读取一次
static{
// 注册驱动
try {
//0 根据jdbc.properties创建输入流
InputStream is = new FileInputStream("src/jdbc.properties");
//1\. 创建配置对象
Properties properties = new Properties();
//2.关联jdbc.properties文件
properties.load(is);
//3.根据key获得值
driverClass = properties.getProperty("driverClass");
url = properties.getProperty("url");
username = properties.getProperty("username");
password = properties.getProperty("password");
Class.forName(driverClass);
} catch (Exception e) {
e.printStackTrace();
}
}
// 提供连接
public static Connection getConnection() throws Exception {
// 2\. 获得连接
Connection connection = DriverManager.getConnection(url, username, password);
return connection;
}
//释放资源
public static void release(ResultSet resultSet,Statement statement,Connection connection){
if(resultSet != null){
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(statement != null){
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(connection != null){
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
- 读取配置文件方式二:
public class JdbcUtils {
static String url;
static String user;
static String password;
static String driver;
//注册驱动
static{
try {
//读取配置文件
ResourceBundle bundle = ResourceBundle.getBundle("jdbc");
url = bundle.getString("url");
user = bundle.getString("user");
password = bundle.getString("password");
driver = bundle.getString("driver");
Class.forName(driver );
} catch (Exception e) {
e.printStackTrace();
}
}
//获得连接
public static Connection getConnection() throws SQLException{
Connection connection = DriverManager.getConnection(url, user, password);
return connection;
}
//关闭资源
public static void release(ResultSet resultSet,Statement statement,Connection connection){
if(resultSet != null){
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(statement != null){
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(connection != null){
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
二:什么是数据库驱动?
数据库驱动是不同数据库开发商(比如oracle mysql等)为了某一种开发语言环境(比如java)能够实现统一的数据库调用而开发的一个程序,他的作用相当于一个翻译人员,将Java语言中对数据库的调用语言通过这个翻译翻译成各个种类的数据库自己的数据库语言,当然这个翻译(数据库驱动)是由各个开发商针对统一的接口自定义开发的。
注:数据库驱动,就是连接数据库的,直接与数据库交互的。
三:为什么要使用jdbc来操作数据库?
因为市面上有多种数据库,数据库编写的语言又不一样,所以连接各个数据库的驱动编写的语言也不一样,这时要使用相关的数据库,则必须学习相关数据库的驱动,就会造成开发人员学习和使用成本。sun公司为了规范统一,则定义了JDBC这套接口,所有厂商的数据库驱动只要遵循这套规范,则可以对数据库操作。开发人员就不必再学习相关的数据库驱动,只需要学习这套规范,使用JDBC操作数据库即可.(使用JDBC操作数据库必须要有数据库驱动包,JDBC是直接操作数据库驱动包进而操作数据库的)。
四:JDBC的原理,一张图片,让你明白JDBC与驱动的关系
注:接口(JDBC)与实现(驱动jar包)的关系。
五:使用数据库连接池。
1.什么是连接池?为什么使用连接池重写JDBC工具类?
连接池类比一个池子。
(1)Connection对象在JDBC使用的时候就会去创建一个对象,使用结束以后就会将这个对象给销毁了(close).每次创建和销毁对象都是耗时操作. 这样做是特别销毁资源的.
(2)需要使用连接池对其进行优化.程序初始化的时候,初始化多个连接,将多个连接放入到池(集合)中.每次获取的时候,都可以直接从连接池中进行获取.使用结束以后,将连接归还到池中.
[图片上传失败...(image-e52bef-1564213471546)]
2.生活里面的连接池例子
- 老方式:
下了地铁需要骑车, 跑去生产一个, 然后骑完之后,直接把车销毁了. - 连接池: 膜拜单车
一开始就有一个公司创建了很多的自行车, 下了地铁需要骑车, 直接扫描骑车, 然后骑完之后,放在原地
3.连接池原理【重点】
- 目的:解决建立数据库连接耗费资源和时间很多的问题,提高性能。
4.数据库连接池API
4.1数据源接口
4.2数据源接口中的方法
5.常用连接池
DataSource本身只是sun公司提供的一个接口,没有具体的实现,它的实现由连接池的数据库厂商去实现。我们只需要学习这个工具如何使用即可。常用的连接池实现有这些:
- DBCP(DataBase Connection Pool)数据库连接池,是Apache上的一个Java连接池项目,也是Tomcat使用的连接池组件。dbcp没有自动回收空闲连接的功能。
- C3P0是一个开源的JDBC连接池目前使用它的开源项目有Hibernate,Spring等。C3P0有自动回收空闲连接功能
5.1dbcp连接池
5.1.1dbcp概念
- DBCP:Apache推出的Database Connection Pool
核心API:
- basicDatasource
- basicDatasourceFactory
以下代码公用User实体类:
public class User {
/*id int primary key auto_increment,
username varchar(20),
password varchar(20),
nickname varchar(20)*/
private int id;
private String username;
private String password;
private String nickname;
public User() {
}
public User(int id, String username, String password, String nickname) {
this.id = id;
this.username = username;
this.password = password;
this.nickname = nickname;
}
//Alt+Shift+S
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
@Override
public String toString() {
return "User [id=" + id + ", username=" + username + ", password=" + password + ", nickname=" + nickname + "]";
}
}
5.1.2dbcp的使用
- 方式一:通过硬编码来编写【了解】
import org.apache.commons.dbcp.BasicDataSource;
import org.apache.commons.dbcp.BasicDataSourceFactory;
import org.junit.Test;
import com.xxxx.bean.User;
import com.mysql.jdbc.Connection;
import com.mysql.jdbc.PreparedStatement;
public class DbcpDemo {
@Test
//查询id=1的用户
//使用Dbcp连接池方式一: 使用硬编码(了解)
public void fun01() throws SQLException{
//1. 创建连接池(数据源) [先把dbcp的jar导入一下]
BasicDataSource basicDataSource = new BasicDataSource();
//设置四个基本参数(驱动,路径,用户名,密码)
basicDataSource.setDriverClassName("com.mysql.jdbc.Driver");
basicDataSource.setUrl("jdbc:mysql://localhost:3306/web11");
basicDataSource.setUsername("root");
basicDataSource.setPassword("123456");
/*//设置初始化连接的数量(选配, 不配置是有默认值)
basicDataSource.setInitialSize(5);
//设置等待时间(选配, 不配置是有默认值)
basicDataSource.setMaxWait(2000);*/
//2. 直接从连接池里面获得连接对象
Connection connection = basicDataSource.getConnection();
//3. 创建预编译sql语句对象
String sql = "select * from user where id = ?";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
//4. 设置参数, 执行, 处理结果
preparedStatement.setInt(1, 1);
ResultSet resultSet = preparedStatement.executeQuery();
User user = null;
while(resultSet.next()){
user = new User(resultSet.getInt("id"), resultSet.getString("username"), resultSet.getString("password"),
resultSet.getString("nickname"));
}
System.out.println(user);
//5. 释放资源(这个方法还是close,但是不是销毁了,而是归还; 因为这个connection是从连接池里面获得的, 连接池内部重写了close)
connection.close();
}
}
- 方式二:通过配置文件来配置【重点】
添加配置文件到src目录 - 配置文件:
import org.apache.commons.dbcp.BasicDataSource;
import org.apache.commons.dbcp.BasicDataSourceFactory;
import org.junit.Test;
import com.xxxx.bean.User;
import com.mysql.jdbc.Connection;
import com.mysql.jdbc.PreparedStatement;
public class DbcpDemo {
@Test
//查询id=1的用户
//使用Dbcp连接池方式二: 配置文件方式[掌握]
public void fun02() throws Exception{
//0 创建配置对象, 加载dbcpconfig.properties
Properties properties = new Properties();
//关联dbcpconfig.properties配置文件
InputStream is = DbcpDemo.class.getClassLoader().getResourceAsStream("dbcpconfig.properties");
properties.load(is);
//1. 创建连接池(数据源) [先把dbcp的jar导入一下, 先把配置文件导入src目录下]
BasicDataSource basicDataSource = (BasicDataSource) BasicDataSourceFactory.createDataSource(properties);
//2. 直接从连接池里面获得连接对象
Connection connection = basicDataSource.getConnection();
//3. 创建预编译sql语句对象
String sql = "select * from user where id = ?";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
//4. 设置参数, 执行, 处理结果
preparedStatement.setInt(1, 1);
ResultSet resultSet = preparedStatement.executeQuery();
User user = null;
while(resultSet.next()){
user = new User(resultSet.getInt("id"), resultSet.getString("username"), resultSet.getString("password"),
resultSet.getString("nickname"));
}
System.out.println(user);
//5. 释放资源(这个方法还是close,但是不是销毁了,而是归还; 因为这个connection是从连接池里面获得的, 连接池内部重写了close)
connection.close();
}
}
5.2 C3P0连接池
5.2.1 c3p0概念
- C3P0开源免费的连接池!目前使用它的开源项目有:Spring、Hibernate等。使用第三方工具需要导入jar包,c3p0使用时还需要添加配置文件c3p0-config.xml.
- 使用C3P0需要添加c3p0-0.9.1.2.jar
5.2.2c3p0的使用
- 通过硬编码来编写(不需要配置文件.了解一下)
import org.junit.Test;
import com.xxxx.bean.User;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import com.mysql.jdbc.Connection;
import com.mysql.jdbc.PreparedStatement;
public class C3P0Demo {
@Test
//查询id=1的用户
//使用C3P0连接池方式一:硬编码(了解)
public void fun01() throws Exception{
//1 创建连接池(数据源)
ComboPooledDataSource cpds = new ComboPooledDataSource();
//加载四个基本项(驱动,路径,用户名,密码)
cpds.setDriverClass("com.mysql.jdbc.Driver");
cpds.setJdbcUrl("jdbc:mysql://localhost:3306/web11");
cpds.setUser("root");
cpds.setPassword("123456");
//初始化连接数量(选配)
//cpds.setInitialPoolSize(5);
//2. 从连接池里面获得Connection
Connection connection = cpds.getConnection();
//3. 创建预编译sql语句对象
String sql = "select * from user where id = ?";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
//4. 设置参数, 执行, 处理结果;
preparedStatement.setInt(1, 1);
ResultSet resultSet = preparedStatement.executeQuery();
User user = null;
while(resultSet.next()){
user = new User(resultSet.getInt("id"), resultSet.getString("username"), resultSet.getString("password"),
resultSet.getString("nickname"));
}
System.out.println(user);
//5. 释放资源(这个方法还是close,但是不是销毁了,而是归还; 因为这个connection是从连接池里面获得的, 连接池内部重写了close)
connection.close();
}
}
- 通过配置文件来编写
编写配置文件c3p0-config.xml,放在src中(注:文件名一定不要写错,不要改)
配置文件:
<c3p0-config>
<default-config>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/web11</property>
<property name="user">root</property>
<property name="password">123456</property>
<property name="initialPoolSize">5</property>
</default-config>
</c3p0-config>
代码实现:
import org.junit.Test;
import com.xxxx.bean.User;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import com.mysql.jdbc.Connection;
import com.mysql.jdbc.PreparedStatement;
public class C3P0Demo {
@Test
//查询id=1的用户
//使用C3P0连接池方式二:配置文件方式(重点)
public void fun02() throws Exception{
//1 创建连接池(数据源);
DataSource cpds = new ComboPooledDataSource();//会自动读取src目录下的c3p0-config.xml配置文件
//2. 从连接池里面获得Connection
Connection connection = cpds.getConnection();
//3. 创建预编译sql语句对象
String sql = "select * from user where id = ?";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
//4. 设置参数, 执行, 处理结果;
preparedStatement.setInt(1, 1);
ResultSet resultSet = preparedStatement.executeQuery();
User user = null;
while(resultSet.next()){
user = new User(resultSet.getInt("id"), resultSet.getString("username"), resultSet.getString("password"),
resultSet.getString("nickname"));
}
System.out.println(user);
//5. 释放资源(这个方法还是close,但是不是销毁了,而是归还; 因为这个connection是从连接池里面获得的, 连接池内部重写了close)
resultSet.close();
preparedStatement.close();
connection.close();
}
}
5.3使用C3P0连接池改写工具类(C3P0配置文件方式+C3P0工具类,开发中常用)
配置文件:
<c3p0-config>
<default-config>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/web11</property>
<property name="user">root</property>
<property name="password">123456</property>
<property name="initialPoolSize">5</property>
</default-config>
</c3p0-config>
c3p0工具类的抽取:
public class C3P0Utils {
//创建一个连接池对象
private static DataSource ds = new ComboPooledDataSource();//会自动读取src目录下的c3p0-config.xml配置文件
//从池中获得一个连接
public static Connection getConnection() throws SQLException{
return ds.getConnection();
}
//释放资源
public static void closeAll(ResultSet rs,Statement stmt,Connection conn){
if(rs!=null){
try {
rs.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
rs = null;
}
if(stmt!=null){
try {
stmt.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
stmt = null;
}
if(conn!=null){
try {
conn.close();//放心的关。是否关闭取决连接是怎么来的
} catch (SQLException e) {
throw new RuntimeException(e);
}
conn = null;
}
}
}
代码实现:
public class C3P0Demo {
@Test
//查询id=1的用户
public void fun01() throws Exception{
// 1. 获得连接
Connection connection = C3P0Utils.getConnection();
//3. 创建预编译sql语句对象
String sql = "select * from user where id = ?";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
//4. 设置参数, 执行, 处理结果;
preparedStatement.setInt(1, 1);
ResultSet resultSet = preparedStatement.executeQuery();
User user = null;
while(resultSet.next()){
user = new User(resultSet.getInt("id"), resultSet.getString("username"), resultSet.getString("password"),
resultSet.getString("nickname"));
}
System.out.println(user);
//5. 释放资源(这个方法还是close,但是不是销毁了,而是归还; 因为这个connection是从连接池里面获得的, 连接池内部重写了close)
C3P0Utils.release(resultSet, preparedStatement, connection);
}
}
六:使用DBUtils 增删改查的操作(简化JDBC代码开发)
6.1概述
- DBUtils是java编程中的数据库操作实用工具,小巧简单实用。 第一个操作数据库框架(jar),
- DBUtils封装了对JDBC的操作,简化了JDBC操作流程,可以少写代码。
- Dbutils三个核心功能介绍
QueryRunner 执行sql语句. update() 执行update, delete,insert , query()执行select
ResultSetHandler 封装查询结果集.(select )
6.2QueryRunner核心类
- QueryRunner(DataSource ds) ,,构造函数. 底层自动维护连接connection
- update(String sql, Object... params) ,执行更新数据 insert update delete 参数就是一个数组,参数个数取决于语句中?的个数.
- query(String sql, ResultSetHandler<T> rsh, Object... params) ,执行查询 select
6.3ResultSetHandler结果集处理类
6.4使用DBUtils完成数据库的CRUD
6.4.1开发步骤:
- 创建项目,并导入jar包
- 创建连接池
- 编写测试类
- 连接池配置文件c3p0-config.xml:
<c3p0-config>
<default-config>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/web11</property>
<property name="user">root</property>
<property name="password">123</property>
<property name="initialPoolSize">5</property>
</default-config>
</c3p0-config>
工具类:代码上面贴了。
import java.sql.SQLException;
import java.util.List;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;
import org.junit.Test;
import com.xxxx.bean.User;
import com.xxxxx.utils.C3P0Utils;
public class DbUtilsTest {
@Test
//更新(底部封装了 PrepareStatemmet)
//把id = 1的用户的名字改成zl
public void fun01() throws SQLException{
//1.创建QueryRunner
QueryRunner queryRunner = new QueryRunner(C3P0Utils.getDataSource());
//执行方法update
String sql = "update user set username = ? where id = ?";
queryRunner.update(sql, "zl",1);
}
@Test
//增加
public void fun02() throws SQLException{
//1创建QueryRunner对象
QueryRunner queryRunner = new QueryRunner(C3P0Utils.getDataSource());
//2执行方法update
String sql = "insert into user values(?,?,?,?)";
queryRunner.update(sql, null,"zs","123456","张三");
}
@Test
//把id=1为1的用户删除
public void fun03() throws SQLException{
//1创建QueryRunner对象
QueryRunner queryRunner = new QueryRunner(C3P0Utils.getDataSource());
//2.执行方法update
String sql = "delete from user where id = ?";
queryRunner.update(sql, 1);
}
@Test
//查询id为2的用户 一条数据 ---》封装成一个User对象(JavaBean) BeanHandler
public void fun04() throws SQLException{
//1.创建 QueryRunner对象
QueryRunner queryRunner = new QueryRunner(C3P0Utils.getDataSource());
//2.执行方法query
String sql = "select * from user where id = ?";
User user = queryRunner.query(sql, new BeanHandler<>(User.class),2);
System.out.println(user);
}
@Test
//查询所有 BeanListHandler
public void fun05() throws SQLException{
//1.创建 QueryRunner对象
QueryRunner queryRunner = new QueryRunner(C3P0Utils.getDataSource());
//2.执行方法query
String sql = "select * from user ";
List<User> user = queryRunner.query(sql, new BeanListHandler<>(User.class));
System.out.println(user);
}
//统计总数量 select count(*) from user
@Test
public void fun06() throws SQLException{
//1.创建 QueryRunner对象
QueryRunner queryRunner = new QueryRunner(C3P0Utils.getDataSource());
//2.执行方法query
String sql = "select count(*) from user ";
Long n = (long)queryRunner.query(sql, new ScalarHandler());
System.out.println(n);
}
}
七.什么是数据源?
由SUN制定的用于获取数据库连接的规范接口。它存在于 javax.sql包中,用来代替 DriverManager 的方式来获取连接,JDBC2.0 提供了javax.sql.DataSource接口,它负责建立与数据库的连接,当在应用程序中访问数据库时不必编写连接数据库的代码,直接引用DataSource获取数据库的连接对象即可。用于获取操作数据Connection对象。
7.1.数据源与数据库连接池
数据源建立多个数据库连接,这些数据库连接会保存在数据库连接池中,当需要访问数据库时,只需要从数据库连接池中获取空闲的数据库连接,当程序访问数据库结束时,数据库连接会放回数据库连接池中。
7.2关于Datasource与数据库连接池的区别。
数据库连接有两种方式:
- 1 :直连数据库方式
当调用DataSource.getConnection()时,其实它调用的是DriverManager.getConnection(url, user, password)来获取一个Connection,Connection使用完后被close,断开与数据库的连接,我们称这总方式是直连数据库,因为每次都需要重新建立与数据库之间的连接,而并没有把之前的Connection保留供下次使用.
- 2 :池化连接方式
1.可以说这种方式就是使用了连接池技术.DataSource内部封装了一个连接池,当你获取DataSource的时候,它已经敲敲的与数据库建立了多个Connection,并将这些Connection放入了连接池,此时调用DataSource.getConnection()它从连接池里取一个Connection返回,Connection使用完后被close,但这个close并不是真正的与数据库断开连接,而是告诉连接池"我"已经被使用完,"你"可以把我分配给其它"人"使用了.就这样连接池里的Connection被循环利用,避免了每次获取Connection时重新去连接数据库.
对DataSource的两种实现方式已经介绍完毕,现在知道DataSource与连接池之间的是关系而不是区别了吧,因为DataSource与连接池根本就不是同一类型的东西,只有同一类型的东西才存在区别,例如:oracle与db2都是数据库,它们才存在区别.
DataSource与连接池的关系是:DataSource利用连接池缓存Connection,以达到系统效率的提升,资源的重复利用.
而连接池它可以单独存在,不需要依靠DataSource来获取连接,你可以直接调用连接池提供的方法来获取连接.
目前大多数应用服务器都支持池化连接方式的DataSource.
常用的连接池有c3p0,dbcp,Proxool...
一般场景:JNDI-> DataSource->连接池(c3p0,dbcp,Proxool等)->DriverManager->connection