JDBC
文章目录
1.JDBC是什么?
Java Database Connectivity(java语言链接数据库)
2.JDBC本质是什么?
JDBC是SUN公司制定的一套接口(interface)
java.sql.*;(这个软件包下有很多接口)
接口都有调用者和实现者
面向接口调用,面向接口写实现类,这都属于面相接口编程
为什么要面相接口编程?
解耦合:降低程序的耦合度,提高程序的扩展力,可以让程序更灵活
多态机制就是非常典型的:面向抽象编程.(不要面向具体编程)
建议:父类型引用指向子类型对象,这个在java中是被允许的
Animal a = new Cat();
Animal a = new Dog();
// 喂养方法
public void feed(Animal a){ // 面向父类型编程
}
举例:螺栓和螺母之间3mm,5mm,1cm…这个所谓的3mm,5mm,1cm…可以抽象为是一种接口,接口定好以后双方都要遵守,生产螺栓的得遵守,生产螺母的得遵守,也就说调用者和实现者都得遵守.
注意:这个喂养的方法就是面向抽象编程,或者更具体的说是面向父类型编程,父类型,这个"父"就是抽象的,那么接口是完全抽象的,我们可以说成是面向接口编程
不建议:(面向具体编程)
Dog d = new Dog();
Cat c = new Cat();
思考:为什么SUN制定一套JDBC接口呢?
因为每一个数据库的底层实现原理不一样.
Oracle数据库有自己的原理
Mysql数据库有自己的原理
MS SqlServer数据库有自己的原理
…
每一个数据库产品都有自己独特的实现原理
如果没有JDBC接口那我们Java程序员会非常痛苦,我们链接Mysql数据库得写一套,我们连接Oracle得写一套…因为不同的数据库都有自己独特的实现原理
此时SUN公司站出来了,制定了一套JDBC接口,让各大数据库厂家去实现接口,而我们只需要调用,不用关心数据库厂家具体是怎么实现的
!(C:\Users\19119\AppData\Roaming\Typora\typora-user-images\image-20220515184837182.png)
我们充当的角色是Java程序员,我们以后只面向接口写程序,我们不用关心连接的是什么品牌的数据库
每个数据库厂家负责编写JDBC接口的实现类
实现类被称为驱动,Mysql实现JDBC就叫mysql驱动,Oracle实现JDBC就叫oracle驱动…
实现JDBC就是一大堆文件,编译之后会生成一大堆.class文件,这些.class文件集合在一起成为一个xxx.jar
3.JDBC开发前的准备工作,先从官网下载对应的驱动jar包,然后将其配置到环境变量classpath当中
3.1 下载Mysql的jar包,下载地址:https://dev.mysql.com/downloads/connector/j/
4.JDBC编程六步(需要背会)
- 第一步: 注册驱动(作用: 告诉Java程序,即将要连接的是哪个品牌的数据库)
- 第二步: 获取链接(表示JVM的进程和数据库进程之间的通道打开了,这属于进程之间的通信,重量级的,使用完成后一定要关闭资源)
- 第三步: 获取数据库操作对象(专门执行sql语句的对象)
- 第四步: 执行SQL语句(DQL,DML…)
- 第五步: 处理查询结果集(只有当第四步是select语句的时候,才有这第五步处理查询结果集)
- 第六步: 释放资源(使用完资源之后一定要关闭资源,Java和数据库属于进程间的通讯,开启之后一定要关闭.
5.使用java操作数据库
5.1插入删除查询
package test;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
/*
* JDBC编程6步
*/
public class JDBCTest01 {
public static void main(String[] args) {
Connection conn = null;
Statement stmt = null;
try {
// 1.注册驱动
Driver driver = new com.mysql.cj.jdbc.Driver();
DriverManager.registerDriver(driver);
// 2.获取链接
/*
* url:统一资源定位符(网络中某个资源的绝对路径)
* https://www.baidu.com/这就是url
* url包括哪几个部分?
* 协议
* IP
* PORT 端口
* 资源名
* http://182.61.200.7:80/index.html
* http:// 通信协议
* 182.61.200.7 服务器IP地址
* 80 服务器上软件的端口
* index.html 是服务器上的某个资源名
*
* jdbc:mysql://127.0.0.1:3306/test
* jdbc:mysql:协议
* 127.0.0.1 IP地址
* 3306 端口号
* bjpowernode 具体的数据库实例名
*
* 说明:localhost和127.0.0.1都是本机的IP地址
* 什么是通信协议,有什么用?
* 通信协议就是通信之前定好的数据传送格式
* 数据包具体怎么传数据,格式提前定好的
*/
String url = "jdbc:mysql://127.0.0.1:3306/bjpowernode";
String user = "root";
String password = "123456";
conn = DriverManager.getConnection(url, user , password );
// 3.获取数据库操作对象(专门执行sql语句的)
stmt = conn.createStatement();
// 4.执行sql语句
String sql = "insert into dept(deptno,dname,loc) values(50,'人事部','北京')";
// 专门执行sql语句的(insert delete update)
// 返回值是"影响数据库中的记录条数"
int count = stmt.executeUpdate(sql);
// 5.处理查询结果集 插入 删除 修改 没有第五步
}catch (SQLException e) {
e.printStackTrace();
}finally {
// 6.关闭资源
//为了保证资源一定释放,在finally语句中关闭资源
//遵循从小到大依次关闭
//分别对其try..catch
try {
if (stmt != null) {
stmt.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (conn != null) {
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
5.2演示删除
package test;
import java.sql.Statement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
/*
* JDBC完成delete
*/
public class JDBCTest02 {
public static void main(String[] args) {
Connection coon = null;
Statement stmt = null;
try {
// 1.注册驱动
DriverManager.registerDriver(new com.mysql.cj.jdbc.Driver());
// 2.获取链接
coon = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode","root","123456");
// 3.获取数据库操作对象
stmt = coon.createStatement();
// 4.执行sql语句
String sql = "delete from dept where deptno = 40";
int count = stmt.executeUpdate(sql);
} catch (SQLException e) {
e.printStackTrace();
}finally {
// 6.关闭资源
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (coon != null) {
try {
coon.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
5.3注册驱动的另一种方式(这种方式常用)
package test;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
/*
* 注册驱动的另一种方式(这种方式常用)
*/
public class JDBCTest03 {
public static void main(String[] args) {
try {
// 1.注册驱动
// 这是注册驱动的第一种写法
//DriverManager.registerDriver(new com.mysql.cj.jdbc.Driver());
// 这是注册驱动的第二种写法 常用的
// 因为参数是一个字符串,字符串可以写到xxx.properties文件中
// 以下方式不需要接受返回值,因为我们只想用它的类加载动作
Class.forName("com.mysql.jdbc.Driver");
// 2.获取链接
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode","root","123456");
} catch (SQLException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
5.4将链接数据库的所有信息配置到配置文件中
// 将链接数据库的所有信息配置到配置文件中
package test;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ResourceBundle;
public class JDBCTest04 {
public static void main(String[] args) {
// 使用资源绑定器绑定属性配置文件
ResourceBundle bundle = ResourceBundle.getBundle("jdbc");
String driver = bundle.getString("driver");
String url = bundle.getString("url");
String user = bundle.getString("user");
String password = bundle.getString("password");
Connection conn = null;
Statement stmt = null;
try {
// 1.注册驱动
Class.forName(driver);
// 2.获取链接
conn = DriverManager.getConnection(url,user,password);
// 3.获取数据库操作对象
stmt = conn.createStatement();
// 4.执行sql语句
String sql = "delete from dept where deptno = 40";
int count = stmt.executeUpdate(sql);
} catch (Exception e) {
e.printStackTrace();
}finally {
// 6.关闭资源
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
配置文件
driverClassname=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/库名
username=root
password=123456
5.5处理查询结果集之遍历结果集
package test;
/*
* 遍历查询结果集
*/
import java.sql.Statement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
public class JDBDTest05 {
public static void main(String[] args) {
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
// 1.注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
// 2.获取连接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test","root","123456");
// 3.获取数据库操作对象
stmt = conn.createStatement();
// 4.执行sql
String sql = "select empno as a,ename,sal from emp";
// 返回int ResultSet executeUpdate(insert/delete/update)
// ResultSet executeQuery(select)
rs = stmt.executeQuery(sql);// 专门执行DQL语句的方法
// 5.处理查询结果集
while(rs.next()) {
/*
* String empno = rs.getString(1);
* String ename = rs.getString(2);
* String sal = rs.getString(3);
* System.out.println(empno + "" + ename + "," + sal);
*/
// 这个不是以列的下标获取,以列的名字获取
/*
* String empno = rs.getString("a");
* String ename = rs.getString("ename");
* String sal = rs.getString("sal");
* System.out.println(empno + "" + ename + "," + sal);
*/
// 除了以String类型取出之外,还可以以特定的类型取出
int empno = rs.getInt(1);
String ename = rs.getString(2);
double sal = rs.getDouble(3);
System.out.println(empno + "" + ename + "," + sal);
}
} catch (Exception e) {
e.printStackTrace();
}finally {
if(rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
6.将获取连接对象和关流封装到工具类中
作用:方便调用
配置文件:(放在当前项目的src下,相对路径)
此代码中文件名为:jdbc.properties
driverClassname=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/day12_homework
username=root
password=123456
代码:
public class JdbcUtils {
private static String driverClassname = null;
private static String url = null;
private static String username = null;
private static String password = null;
static{
try {
Properties p = new Properties();
InputStream is = JdbcUtils.class.getClassLoader().getResourceAsStream("jdbc.properties");
p.load(is);//加载
driverClassname = p.getProperty("driverClassname");
url = p.getProperty("url");
username = p.getProperty("username");
password = p.getProperty("password");
Class.forName(driverClassname);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
private JdbcUtils(){}
//获取连接对象的方法
public static Connection getConnection() throws SQLException {
Connection conn = DriverManager.getConnection(url, username, password);
return conn;
}
//关流的方法
public static void close(Statement stmt, Connection conn){
close(null,stmt,conn);
}
//关流的方法
public static void close(ResultSet rs, Statement stmt, Connection conn){
if (rs != null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (stmt != null){
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
7.单元测试
7.1单元测试操作步骤
1)导包junit核心包以及hmrecrest-core 依赖包
2)测试某个功能
编写测试用例----编写一个单元测试方法
这个方法没有返回值类型,没有参数
3)需要标记这个方法为单元测试方法,需要在方法上面加入@Test注解
4)在junit包下一个断言Assert里面很多的方法来断言某个功能的结果
是否和预期结果一致,如果一致,说明断言成功;否则失败! (如果测试自己的功能,不用断言,可以直接接口多态测试数据是否存在!)
7.2代码
//定义一个计算器类
public class Clacuator {
public int add(int a,int b){
return a + b ;
}
public int sub(int a,int b){
return a-b ;
}
public int mul(int a,int b){
return a*b ;
}
public int div(int a,int b){
return a/b ;
}
}
public class CalcTest {
/**
* 测试计算器类中 两个求和功能
*/
@Test
public void testAdd(){
//创建计算类的对象
Clacuator cal = new Clacuator() ;
//调用求和功能
int result = cal.add(10, 20);
//使用Assert断言测试结果和预期结果是否一致
Assert.assertEquals(30,result);
}
}
8.使用PrepareStatement预编译对象执行sql语句(DML/DQL)
8.1 简介:
使用预编译对象PreparedStatement对象作为"数据库的执行对象"
public interface PreparedStatement extends Statement
8.2操作步骤:
DML语句/DQL语句
原生的写法:
- 0)导包
- 1)注册驱动
- 2)获取数据库的连接对象
- 3)准备sql:参数化的sql语句
- 4)获取执行对象 并且同时执行更新或者查询
- 5)返回结果
- 6)释放资源
8.3 准备:配置文件
配置文件名为:jdbc.properties
内容:
driverClassname=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/day12_homework
username=root
password=123456
8.4 封装工具类代码:
//先封装工具类
//将当前jdbc的部分操作封装到这个工具类中
public class JdbcUtils {
//成员变量声明
private static String url = null;
private static String username = null;
private static String password = null;
private static String driverClassname = null;
//try--catch-->ctrl+alt+t---->选择 6
//静态代码块
static {
try {
//应该要获取url,username,password,driverClassname 这几个值
//需要properties配置文件
//java.util.Properties:属性集合列表 :属性列表中的每个键及其对应的值都是一个字符串。
Properties prop = new Properties();
//System.out.println(prop);
//有个功能:public void load(InputStream inStream):将输入流中的内容到属性集合列表中
//读取src下面的jdbc.properties配置文件
//当期类.class.getClassLoader().getResourceAsStream("配置文件的名称xxx.properties") ;---获取资源文件所在的输入流对象
InputStream inputStream = JdbcUtils.class.getClassLoader().getResourceAsStream("jdbc.properties");
//加载流对象到属性集合列表中
prop.load(inputStream);
//System.out.println(prop);
//java.util.Properties的特有功能:可以通过键获取值
//public String getProperty(String key)
driverClassname = prop.getProperty("driverClassname"); //获取驱动类
url = prop.getProperty("url"); //获取url
username = prop.getProperty("username"); //用户名
password = prop.getProperty("password");//密码
//注册驱动
Class.forName(driverClassname);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
//外界不能new 对象
private JdbcUtils() {
}
//获取连接对象
public static Connection getConnnection() {
try {
Connection connection = DriverManager.getConnection(
url,
username,
password
);
return connection;
} catch (SQLException e) {
e.printStackTrace();
}
return null ;
}
//释放资源
//如果是针对ddl语句或者dml语句,Statement对象以及Connection对象关闭
public static void close(Statement stmt, Connection conn) {
close(null, stmt, conn);
}
//如果是针对DQL语句,ResultSet对象以及Statement以及Connection对象关闭
public static void close(ResultSet rs, Statement stmt, Connection conn) {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
//以后事务管理也可以封装到这块
public static void main(String[] args) throws SQLException {
//测试 能否获取连接对象
System.out.println(JdbcUtils.getConnnection());
}
}
8.5 使用PrepareStatement预编译对象执行sql语句DML代码
public class PreparedStatementDemo {
public static void main(String[] args) throws SQLException {
//添加员工数据
Connection connnection = JdbcUtils.getConnnection();
//sql---参数化的sql语句
//sql语句中的值不是写死的,是在执行sql执行之前,可以多次赋值
String sql = "insert into emploee(name,age,gender,address,salary) values(?,?,?,?,?)" ;
//通过连接对象获取预编译对象PreparedStatement
//Connection---->PreparedStatement prepareStatement(String sql) throws SQLException
//将参数化的sql发送给数据库,并且存储到PreparedStatement对象中
PreparedStatement ps = connnection.prepareStatement(sql);
//PreparedStatement预编译对象中,需要给 ? (占位符号)进行赋值
//通用方法:void setXxx(int parameterIndex, Xxx x) :给占位符号赋值,
//参数1:第几个占位符号(从1开始)
//参数2:实际参数
ps.setString(1,"亓桑") ;
ps.setInt(2,20);
ps.setString(3,"男");
ps.setString(4,"西安市") ;
ps.setDouble(5,10000.00) ;
//PreparedStatement预编译对象 :执行sql
//通用方法:int executeUpdate() :DML语句
//ResultSet executeQuery() :DQL语句
int count = ps.executeUpdate();
System.out.println("影响了"+count+"行");
//释放资源
JdbcUtils.close(ps,connnection);
}
}
8.6 使用PrepareStatement预编译对象执行sql语句DQL代码
public class PreparedStatementDemo2 {
public static void main(String[] args) throws SQLException {
//添加员工数据
Connection connnection = JdbcUtils.getConnnection();
//sql---参数化的sql语句
//sql语句中的值不是写死的,是在执行sql执行之前,可以多次赋值
String sql = "select * from emploee where id = ?" ;
//通过连接对象获取预编译对象PreparedStatement
//Connection---->PreparedStatement prepareStatement(String sql) throws SQLException
//将参数化的sql发送给数据库,并且存储到PreparedStatement对象中
PreparedStatement ps = connnection.prepareStatement(sql);
//给参数赋值
ps.setInt(1,7) ;
//执行查询
//ResultSet executeQuery() :DQL语句
ResultSet rs = ps.executeQuery();
while (rs.next()){
int id = rs.getInt("id");
String name = rs.getString("name");
int age = rs.getInt("age");
String gender = rs.getString("gender");
String address = rs.getString("address");
double salary = rs.getDouble("salary");
System.out.println(id+"\t"+name+"\t"+age+"\t"+gender+"\t"+address+"\t"+salary);
}
//释放资源
JdbcUtils.close(ps,connnection);
}
}
9.Statement和PreparedStatement的区别(面试题)
* Statement对象和PreparedStatement对象的区别: 面试题
* 1)执行sql效率区别
* Statement每一次需要将静态化的sql进行发送给数据库, 写好一条sql,发送一次,相对于PreparedStatement预编译对象来说,执行sql效率非常低!
* PreparedStatement将编译参数化的sql(占位符号?)发送给数据库,数据库会校验它的列索引值以及对应的类型,将sql保存到预编译对象中
* 预编译对象可以赋值不同的参数,执行对应sql,执行效率相对Statement高很多!
* 2)是否会造成"SQL注入"
* Statement对象发送的sql属于静态sql语句,存在"硬编码",而且还有字符串拼接,就会造成sql注入,非常不安全的一种行为!
* PreparedStatement对象发送的sql都是参数化的sql,不存在字符串拼接,不会造成sql注入,相对Statement是一种安全行为!
*
* 注意事项:
* 后期框架(半成品的东西:一堆通用代码+一堆配置文件)底层对象性jdbc的封装使用的就是PreparedStatement
10.引入连接池以及它的使用
10.1 什么是DataSource?
java.sql.DataSource:物理数据源的工厂 是sun公司提供的接口---最终替代DriverManager:管理jdbc的驱动服务
里面有一个Connection getConnection() ; 直接获取连接对象
也就是说最终目的就是获得Connection对象(连接对象);那么为啥要用这个? 后边说他的好处
解释:
DataSource是sun提供的接口里边有getConnection方法,
数据库厂商实现这个接口,提供对应的连接池jar包:举例:Druid(德鲁伊,阿里提供的)
10.2 操作步骤:
* 1)导入包
* druid-1.1.10.jar
* 2)获取数据库连接对象----提前配置好数据库连接池的配置文件
* druid.properties
*
* 配置文件的内容key是DruidDataSource里面的参数信息
* 配置好数据库的基本信息
*
* 配置好连接其他信息
* 初始化数量
* initialSize
* 最大激活数量...
* 最小空闲数量:.minIdle
* 最大空闲数量:maxIdle
* activeCount
* 最大等待时间(超过最大连接数量的等待时间:毫秒值)
* maxWait
* maxActive:最大激活数量
* 3)读取配置文件
*
* 4)获取数据库的连接对象
10.3 数据库连接池的好处
可以初始化连接数量,当某个线程使用某个连接对象时候,连接对象就会被这个线程持有,当这个线程结束了,释放连接对象,会将连接对象归还到数据库连接池中等待下一次利用!
相比之前的,用一次连接一次,用完了就关闭流,很浪费资源
10.4代码
准备工作:
配置文件名称:druid.properties,放在src下
配置文件内容:
driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/库名?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true
username=root
password=123456
initialSize=5
maxActive=10
maxWait=3000
操作代码:
public class DruidDemo {
public static void main(String[] args) throws Exception {
//1)读取配置文件
InputStream inputStream = DruidDemo.class.getClassLoader().getResourceAsStream("druid.properties");
//创建一个属性集合列表类对象
Properties prop = new Properties() ;
//加载流对象
prop.load(inputStream);
//2)创建Datasource数据源对象:java.sql.DataSource接口
//DruidDataSourceFactory是德鲁伊的数据源工厂
// 这个类他实现了sun公司的提供的接口DataSource 返回值是一个DataSource对象 形参是配置文件
//有一个方法:protected DataSource createDataSourceInternal(Properties properties)
//DruidDataSourceFactory数据源工厂
DataSource dataSource = DruidDataSourceFactory.createDataSource(prop);
//获取连接对象
for(int i = 1; i <=11; i++){
Connection connection = dataSource.getConnection();
if(i==3){
//将第三个连接对象释放掉
connection.close() ; //om.mysql.jdbc.JDBC4Connection@17ed40e0
}
System.out.println(connection);
}
}
}
11.引入commons-dbutils工具类库以及使
11.1 简介
commons-dbutils的官网地址: Apache组织机构旗下的开源的工具类库
https://commons.apache.org/proper/commons-dbutils/
就是被用来完成jdbc操作,简化了jdbc操作的一种书写格式(查询多条记录,将这些记录封装List中)
针对原生Jdbc的建议封装
11.2 操作步骤
完成jdbc操作
使用步骤:
1)需要导入核心的jar包 commons-dbtils.jar
mysql驱动jar包
连接池--druid的jar包
junit单元测试:核心包junit.jar以及依赖包
2)有关commons-dbtils.jar 核心接口以及核心类有哪些
使用的执行对象:操作数据库
org.apache.commons.dbutils.QueryRunner 里面封装就是PreparedStatement
两个通用的方法
query(xxx,ResultSetHandler rsh):针对dql语句来完成查询操作
update(xxx,xx):针对dml域操作:insert into,update,delete from ....
核心接口:ResultSetHandler:针对结果集的处理
很多很多实现类
BeanListHandler:可以将查询的多条记录封装到List集合中
BeanHandler:将查询的结果集中某条记录封装到一个Java实体类中
ScalarHandler:查询出的单行单列的数据----查询出总记录数(经常用到) :聚合函数
select count(id字段) from 表名 ; -----封装到Object对象中
11.3 准备工作之工具类
代码:
/* 改进优化:
* 1)从DataSource获取连接对象getConnection(),DataSource替代了DriverManager (连接池获取连接对象)
* 2)读取的连接池的配置文件
* 3)提供静态代码块----加载当前类的时候,直接读取连接池的配置文件,
* 获取的连接池对象---DruidDataSourceFactroy工厂类获取数据源对象
*
*/
public class DruidJdbcUtils {
//成员变量位置:提供ThreadLocal<Connection>:模拟线程,每一个线程使用自己的Connection
private static ThreadLocal<Connection> t1 = new ThreadLocal<>() ;
//声明一个DataSource类型的变量
private static DataSource ds ;
//无参构造私有化:目的外界不能new对象了
private DruidJdbcUtils(){}
//静态代码块
static{
try {
//创建属性集合列表
Properties prop = new Properties() ;
//直接读取连接池的配置文件
InputStream inputStream = DruidJdbcUtils.class.getClassLoader().getResourceAsStream("druid.properties");
//将字节输入流的内容加载属性列表中
prop.load(inputStream) ;
//DruidDataSourceFactroy工厂类获取数据源对象
ds = DruidDataSourceFactory.createDataSource(prop);
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
//封装一个功能:获取数据源
public static DataSource getDataSource(){
return ds;
}
//封装一个功能:获取数据库的连接对象
public static Connection getConnection(){
//1)首先要从当前线程中获取连接对象
try {
Connection conn = t1.get();
//2)判断conn是空的
if(conn==null){
//当前线程中没有开始绑定连接对象
//3)从数据源连接池获取连接对象
conn = ds.getConnection() ;
//4)将当前连接对象绑定给自己的线程中
t1.set(conn);
}
return conn ;
} catch (SQLException e) {
e.printStackTrace();
}
return null ;
}
//封装释放资源
public static void close(PreparedStatement ps,Connection conn){
close(null,ps,conn);
}
public static void close(ResultSet rs, PreparedStatement ps,Connection conn){
if(rs!=null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(ps!=null){
try {
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn!=null){
try {
conn.close();
//需要将自己线程中的连接对象解绑
t1.remove();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
/* public static void main(String[] args) {
// DataSource dataSource = DruidJdbcUtils.getDataSource();
// System.out.println(dataSource);
//获取连接对象
Connection connection = DruidJdbcUtils.getConnection();
System.out.println(connection);
}*/
}
11.4 准备工作之接口(2.5-2.8都是对此接口实现重写的方法代码)
//针对员工的数据访问接口层
public interface EmployeeDao1 {
/**
* 查询所有员工
* @return 返回员工列表
*/
List<Employee> findAll() throws SQLException;
/**
* 根据员工编号获取员工
* @param id 员工编号
* @return 返回就是员工实体类对象
*/
Employee findEmployeeById(int id) throws SQLException;
/**
* 添加员工
* @param employee
*/
void addEmployee(Employee employee) throws SQLException;
/**
* 修改员工
* @param employee 修改员工实体
*/
void updateEmployee(Employee employee) throws SQLException;
/**
* 根据员工编号删除员工
* @param id 员工编号
* @return 影响的行数
*/
int deleteEmployeeById(int id) throws SQLException;
/**
* 获取员工表中总记录数
* @return 返回总条数
*/
int getTotalCount() throws SQLException;
}
11.5 使用ResultSetHandler的实现类BeanListHandler
代码:
public class EmployeeDaoImpl implements EmployeeDao1 {
/**
* 查询所有员工
* @return 返回员工列表
*/
@Override
public List<Employee> findAll() throws SQLException {
//commons-dbutils的使用步骤
//1)导入dbutilsjar包
//2)创建数据库的执行对象
//QueryRunner
//public QueryRunner(DataSource ds)
QueryRunner qr = new QueryRunner(DruidJdbcUtils.getDataSource()) ;
//3)准备sql语句---参数化的sql---查询全部员工数据
String sql = "select * from employee" ;
//4)执行它的通用方法
//query(String sql,ResultSetHandler rsh)
//参数1:sql语句
//参数2:结果集的处理对象:接口
//最终目的将所有的记录封装到List<Employee>中
//public BeanListHandler<T>(Class<T> type) :参数是存储当前类型的字节码文件对象
List<Employee> list = qr.query(sql,
new BeanListHandler<Employee>(Employee.class));
return list;
}
}
11.6 使用ResultSetHandler的实现类BeanHandler
代码
public class EmployeeDaoImpl implements EmployeeDao1 {
/**
* 根据员工编号获取员工
* @param id 员工编号
* @return 返回就是员工实体类对象
*/
@Override
public Employee findEmployeeById(int id) throws SQLException {
//创建执行对象
QueryRunner qr = new QueryRunner(DruidJdbcUtils.getDataSource()) ;
//准备sql语句---参数化的sql
String sql = "select * from employee where id = ?" ;
//执行sql:将查询的这条记录封装到Employee类中
// public <T> T query(String sql, ResultSetHandler<T> rsh, Object... params)
//参数1:sql语句
//参数2:结果集的处理:BeanHandler对象 //有参构造 public BeanHandler<T>(Class<T> class)
//参数3:给占位符号 赋值
Employee employee = qr.query(sql, new BeanHandler<Employee>(Employee.class), id);
return employee;
}
}
11.7 使用ResultSetHandler的实现类ScalarHandler
代码:
public class EmployeeDaoImpl implements EmployeeDao1 {
/**
* 获取员工表中总记录数
* @return 返回总条数
*/
@Override
public int getTotalCount() throws SQLException {
//执行对象
QueryRunner qr = new QueryRunner(DruidJdbcUtils.getDataSource()) ;
//Sql
String sql = "select count(id) from employee" ;
//执行查询操作
//ScalarHandler实现类 ResultSetHandler:查询出的单行单列的数据----查询出总记录数(经常用到) :聚合函数
//public ScalarHandler()
Object obj = qr.query(sql, new ScalarHandler<>()) ;
//String类有万能方法:valueOf(可以传递任意类型包括Object)---->String
String s = String.valueOf(obj);
//在String--->Integer的parseInt(String s)---->int
int totalCount = Integer.parseInt(s);
return totalCount;
}
}
11.8 其他的qr.update方法
/**
* 添加员工
* @param employee
*/
@Override
public void addEmployee(Employee employee) throws SQLException {
//执行对象
QueryRunner qr = new QueryRunner(DruidJdbcUtils.getDataSource()) ;
//sql
String sql = "insert into employee (name,age,gender,address,salary) values (?,?,?,?,?)" ;
//执行更新
//通用的方法
//update(String sql,Object...params)
//参数1:sql
//参数2:给占位符号赋值
int count = qr.update(sql,
employee.getName(),
employee.getAge(),
employee.getGender(),
employee.getAddress(),
employee.getSalary()
);
System.out.println(count);
}
/**
* 修改员工
* @param employee 修改员工实体
*/
@Override
public void updateEmployee(Employee employee) throws SQLException {
//执行对象
QueryRunner qr = new QueryRunner(DruidJdbcUtils.getDataSource()) ;
//sql
String sql = "update employee set name =?,age=?,gender=?,address=?,salary=? where id = ?" ;
//更新
int count = qr.update(sql,
employee.getName(),
employee.getAge(),
employee.getGender(),
employee.getAddress(),
employee.getSalary(),
employee.getId());
System.out.println(count);
}
/**
* 根据员工编号删除员工
* @param id 员工编号
* @return 影响的行数
*/
@Override
public int deleteEmployeeById(int id) throws SQLException {
//执行对象
QueryRunner qr = new QueryRunner(DruidJdbcUtils.getDataSource()) ;
//sql
String sql = "delete from employee where id = ?" ;
//更新
int count = qr.update(sql,id) ;
System.out.println(count);
return count;
}
11.9 总结
qr.ruery这个方法的形参中的接口ResultSetHandler的实现类常用的有三种
BeanListHandler返回一个集合
BeanHandler返回一个对象实例
ScalarHandler返回一个Object对象,使用的时候需要转型
12.JDBC方式管理事务
12.1 什么是事务
事务: 针对关系型数据库的一种机制
就是在执行业务操作过程中,同时执行多个sql或者多张表(添加/删除/修改),这些sql语句要么同时执行成功;
要么同时执行失败;
12.2 举例代码 转账
开启事务: connection.setAutoCommit(false) ;
提交事务: connection.commit();
事务回滚:connection.rollback() ;
public class JDBCTransactionDemo {
public static void main(String[] args) {
//声明Connection类型变量
Connection connection = null ;
PreparedStatement ps = null ;
PreparedStatement ps2 = null ;
try {
//没有通过jdbc管理事务-----当同时执行多条sql,中间如果存在异常,第一条件语句成功了,第二条数据失败;
// 转账业务失败----->应该在jdbc操作转账的业务中加入事务操作!
//使用Jdbc控制事务--->通过获取连接对象之后,加入事务的方法
//通过工具类获取连接对象
connection = DruidJdbcUtils.getConnection();
//开启事务---->利用Connection的功能void setAutoCommit(boolean autoCommit):默认自动提交
//参数为false:禁用自动提交,需要手动提交事务
connection.setAutoCommit(false) ;
//准备sql---参数化sql
String sql = "update account set balance = balance - ? where id = ?" ;
//获取预编译对象
ps = connection.prepareStatement(sql);
//参数赋值
ps.setInt(1,500) ;
ps.setInt(2,1) ;
String sql2 = "update account set balance = balance + ? where id = ?" ;
//获取预编译对象
ps2 = connection.prepareStatement(sql2);
ps2.setInt(1,500) ;
ps2.setInt(2,2);
//分别执行更新操作
int count = ps.executeUpdate();
int i = 10/0 ;
int count2 = ps2.executeUpdate();
System.out.println(count+"---"+count2);
//提交事务: 如果没有问题,提交事务---数据在能永久更新
//Connection对象的方法:void commit()
} catch (Exception e) {
System.out.println("执行catch语句");
//出现异常,程序执行catch语句
//事务回滚
//连接对象的方法void rollback():回滚到默认在更新之前的操作
try {
connection.rollback() ;
} catch (SQLException ex) {
ex.printStackTrace();
}
e.printStackTrace();
/*System.out.println("异常抛出了");*/
}finally {
//任何情况下finally中的代码一定会执行的,除非 只执行这个语句之前,jvm退出了 System.exit(0) ;
try {
connection.commit();
} catch (SQLException e) {
e.printStackTrace();
}
//释放资源
DruidJdbcUtils.close(ps,connection);
DruidJdbcUtils.close(ps2,connection);
}
}
}