博客
关于我
Day112.JDBC技术复习 -JDBC技术
阅读量:328 次
发布时间:2019-03-04

本文共 30338 字,大约阅读时间需要 101 分钟。

JDBC技术

01-JDBC概述

1、数据的持久化:

持久化(persistence):把数据保存到可掉电式存储设备中以供之后使用

2、JDBC的理解:

JDBC(Java Database Connectivity)是一个独立于特定数据库管理系统、通用的SQL数据库存取和操作的公共接口(一组API)

简单理解为: JDBC,是SUN公司提供的一套接口,使用此接口可以实现对具体数据库的操作(获取连接、关闭连接、DML、DDL、TCL)

3、图示理解:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-W38lvKLh-1606232618405)(C:\Users\PePe\AppData\Roaming\Typora\typora-user-images\image-20201124210038074.png)]

好处:

  • 对 程序开发人员,只需要关注对这套接口编程即可,不需要关注每种数据库的底层细节
  • 对 数据库开发商,只需要提供对这套接口标准的实现

4、数据库的驱动

数据库厂商针对于JDBC这套接口,提供的具体实现类的集合

类似于↓

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-R66Q2xoP-1606232618408)(C:\Users\PePe\AppData\Roaming\Typora\typora-user-images\image-20201124211409428.png)]

5、面向接口编程的思想:

JDBC是sun公司提供一套用于数据库操作的接口,java程序员只需要面向这套接口编程即可。

不同的数据库厂商,需要针对这套接口,提供不同实现。不同的实现的集合,即为不同数据库的驱动。 ————面向接口编程

02-数据库的连接

方式一、方式二、方式三、方式四:作为过程迭代存在,了解即可。

方式五:最终版

//获取数据库的连接public static Connection getConncetion() throws Exception {       //1.读取配置文件的4个基本信息    InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.properties");    Properties pros = new Properties();    pros.load(is);    String user = pros.getProperty("user");    String password = pros.getProperty("password");    String url = pros.getProperty("url");    String driver = pros.getProperty("driver");    //2.加载driver    Class.forName(driver);    //3.获取连接    Connection conn = DriverManager.getConnection(url, user, password);    //            System.out.println(conn);//com.mysql.jdbc.JDBC4Connection@68fb2c38    return conn;}

其中,配置文件【jdbc.properties】: 此配置文件声明在工程的src下

user=rootpassword=00000url=jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=truedriver=com.mysql.jdbc.Driver

JDBCUtils.java

