JDBC完整版

9 篇文章 0 订阅
4 篇文章 0 订阅

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);
        }
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值