/** * 操作数据库的工具类 * @author 阿昌 * @create 2020-11-21 13:40 */public class JDBCUtil {       //获取数据库的连接    public static Connection getConncetion() throws Exception {               //1.读取配置文件的4个基本信息            InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.properties");            Properties pros = new Properties();            pros.load(is);            String user = pros.getProperty("user");            String password = pros.getProperty("password");            String url = pros.getProperty("url");            String driver = pros.getProperty("driver");            //2.加载driver            Class.forName(driver);            //3.获取连接            Connection conn = DriverManager.getConnection(url, user, password);//            System.out.println(conn);//com.mysql.jdbc.JDBC4Connection@68fb2c38            return conn;    }    //关闭连接和Statement的操作    public static void closeResource(Connection conn, PreparedStatement ps){           try {               if (conn != null){                   conn.close();            }        } catch (SQLException throwables) {               throwables.printStackTrace();        }        try {               if (ps !=null){                   ps.close();            }        } catch (SQLException throwables) {               throwables.printStackTrace();        }    }    //关闭资源操作,方法重载    public static void closeResource(Connection conn, PreparedStatement ps, ResultSet resultSet){           try {               if (conn != null){                   conn.close();            }        } catch (SQLException throwables) {               throwables.printStackTrace();        }        try {               if (ps !=null){                   ps.close();            }        } catch (SQLException throwables) {               throwables.printStackTrace();        }        try {               if (resultSet !=null){                   resultSet.close();            }        } catch (SQLException throwables) {               throwables.printStackTrace();        }    }    /*    使用dbutils.jar提供的DbUtils工具类实现资源的关闭     */    public static void closeResource1(Connection conn, PreparedStatement ps, ResultSet resultSet){   //        try {   //            DbUtils.close(conn);//        } catch (SQLException throwables) {   //            throwables.printStackTrace();//        }////        try {   //            DbUtils.close(ps);//        } catch (SQLException throwables) {   //            throwables.printStackTrace();//        }////        try {   //            DbUtils.close(resultSet);//        } catch (SQLException throwables) {   //            throwables.printStackTrace();//        }        DbUtils.closeQuietly(conn);        DbUtils.closeQuietly(ps);        DbUtils.closeQuietly(resultSet);    }}

03-Statement接口实现CRUD操作(了解)

不需要细看(了解即可)// 使用Statement实现对数据表的查询操作	public 
T get(String sql, Class
clazz) { T t = null; Connection conn = null; Statement st = null; ResultSet rs = null; try { // 1.加载配置文件 InputStream is = StatementTest.class.getClassLoader().getResourceAsStream("jdbc.properties"); Properties pros = new Properties(); pros.load(is); // 2.读取配置信息 String user = pros.getProperty("user"); String password = pros.getProperty("password"); String url = pros.getProperty("url"); String driverClass = pros.getProperty("driverClass"); // 3.加载驱动 Class.forName(driverClass); // 4.获取连接 conn = DriverManager.getConnection(url, user, password); st = conn.createStatement(); rs = st.executeQuery(sql); // 获取结果集的元数据 ResultSetMetaData rsmd = rs.getMetaData(); // 获取结果集的列数 int columnCount = rsmd.getColumnCount(); if (rs.next()) { t = clazz.newInstance(); for (int i = 0; i < columnCount; i++) { // //1. 获取列的名称 // String columnName = rsmd.getColumnName(i+1); // 1. 获取列的别名 String columnName = rsmd.getColumnLabel(i + 1); // 2. 根据列名获取对应数据表中的数据 Object columnVal = rs.getObject(columnName); // 3. 将数据表中得到的数据,封装进对象 Field field = clazz.getDeclaredField(columnName); field.setAccessible(true); field.set(t, columnVal); } return t; } } catch (Exception e) { e.printStackTrace(); } finally { // 关闭资源 if (rs != null) { try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } if (st != null) { try { st.close(); } catch (SQLException e) { e.printStackTrace(); } } if (conn != null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } return null; }

Statement使用的弊端

  • 问题一:存在拼串操作,繁琐
Scanner scan = new Scanner(System.in);String username = scan.next();...String sql = "insert into customers(name,email,birth)values('"+ name +"','" +email+ "','"+ birth +"')"
  • 问题二:存在SQL注入问题

SQL 注入问题:

是利用某些系统没有对用户输入的数据进行充分的检查,而在用户输入数据中注入非法的 SQL 语句段或命令(如:SELECT user, password FROM user_table WHERE user=‘a’ OR 1 = ’ AND password = ’ OR ‘1’ = ‘1’) ,从而利用系统的 SQL 引擎完成恶意行为的做法。

SQL注入:

SELECT user, password FROM user_table WHERE user='a' OR 1 = ' AND password = ' OR '1' = '1'

其他问题:

1、Statement没办法操作Blob类型变量

2、Statement实现批量插入操作,效率低

04-PreparedStatement接口替换Statement实现CRUD操作

1、PreparedStatement的理解

①PreparedStatement是Statement的子接口

②PreparedStatement预编译SQL语句

③可以解决Statement的SQL注入问题与拼串问题

2、使用PreparedStatement实现通用的增、删、改的方法: —Version 1.0

//通用的增删改操作public void update(String sql,Object ...args) throws Exception {   //sql中占位符的个数与可变形参的长度相同    Connection conn = null;    PreparedStatement ps = null;    try {           //1.获取数据库连接        conn = JDBCUtil.getConncetion();        //2.预编译sql语句,返回PreparedStatement的实例 ?:占位符        ps = conn.prepareStatement(sql);        //3.填充占位符        for (int i=0;i

3、使用PreparedStatement实现通用的查询操作

//针对不同的表的通用的查询操作,返回表中的一条记录public 
T getInstance(Class
clazz,String sql,Object ...args){ Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; try { conn = JDBCUtil.getConncetion(); ps = conn.prepareStatement(sql); for (int i=0;i
//针对不同的表的通用的查询操作,返回表中的多条记录构成的集合public 
List
getForList(Class
clazz, String sql, Object ...args){ Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; try { conn = JDBCUtil.getConncetion(); ps = conn.prepareStatement(sql); for (int i=0;i
arrayList = new ArrayList<>(); while (rs.next()){ T t = clazz.newInstance(); //处理结果集一行数据中的每一个列:给t对象指定的属性赋值 for (int i=0;i

【总结】

两种思想:

1、面向接口编程思想:

面向SUN定义的接口编程,跟具体的数据库厂商没有关系

**2、ORM编程思想(object relational mapping)**对象关系映射

一个数据表对应一个java类
表中的一条记录对应java类的一个对象
表中的一个字段对应java类的一个属性

两种技术:

1、使用结果集的元数据: ResultSetMetaData		>getColumnCount(): 获取列数		>getColumnLabel(): 获取列的别名			>说明: 如果sql中没有给字段起别名,getColumnLabel()仍然获取的是列名2、反射的使用	①创建对应运行时类的对象	②在运行时动态的调用指定的运行时类的属性、方法

查询的图示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vAD65peV-1606232618411)(C:\Users\PePe\AppData\Roaming\Typora\typora-user-images\image-20201124215358382.png)]

05-PreparedStatement操作Blob类型变量

PreparedStatement可以操作Blob类型变量。

写入操作的方法: setBlob(int index,InputStream is);

读取操作的方法:

Blob blob = getBlob(int index);//或者填写别名

InputStream is = blob.getBinaryStream();

具体的insert:

//向Customers数据表中插入Blob类型字段@Testpublic void testInsert() throws Exception {       Connection conncetion = JDBCUtil.getConncetion();    String sql = "insert into customers(name,email,birth,photo)values(?,?,?,?)";    PreparedStatement ps = conncetion.prepareStatement(sql);    ps.setObject(1,"阿昌");    ps.setObject(2,"achang@qq.com");    ps.setObject(3,"1999-03-09");    FileInputStream fis = new FileInputStream(new File("45.jpg"));    ps.setBlob(4,fis);    ps.execute();    fis.close();    JDBCUtil.closeResource(conncetion,ps);}

具体的query:

//查询数据表Customers中Blob类型字段@Testpublic void testQuery()  {       Connection conn = null;    PreparedStatement ps = null;    InputStream is =null;    FileOutputStream fos=null;    try {           conn = JDBCUtil.getConncetion();        String sql = "select id,name,email,birth,photo from customers where id = ?";        ps = conn.prepareStatement(sql);        ps.setObject(1,20);        ResultSet rs = ps.executeQuery();        if (rs.next()){               //方式一:getXxx(columnIndex)            //            int id = rs.getInt(1);            //            String name = rs.getString(2);            //            String email = rs.getString(3);            //            Date date = rs.getDate(4);            //            //方式二:getXxx(columnLabel)            int id = rs.getInt("id");            String name = rs.getString("name");            String email = rs.getString("email");            Date birth = rs.getDate("birth");            Customer customer = new Customer(id, name, email, birth);            System.out.println(customer);            //将Blob类型的字段下载下来,以文件方式保存在本地            Blob photo = rs.getBlob("photo");            is = photo.getBinaryStream();//读取blob文件的流            fos = new FileOutputStream("阿昌.jpg");//输出下载的目标地址的流            byte[] buffer = new byte[1024];            int len;            while ((len=is.read(buffer)) !=-1){                   fos.write(buffer,0,len);            }        }    } catch (Exception e) {           e.printStackTrace();    } finally {           JDBCUtil.closeResource(conn,ps);        try {               if (fos!=null){                   fos.close();            }            if (is !=null){                   is.close();            }        } catch (IOException e) {               e.printStackTrace();        }    }}

注意:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7qBKmy0d-1606232618414)(C:\Users\PePe\AppData\Roaming\Typora\typora-user-images\image-20201124221100613.png)]

06-PreparedStatement实现高效批量插入

测试使用PreparedStatement实现批量操作:

层次一:使用Statement实现

Connction conn = JDBCUtil.getConnction();Statement st = conn.createStatement();for(int i=1;i<=20000);i++){       String sql = "insert into goods(name)values('name_"+ i +"')"        st.execute(sql);}

层次二:使用PreparedStatement实现替换Statement

@Testpublic void testInsert1() {       Connection conn = null;    PreparedStatement ps = null;    try {           long startTimes = System.currentTimeMillis();        conn = JDBCUtil.getConncetion();        String sql = "insert into goods(name)values(?)";        ps = conn.prepareStatement(sql);        for (int i = 1;i<=20000;i++){               ps.setObject(1,"name_" + i);            ps.execute();        }        long endTimes = System.currentTimeMillis();        System.out.println("花费的时间为: "+(endTimes-startTimes));//20000条---601234    } catch (Exception e) {           e.printStackTrace();    } finally {           JDBCUtil.closeResource(conn,ps);    }}

层次三:使用Batch

1、addBatch()、executeBatch()、ClearBatch()

2、mysql服务器默认是关闭批处理的,我们需要通过一个参数,让mysql开启批处理的支持。
?rewriteBatchedStatements=true 写在配置文件的url后面
3、使用更新的mysql 驱动:mysql-connector-java-5.1.37-bin.jar

@Testpublic void testInsert2() {       Connection conn = null;    PreparedStatement ps = null;    try {           long startTimes = System.currentTimeMillis();        conn = JDBCUtil.getConncetion();        String sql = "insert into goods(name)values(?)";        ps = conn.prepareStatement(sql);        for (int i = 1;i<=1000000;i++){               ps.setObject(1,"name_" + i);            //1."攒"sql            ps.addBatch();            if (i % 500 ==0){                   //2.执行batch                ps.executeBatch();                //3.清空batch                ps.clearBatch();            }        }        long endTimes = System.currentTimeMillis();        System.out.println("花费的时间为: "+(endTimes-startTimes));//20000条---1293,1000000条---44166    } catch (Exception e) {           e.printStackTrace();    } finally {           JDBCUtil.closeResource(conn,ps);    }}

**最终版的代码体现:**层次四:设置连接不运行自动提交数据

//批量插入的方式四: 设置连接不运行自动提交数据@Testpublic void testInsert3() {       Connection conn = null;    PreparedStatement ps = null;    try {           long startTimes = System.currentTimeMillis();        conn = JDBCUtil.getConncetion();        //设置不运行自动提交数据        conn.setAutoCommit(false);        String sql = "insert into goods(name)values(?)";        ps = conn.prepareStatement(sql);        for (int i = 1;i<=1000000;i++){               ps.setObject(1,"name_" + i);            //1."攒"sql            ps.addBatch();            if (i % 500 ==0){                   //2.执行batch                ps.executeBatch();                //3.清空batch                ps.clearBatch();            }        }        //设置自动提交数据        conn.commit();        long endTimes = System.currentTimeMillis();        System.out.println("花费的时间为: "+(endTimes-startTimes));//1000000条---8437    } catch (Exception e) {           e.printStackTrace();    } finally {           JDBCUtil.closeResource(conn,ps);    }}

总结: PreparedStatement与Statement的异同【面试题】

①指出二者的关系? 子接口 与 接口的关系

②开发中,PreparedStatement替换Statement

③PreparedStatement是预编译SQL语句,编译语句后,会被数据库缓存下来,不需要每次都编译

​ Statement每次都需要再编译,就慢了

1、PreparedStatement能最大可能提高性能

2、防止SQL注入问题

3、批量插入数据性能高

4、可操作Blob类型变量

07-数据库的事务

1.事务:

一组逻辑操作单元,使数据从一种状态变到另一种状态     > 一组逻辑操作: 一个或多个DML操作

2.事务处理的原则:

保证所有事务都作为一个工作单元来执行,即使出现了故障,都不能改变这种执行方式。当在一个事务中执行多个操作时,要么所有的事务都被提交(commit),那么这些修改就永久地保存下来;要么数据库管理系统将放弃所作的所有修改,整个事务**回滚(rollback)**到最初状态。

说明:

1.数据一旦提交,就不可回滚1.哪些操作会导致数据的自动提交    >DDL操作一旦执行,都会自动提交        >set_autocommit=false的方式对DDL操作失效    >DML默认情况下,一旦执行,会自动提交        >我们可以通过set_autocommit=false的方式取消DML操作的自动提交    >默认在关闭连接时,会自动提交数据

3.代码的体现:

@Test    public void testUpdateWithTransaction() {           Connection conn = null;        try {               conn = com.achang2.util.JDBCUtil.getConncetion();            //1、设置取消数据的自动提交功能            conn.setAutoCommit(false);            String sql1 = "update user_table set balance = balance - 100 where user = ?";            update(conn,sql1, "AA");            //模拟网络异常            //System.out.println(10/0);            String sql2 = "update user_table set balance = balance + 100 where user = ?";            update(conn,sql2, "BB");            System.out.println("转账成功");            //2、提交数据            conn.commit();        } catch (Exception e) {               e.printStackTrace();            try {                   //3、回滚数据                conn.rollback();            } catch (SQLException throwables) {                   throwables.printStackTrace();            }        } finally {               try {                   //修改其为自动提交数据                //主要针对于使用数据库连接池时的使用                conn.setAutoCommit(true);            } catch (SQLException throwables) {                   throwables.printStackTrace();            }            com.achang2.util.JDBCUtil.closeResource(conn,null);        }

4.考虑到事务以后,实现的通用增删改操作: Version2.0

//通用的增删改操作   ---   Version2.0(考虑数据库事务)public int update(Connection conn,String sql,Object ...args) throws Exception {   //sql中占位符的个数与可变形参的长度相同    PreparedStatement ps = null;    try {           //1.预编译sql语句,返回PreparedStatement的实例 ?:占位符        ps = conn.prepareStatement(sql);        //2.填充占位符        for (int i=0;i

考虑到事务以后,实现的通用查询操作: Version2.0

//通用的查询操作,用于返回数据表中的一条记录(Version2.0: 考虑上事务)public 
T getInstance(Connection conn,Class
clazz,String sql,Object ...args){ PreparedStatement ps = null; ResultSet rs = null; try { conn = JDBCUtil.getConncetion(); ps = conn.prepareStatement(sql); for (int i=0;i

事务的属性

四大属性: ACID

  1. 原子性(Atomicity)

    原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。

  2. 一致性(Consistency)

    事务必须使数据库从一个一致性状态变换到另外一个一致性状态。

  3. 隔离性(Isolation)

    事务的隔离性是指一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。

  4. 持久性(Durability)

    持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来的其他操作和数据库故障不应该对其有任何影响。

数据操作过程中可能出现的为:(针对隔离性)

  • 对于同时运行的多个事务, 当这些事务访问数据库中相同的数据时, 如果没有采取必要的隔离机制, 就会导致各种并发问题:
    • 脏读: 对于两个事务 T1, T2, T1 读取了已经被 T2 更新但还没有被提交的字段。之后, 若 T2 回滚, T1读取的内容就是临时且无效的。
    • 不可重复读: 对于两个事务T1, T2, T1 读取了一个字段, 然后 T2 更新了该字段。之后, T1再次读取同一个字段, 值就不同了。
    • 幻读: 对于两个事务T1, T2, T1 从一个表中读取了一个字段, 然后 T2 在该表中插入了一些新的行。之后, 如果 T1 再次读取同一个表, 就会多出几行。

数据库的四种隔离级别:(一致性和并发性: 一致性越好,并发性越差)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SxpmP6IO-1606232618416)(C:\Users\PePe\AppData\Roaming\Typora\typora-user-images\image-20201124230518492.png)]

  • Oracle 支持的 2 种事务隔离级别:READ COMMITED, SERIALIZABLE。 Oracle 默认的事务隔离级别为: READ COMMITED

  • Mysql 支持 4 种事务隔离级别。Mysql 默认的事务隔离级别为: REPEATABLE READ。

如何查看并设置隔离级别:

  • 每启动一个 mysql 程序, 就会获得一个单独的数据库连接. 每个数据库连接都有一个全局变量 @@tx_isolation, 表示当前的事务隔离级别。

  • 查看当前的隔离级别:

    SELECT @@tx_isolation;
  • 设置当前 mySQL 连接的隔离级别:

    set  transaction isolation level read committed;
  • 设置数据库系统的全局的隔离级别:

    set global transaction isolation level read committed;
  • 补充操作:

    • 创建mysql数据库用户:

      create user tom identified by 'abc123';
    • 授予权限

      #授予通过网络方式登录的tom用户,对所有库所有表的全部权限,密码设为abc123.grant all privileges on *.* to tom@'%'  identified by 'abc123';  #给tom用户使用本地命令行方式,授予atguigudb这个库下的所有表的插删改查的权限。grant select,insert,delete,update on atguigudb.* to tom@localhost identified by 'abc123';

08-DAO及其子类(重点)

DAO: date(base) access Object 数据(库)访问对象

【BaseDAO.java】

/** * 封装了针对数据表的通用操作 * DAO: data(base) access object * @author 阿昌 * @create 2020-11-23 19:02 */public abstract class BaseDAO {       private QueryRunner runner = new QueryRunner();    //通用的增删改操作   ---   Version2.0(考虑数据库事务)    public int update(Connection conn, String sql, Object ...args) {   //sql中占位符的个数与可变形参的长度相同        int count = 0;        try {               count = runner.update(conn, sql, args);        } catch (SQLException throwables) {               throwables.printStackTrace();        }        return count;    }    //通用的查询操作,用于返回数据表中的一条记录(Version2.0: 考虑上事务)    public 
T getInstance(Connection conn,Class
clazz,String sql,Object ...args){ T t = null; try { t = runner.query(conn, sql, new BeanHandler
(clazz), args); } catch (SQLException throwables) { throwables.printStackTrace(); } return t; } //通用的查询操作,用于返回数据表中的多条记录构成的集合(Version2.0: 考虑上事务) public
List
getForList(Connection conn,Class
clazz, String sql, Object ...args){ List
list =null; try { list = runner.query(conn, sql, new BeanListHandler<>(clazz), args); } catch (SQLException throwables) { throwables.printStackTrace(); } return list; } //用于查询特殊值的通用方法 public Object getValue(Connection conn,String sql,Object...args) { Object count = null; try { count = runner.query(conn, sql, new ScalarHandler(), args); } catch (SQLException throwables) { throwables.printStackTrace(); } return count; }}

CustomerDAO

/** * 此接口用来规范针对于Customers表的常用操作 * @author 阿昌 * @create 2020-11-23 19:22 */public interface CustomerDAO {       //将cust对象添加到数据库中    void insert(Connection conn,com.achang2.bean.Customer cust) throws Exception;    //根据指定id,删除表中的一条记录    void deleteById(Connection conn,int id) throws Exception;    //针对于内存中的cust对象,去修改数据表中的记录    void updateById(Connection conn,com.achang2.bean.Customer cust) throws Exception;    //根据指定id查询对应的Customer对象    Customer getById(Connection conn,int id);    //查询表中的所有记录,构成集合    List
getAll(Connection conn); //返回数据表中数据的条目数 Long getCount(Connection conn); //返回数据表中最大的生日 Date getMaxBirth(Connection conn);}

CustomerDAOImpl

/** * @author 阿昌 * @create 2020-11-23 19:36 */public class CustomerDAOImpl extends BaseDAO implements CustomerDAO {       @Override    public void insert(Connection conn, com.achang2.bean.Customer cust) throws Exception {           String sql = "insert into customers(name,email,birth)values(?,?,?)";        update(conn,sql,cust.getName(),cust.getEmail(),cust.getBirth());    }    @Override    public void deleteById(Connection conn, int id) throws Exception {           String sql = "delete from customers where id =?";        update(conn,sql,id);    }    @Override    public void updateById(Connection conn, com.achang2.bean.Customer cust) throws Exception {           String sql = "update customers set name = ?,email = ?,birth = ? where id= ?";        update(conn,sql,cust.getName(),cust.getEmail(),cust.getBirth(),cust.getId());    }    @Override    public Customer getById(Connection conn, int id) {           String sql = "select id,name,email,birth from customers where id = ?";        Customer customer = getInstance(conn, Customer.class, sql, id);        return customer;    }    @Override    public List getAll(Connection conn) {           String sql = "select id,name,email,birth from customers";        List
list = getForList(conn, com.achang2.bean.Customer.class,sql); return list; } @Override public Long getCount(Connection conn) { String sql = "select count(*) from customers"; return (Long) getValue(conn, sql); } @Override public Date getMaxBirth(Connection conn) { String sql = "select max(birth) from customers"; return (Date) getValue(conn,sql); }}

总结: 考虑到事务以后的数据库操作(重点)

1、获取数据库连接

Connection conn = JDBCUtils.getConnection();//方式一: 手动获取连接 方式二:数据库连接池

conn.setAutoCommit(false); //体现事务

2、如下的多个DML操作,作为一个事务出现:

操作①: 需要使用通用的增删改查操作 //通用的增删改查操作如何实现?

操作②: 需要使用通用的增删改查操作 //方式一: 手动使用PreparedStatement实现

操作③: 需要使用通用的增删改查操作 //方式二: 使用dbutil.jar中QueryRunner类

conn.commit();

3、如果出现异常,则:

conn.rollback()

4、关闭资源

JDBCUtils.closeResource(…,…,…,); //方式一: 手动关闭资源 方式二:DbUtils类的关闭方法

09-数据库连接池(需要会使用)

1.传统连接的问题:

  • 普通的JDBC数据库连接使用 DriverManager 来获取,每次向数据库建立连接的时候都要将 Connection 加载到内存中,再验证用户名和密码(得花费0.05s~1s的时间)。需要数据库连接的时候,就向数据库要求一个,执行完成后再断开连接。这样的方式将会消耗大量的资源和时间。**数据库的连接资源并没有得到很好的重复利用。**若同时有几百人甚至几千人在线,频繁的进行数据库连接操作将占用很多的系统资源,严重的甚至会造成服务器的崩溃。
  • **对于每一次数据库连接,使用完后都得断开。**否则,如果程序出现异常而未能关闭,将会导致数据库系统中的内存泄漏,最终将导致重启数据库。(回忆:何为Java的内存泄漏?)—无法被回收的资源
  • 这种开发不能控制被创建的连接对象数,系统资源会被毫无顾及的分配出去,如连接过多,也可能导致内存泄漏,服务器崩溃。

2.如何解决传统开发中的数据库连接问题:

  • 使用数据库连接池

3.使用数据库连接池的好处:

① 资源重用

由于数据库连接得以重用,避免了频繁创建,释放连接引起的大量性能开销。在减少系统消耗的基础上,另一方面也增加了系统运行环境的平稳性。

② 更快的系统反应速度

数据库连接池在初始化过程中,往往已经创建了若干数据库连接置于连接池中备用。此时连接的初始化工作均已完成。对于业务请求处理而言,直接利用现有可用连接,避免了数据库连接初始化和释放过程的时间开销,从而减少了系统的响应时间

③新的资源分配手段

对于多应用共享同一数据库的系统而言,可在应用层通过数据库连接池的配置,实现某一应用最大可用数据库连接数的限制,避免某一应用独占所有的数据库资源

④统一的连接管理,避免数据库连接泄漏

在较为完善的数据库连接池实现中,可根据预先的占用超时设定,强制回收被占用连接,从而避免了常规数据库连接操作中可能出现的资源泄露

自己的话总结:

①提高了程序的相应速度(减少了创建连接相应的时间)

②降低资源的消耗,提供资源利用率(可以重复使用已经提供好的连接)

③便于连接的管理

4.实现的方法:

  • DBCP 是Apache提供的数据库连接池。tomcat 服务器自带dbcp数据库连接池。速度相对c3p0较快,但因自身存在BUG,Hibernate3已不再提供支持。
  • C3P0 是一个开源组织提供的一个数据库连接池,**速度相对较慢,稳定性还可以。**hibernate官方推荐使用
  • Proxool 是sourceforge下的一个开源项目数据库连接池,有监控连接池状态的功能,稳定性较c3p0差一点
  • BoneCP 是一个开源组织提供的数据库连接池,速度快
  • Druid 是阿里提供的数据库连接池,据说是集DBCP 、C3P0 、Proxool 优点于一身的数据库连接池,但是速度不确定是否有BoneCP快

C3P0

导入jar包:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EeESSlCk-1606232618418)(C:\Users\PePe\AppData\Roaming\Typora\typora-user-images\image-20201124233352685.png)]

测试连接的代码:

//使用C3P0的数据库连接池技术//数据库连接池只需要提供一个即可private static ComboPooledDataSource cpds = new ComboPooledDataSource("helloC3p0");public static Connection getConnection1() throws Exception {       Connection conn = cpds.getConnection();    return conn;}

其中,配置文件定义在src下。名为:c3p0-config.xml

com.mysql.jdbc.Driver
jdbc:mysql://localhost:3306/test
root
00000
5
100
10
100
50
2

DBCP

导入jar包:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5i99329B-1606232618419)(C:\Users\PePe\AppData\Roaming\Typora\typora-user-images\image-20201124233619420.png)]

测试连接的代码:

//使用DBCP的数据库连接池技术private static DataSource source;static {       Properties pros = new Properties();    try {           //创建流        FileInputStream is = new FileInputStream("src/dbcp.properties");        pros.load(is);        //创建一个DBCP数据库连接池        source = BasicDataSourceFactory.createDataSource(pros);    } catch (Exception e) {           e.printStackTrace();    }}public static  Connection getConnection2() throws Exception {       Connection conn = source.getConnection();    return conn;}

其中,配置文件定义在src下。名为dbcp.properties

driverClassName=com.mysql.jdbc.Driverurl=jdbc:mysql://localhost:3306/testusername=rootpassword=00000initialSize=10...

Druid(德鲁伊)

导入jar包:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LiHplqvH-1606232618421)(C:\Users\PePe\AppData\Roaming\Typora\typora-user-images\image-20201124233748888.png)]

测试连接的代码:

//Druid数据库连接池技术private static DataSource source1;static {       try {           Properties pros = new Properties();        InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("druid.properties");        pros.load(is);        source1 = DruidDataSourceFactory.createDataSource(pros);    } catch (Exception e) {           e.printStackTrace();    }}public static Connection getConnection3() throws Exception {       //创建Druid数据库连接池    Connection conn = source1.getConnection();    return conn;}

其中,配置文件定义在src下。名为druid.properties

driverClassName=com.mysql.jdbc.Driverurl=jdbc:mysql://localhost:3306/testusername=rootpassword=00000initialSize=5...

10-DBUtils提供jar包实现CRUD操作

导入jar包:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xJLUZdpW-1606232618422)(C:\Users\PePe\AppData\Roaming\Typora\typora-user-images\image-20201124234003022.png)]

使用现成的jar中的QueryRunner测试增删改操作:

//测试插入操作@Testpublic void testInsert() {       Connection conn = null;    try {           QueryRunner runner = new QueryRunner();        conn = JDBCUtil.getConnection3();        String sql = "insert into customers(name,email,birth)values(?,?,?)";        int insertCount = runner.update(conn, sql, "韩信", "Hanxin@qq.com", new Date(5635515L));        System.out.println("添加了 "+insertCount+" 条记录");    } catch (Exception e) {           e.printStackTrace();    } finally {           JDBCUtil.closeResource(conn,null);    }}

使用现成的jar中的QueryRunner测试查询操作:

//测试查询操作    /*    BeanHandler: 是ResultSetHandler接口的实现类,用于封装表中的一条记录     */    @Test    public void testQuery1() {           Connection conn = null;        try {               QueryRunner runner = new QueryRunner();            conn = JDBCUtil.getConnection3();            String sql = "select id,name,email,birth from customers where id = ?";            BeanHandler
handler = new BeanHandler
(Customer.class); Customer cust = runner.query(conn, sql, handler, 20); System.out.println(cust); } catch (Exception e) { e.printStackTrace(); } finally { JDBCUtil.closeResource(conn,null); } } /* BeanListHandler: 是ResultSetHandler接口的实现类,用于封装表中的多条记录构成的集合 */ @Test public void testQuery2() { Connection conn = null; try { QueryRunner runner = new QueryRunner(); conn = JDBCUtil.getConnection3(); String sql = "select id,name,email,birth from customers where id < ?"; BeanListHandler
handler = new BeanListHandler
(Customer.class); List
list = runner.query(conn, sql, handler, 20); list.forEach(System.out::println); } catch (Exception e) { e.printStackTrace(); } finally { JDBCUtil.closeResource(conn,null); } } /*MapHandler: 是ResultSetHandler接口的实现类,对应表中的 一条 记录。 将字段及相应字段的值作为map中的Key和Value */ @Test public void testQuery3() { Connection conn = null; try { QueryRunner runner = new QueryRunner(); conn = JDBCUtil.getConnection3(); String sql = "select id,name,email,birth from customers where id = ?"; MapHandler handler = new MapHandler(); Map
map = runner.query(conn, sql, handler, 20); System.out.println(map); } catch (Exception e) { e.printStackTrace(); } finally { JDBCUtil.closeResource(conn,null); } } /* MapListHandler: 是ResultSetHandler接口的实现类,用于封装表中的多条记录构成的集合 将字段及相应字段的值作为map中的Key和Value。将这些map添加到List中 */ @Test public void testQuery4() { Connection conn = null; try { QueryRunner runner = new QueryRunner(); conn = JDBCUtil.getConnection3(); String sql = "select id,name,email,birth from customers where id < ?"; MapListHandler handler = new MapListHandler(); List
> mapList = runner.query(conn, sql, handler, 20); mapList.forEach(System.out::println); } catch (Exception e) { e.printStackTrace(); } finally { JDBCUtil.closeResource(conn,null); } } /* ScalarHandler: 用于查询特殊值(最大值,最小值,个数 等) */ //查询Customers表中一共有多少条记录 @Test public void testQuery5() { Connection conn = null; try { QueryRunner runner = new QueryRunner(); conn = JDBCUtil.getConnection3(); String sql = "select count(*) from customers "; ScalarHandler handler = new ScalarHandler(); Long count = (Long) runner.query(conn, sql, handler); System.out.println(count); } catch (Exception e) { e.printStackTrace(); } finally { JDBCUtil.closeResource(conn,null); } } /* 自定义ResultHandler实现类 */ @Test public void testQuery6() { Connection conn = null; try { QueryRunner runner = new QueryRunner(); conn = JDBCUtil.getConnection3(); ResultSetHandler
handler = new ResultSetHandler
(){ @Override public Customer handle(ResultSet resultSet) throws SQLException { return null; } }; String sql ="select id,name,birth from customers where id = ?"; Customer cust = runner.query(conn, sql, handler, 23); System.out.println(cust); } catch (Exception e) { e.printStackTrace(); } finally { JDBCUtil.closeResource(conn,null); } }

使用dbutils.jar包中DbUtils工具类实现连接等资源的关闭:

/*    使用dbutils.jar提供的DbUtils工具类实现资源的关闭     */public static void closeResource1(Connection conn, PreparedStatement ps, ResultSet resultSet){       //        try {       //            DbUtils.close(conn);    //        } catch (SQLException throwables) {       //            throwables.printStackTrace();    //        }    //    //        try {       //            DbUtils.close(ps);    //        } catch (SQLException throwables) {       //            throwables.printStackTrace();    //        }    //    //        try {       //            DbUtils.close(resultSet);    //        } catch (SQLException throwables) {       //            throwables.printStackTrace();    //        }    DbUtils.closeQuietly(conn);    DbUtils.closeQuietly(ps);    DbUtils.closeQuietly(resultSet);}

感谢尚硅谷

学习视频
https://www.bilibili.com/video/BV1eJ411c7rf

转载地址:http://rboq.baihongyu.com/

你可能感兴趣的文章
Manjaro 24.1 “Xahea” 发布!具有 KDE Plasma 6.1.5、GNOME 46 和最新的内核增强功能
查看>>
mapping文件目录生成修改
查看>>
MapReduce程序依赖的jar包
查看>>
mariadb multi-source replication(mariadb多主复制)
查看>>
MariaDB的简单使用
查看>>
MaterialForm对tab页进行隐藏
查看>>
Member var and Static var.
查看>>
memcached高速缓存学习笔记001---memcached介绍和安装以及基本使用
查看>>
memcached高速缓存学习笔记003---利用JAVA程序操作memcached crud操作
查看>>
Memcached:Node.js 高性能缓存解决方案
查看>>
memcache、redis原理对比
查看>>
memset初始化高维数组为-1/0
查看>>
Metasploit CGI网关接口渗透测试实战
查看>>
Metasploit Web服务器渗透测试实战
查看>>
MFC模态对话框和非模态对话框
查看>>
Moment.js常见用法总结
查看>>
MongoDB出现Error parsing command line: unrecognised option ‘--fork‘ 的解决方法
查看>>
mxGraph改变图形大小重置overlay位置
查看>>
MongoDB可视化客户端管理工具之NoSQLbooster4mongo
查看>>
Mongodb学习总结(1)——常用NoSql数据库比较
查看>>