JDBC技术讲解

xiaoxiao2022-06-12  35

文章目录

一、JDBC概述二、JDBC编程2.1 加载驱动2.2 获取连接2.3 执行sql命令2.4 关闭连接2.5 工具类封装 三、Preparedstatement四、数据库连接池4.1 使用连接池的优点4.2 源的数据库连接池4.3Druid数据源的使用 五、批处理六、Blob数据七、事务八、使用Apache提供的DBUtils

一、JDBC概述

在Java中,数据库存取技术可分为如下几类:

JDBC直接访问数据库JDO技术(Java Data Object)第三方O/R工具,如Hibernate, MyBatis等

JDBC是Java访问数据库的基石,JDO、Hibernate等只是更好的封装了JDBC。

什么是JDBC?

JDBC(Java Database Connectivity)是一个独立于特定数据库管理系统(DBMS)、通用的SQL数据库存取和操作的公共接口(一组API),定义了用来访问数据库的标准Java类库,使用这个类库可以以一种标准的方法、方便地访问数据库资源。

JDBC为访问不同的数据库提供了一种统一的途径,为开发者屏蔽了一些细节问题。

JDBC的目标是使Java程序员使用JDBC可以连接任何提供了JDBC驱动程序的数据库系统,这样就使得程序员无需对特定的数据库系统的特点有过多的了解,从而大大简化和加快了开发过程。

如果没有JDBC,那么Java程序访问数据库时是这样的: 现在:

JDBC是SUN公司提供一套用于数据库操作的接口API,Java程序员只需要面向这套接口编程即可。不同的数据库厂商,需要针对这套接口,提供不同实现。不同的实现的集合,即为不同数据库的驱动。

二、JDBC编程

2.1 加载驱动

类加载的几种方式:

使用new创建对象调用类的静态成员加载子类反射

前三种属于编译期加载,是指编译期间,需要检测类文件是否存在,如果不存在,直接编译报错,导致依赖性太强!

第四种属于运行期加载,是指编译期间,不需要检测类文件是否存在,而是运行期间才检测,降低了类的依赖性!

加载驱动方式一:new

DriverManager.registerDriver(new Driver()) DriverManager.registerDriver(new com.mysql.jdbc.Driver());

不足:

属于编译加载,依赖性太强导致Driver对象new了两遍

加载驱动方式二:反射(推荐)

Class.forName("com.mysql.jdbc.Driver");

使用反射方式加载Driver,避免了上述的不足

2.2 获取连接

① 传统的方式一:

// 直接自己拼写url等,维护性较差、容易报错、开发体验较差 @Test public void test1() throws ClassNotFoundException, SQLException { //1.加载驱动 Class.forName("com.mysql.jdbc.Driver"); //2.获取连接 Connection connection= DriverManager.getConnection("jdbc:mysql://localhost:3306/db", "root", "root"); }

② 传统方式二:

将url、driverName、user、password保存在配置文件中,然后通过Properties读取

//创建Properties对象 Properties info = new Properties(); // info.load(new FileInputStream("src\\jdbc.properties")); info.load(this.getClass().getClassLoader().getResourceAsStream("jdbc.properties")); //读取指定键的值 String url = info.getProperty("url"); String user = info.getProperty("user"); String password = info.getProperty("password"); String driverName = info.getProperty("driverName"); //1.加载驱动 Class.forName(driverName); //2.获取连接 Connection connection = DriverManager.getConnection(url,user,password); System.out.println("connected!");

③ Druid数据库连接池

@Test public void test3() throws SQLException { //创建一个数据库连接池 DruidDataSource dds = new DruidDataSource(); //设置参数 dds.setUrl("jdbc:mysql://localhost:3306/customersDB"); dds.setUsername("root"); dds.setPassword("root"); dds.setDriverClassName("com.mysql.jdbc.Driver"); //设置池子中的初始化连接数 dds.setInitialSize(5); //设置池子中的最大连接数 dds.setMaxActive(10); //设置池子中的最小连接数 dds.setMinIdle(5); //获取连接 Connection connection = dds.getConnection(); System.out.println("connected!"); connection.close();//不是断掉连接,而是放回池子中 }

④ Druid数据库连接池方式二(推荐)

@Test public void test4() throws Exception { //使用德鲁伊数据库连接池获取连接方式一: Properties properties = new Properties(); properties.load(this.getClass().getClassLoader().getResourceAsStream("druid.properties")); //1、获取连接池 DataSource dataSource = DruidDataSourceFactory.createDataSource(properties); //2、获取连接 Connection connection = dataSource.getConnection(); System.out.println(connection); //3.关闭连接 connection.close(); }

2.3 执行sql命令

增删改

//1、加载驱动+获取连接 Connection connection = JDBCUtils.getConnection(); //2、执行sql语句 ①获取命令 Statement statement = connection.createStatement(); ②执行sql语句(excuteUpdate:用于增删改) int update = statement.executeUpdate(“delete from customers where id =2); ③处理结果 System.out.println(update>0?”success”:”failure”); //关闭连接 JDBCUtils.close(null, statement, connection);

查询

//1、加载驱动+获取连接 Connection connection = JDBCUtils.getConnection(); //2、执行sql语句 ①获取命令 Statement statement = connection.createStatement(); ②执行sql语句(excuteQuery:用于查询) ResultSet set = statement.executeQuery(“selete * from customers”); ③处理结果 while(set.next()){ //从1开始计数 int id = set.getInt(1); String name = set.getString(2); System.out.println(id+“ ”+name); } //关闭连接 JDBCUtils.close(null, statement, connection);

2.4 关闭连接

statement.close(); connection.close(); set.close();//executeQuery的结果集

2.5 工具类封装

public class JDBCUtils { /** * 功能:获取连接对象 * * @return 可用连接 * @throws Exception */ private static DataSource dataSource; static { try { Properties properties = new Properties(); properties.load(JDBCUtils.class.getClassLoader().getResourceAsStream("druid.properties")); // 1.注册驱动 Class.forName("com.mysql.jdbc.Driver"); // 2.获取连接 dataSource = DruidDataSourceFactory.createDataSource(properties); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } public static Connection getConnection() throws Exception { Connection connection = dataSource.getConnection(); return connection; } public static void close(ResultSet set,Statement statement,Connection connection) throws SQLException { if (set!=null) { set.close(); } if (statement!=null) { statement.close(); } if (connection!=null) { connection.close(); } } }

三、Preparedstatement

使用Statement的不足:

使用+号拼接sql语句,容易导致语法错误、语句不够简洁、可读性差导致sql注入问题

使用PreparedStatement的好处:

正常的sql和变量部分实现分离,提高语句的简洁性和分离性有效避免了sql注入大大减少了编译的次数,提高了效率 //使用PreparedStatement @Test public void test2() throws Exception { String username = "root"; String password = "root"; //1、加载驱动+获取连接 Connection connection = JDBCUtils.getConnection(); //2.执行操作 //获取PreparedStatement对象 PreparedStatement statement = connection.prepareStatement("SELECT COUNT(*) FROM admin WHERE username = ? AND PASSWORD = ?"); //设置问号的值(设置占位符的值),占位符索引从1开始 statement.setString(1, username); statement.setString(2, password); //执行sql语句,返回结果 ResultSet set = statement.executeQuery(); // statement.execute(); // statement.executeUpdate(); set.next(); int count = set.getInt(1); System.out.println(count > 0 ? "login success!": "login failure!"); //关闭连接 JDBCUtils.close(null, statement, connection); }

四、数据库连接池

4.1 使用连接池的优点

普通的JDBC数据库连接使用DriverManager来获取,每次向数据库建立连接的时候都要将 Connection加载到内存中,再验证IP地址,用户名和密码(得花费0.05s~1s的时间)。需要数据库连接的时候,就向数据库要求一个,执行完成后再断开连接。这样的方式将会消耗大量的资源和时间。数据库的连接资源并没有得到很好的重复利用.若同时有几百人甚至几千人在线,频繁的进行数据库连接操作将占用很多的系统资源,严重的甚至会造成服务器的崩溃。

对于每一次数据库连接,使用完后都得断开。否则,如果程序出现异常而未能关闭,将会导致数据库系统中的内存泄漏,最终将导致重启数据库。这种开发不能控制被创建的连接对象数,系统资源会被毫无顾及的分配出去,如连接过多,也可能导致内存泄漏,服务器崩溃。

为解决传统开发中的数据库连接问题,可以采用数据库连接池技术(connection pool)。数据库连接池的基本思想就是为数据库连接建立一个“缓冲池”。预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,只需从“缓冲池”中取出一个,使用完毕之后再放回去。数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是重新建立一个。连接池的最大数据库连接数量限定了这个连接池能占有的最大连接数,当应用程序向连接池请求的连接数超过最大连接数量时,这些请求将被加入到等待队列中。

数据库连接池技术的优点:

资源重用:由于数据库连接得以重用,避免了频繁创建,释放连接引起的大量性能开销。在减少系统消耗的基础上,另一方面也增加了系统运行环境的平稳性更快的系统反应速度:数据库连接池在初始化过程中,往往已经创建了若干数据库连接置于连接池中备用。此时连接的初始化工作均已完成。对于业务请求处理而言,直接利用现有可用连接,避免了数据库连接初始化和释放过程的时间开销,从而减少了系统的响应时间新的资源分配手段:对于多应用共享同一数据库的系统而言,可在应用层通过数据库连接池的配置,实现某一应用最大可用数据库连接数的限制,避免某一应用独占所有的数据库资源统一的连接管理,避免数据库连接泄露:在较为完善的数据库连接池实现中,可根据预先的占用超时设定,强制回收被占用连接,从而避免了常规数据库连接操作中可能出现的资源泄露

4.2 源的数据库连接池

JDBC的数据库连接池使用javax.sql.DataSource来表示,DataSource只是一个接口,该接口通常由服务器(Weblogic、 WebSphere、Tomcat)提供实现,也有一些开源组织提供实现:

DBCP是Apache提供的数据库连接池,速度相对C3P0较快,但因自身存在BUG,Hibernate3已不再提供支持C3P0是一个开源组织提供的一个数据库连接池,速度相对较慢,稳定性还可以Proxool是sourceforge下的一个开源项目数据库连接池,有监控连接池状态的功能,稳定性较C3P0差一点BoneCP是一个开源组织提供的数据库连接池,速度快Druid是阿里提供的数据库连接池,据说是集DBCP、C3P0、Proxool优点于一身的数据库连接池,但是速度不知道是否有BoneCP快

DataSource通常被称为数据源,它包含连接池和连接池管理两个部分,习惯上也经常把 DataSource称为连接池

注意:

数据源和数据库连接不同,数据源无需创建多个,它是产生数据库连接的工厂,因此整个应用只需要一个数据源即可当数据库访问结束后,程序还是像以前一样关闭数据库连接:conn.close(),但conn.close()并没有关闭数据库的物理连接,它仅仅把数据库连接释放,归还给了数据库连接池

4.3Druid数据源的使用

Druid是阿里巴巴开源平台上一个数据库连接池实现,它结合了C3P0、DBCP、Proxool等DB池的优点,同时加入了日志监控,可以很好的监控DB池连接和SQL的执行情况,可以说是针对监控而生的DB连接池,据说是目前最好的连接池

配置文件:

driverClassName=com.mysql.jdbc.Driver #url=jdbc:mysql://localhost:3306/db?rewriteBatchedStatements=true url=jdbc:mysql://localhost:3306/db username=root password=1234 initialSize=10 minIdle=5 maxActive=20 maxWait=5000

代码:

Properties properties = new Properties(); properties.load(this.getClass().getClassLoader().getResourceAsStream("druid.properties")); //1、获取连接池 DataSource dataSource = DruidDataSourceFactory.createDataSource(properties); //2、获取连接 Connection connection = dataSource.getConnection(); //3.关闭连接 connection.close();

五、批处理

相关API:addBatch()->executeBatch()->clearBatch()

情况1:多条sql语句的批量执行【较少使用】 Statement+批处理:提高了执行的效率,并没有减少编译的次数

情况2:一条sql语句的批量传参【较多使用】 PreparedStatement+批处理:减少了执行的次数,也减少了编译的次数,大大提高效率

注意:使用批处理 ,数据库url后要添加?rewriteBatchedStatements=true

/使用批处理 @Test public void testUseBatch() throws Exception { //1.获取连接 Connection connection = JDBCUtils.getConnection(); //2.执行插入 PreparedStatement statement = connection.prepareStatement("insert into admin values(?,?,?)"); for(int i=1;i<=50000;i++) { statement.setInt(1, i); statement.setString(2, "john"+i); statement.setString(3, "0000"); //添加到批处理包(放到框中) statement.addBatch(); if(i%1000==0) { statement.executeBatch();//执行批处理包的sql(将框中的苹果们运到了楼上) statement.clearBatch();//清空批处理包的sql(卸货) } } //3.关闭 JDBCUtils.close(null, statement, connection); }

六、Blob数据

只能使用PreparedStatement实现Blob类型的读写,不能使用Statement

相关API:

setBlob(占位符索引,InputStream)InputStream is =getBinaryStream(列索引) //测试Blob类型数据的写入:修改小苍的照片为指定照片 @Test public void test1() throws Exception { //1.获取连接 Connection connection = JDBCUtils.getConnection(); //2.执行修改 PreparedStatement statement = connection.prepareStatement("update customers set photo = ? where name = ?"); statement.setBlob(1, new FileInputStream("E:\\beauty\\cang.jpg")); statement.setString(2, "小苍"); int update = statement.executeUpdate(); System.out.println(update>0?"插入成功":"插入失败"); //3.关闭 JDBCUtils.close(null, statement, connection); } //测试Blob类型数据的读取:将小苍的图片读取到项目的根目录下 @Test public void test2() throws Exception { //1.获取连接 Connection connection = JDBCUtils.getConnection(); //2.执行查询 PreparedStatement statement = connection.prepareStatement("select photo from customers where name = ?"); statement.setString(1, "小苍"); ResultSet set = statement.executeQuery(); if(set.next()) { // Blob blob = set.getBlob(1); // InputStream binaryStream = blob.getBinaryStream(); InputStream inputStream = set.getBinaryStream(1); FileOutputStream fos = new FileOutputStream("src\\beauty.jpg"); //边读边写:复制图片 byte[] b = new byte[1024]; int len; while((len=inputStream.read(b))!=-1) { fos.write(b, 0, len); } fos.close(); inputStream.close(); } //3.关闭连接资源 JDBCUtils.close(set, statement, connection); }

七、事务

//使用事务 @Test public void testTransaction() throws Exception { //1.获取连接 Connection connection = null; //事务使用步骤二:编写事务的语句 //2.执行增删改查 PreparedStatement statement = null; try { connection = JDBCUtils.getConnection(); //事务使用步骤一:开启事务 connection.setAutoCommit(false);//取消了事务的自动提交+开启事务 statement = connection.prepareStatement("update account set balance = ? where username = ?"); statement.setDouble(1, 995); statement.setString(2, "冯绍峰"); statement.executeUpdate(); int i=1/0; //操作2:赵丽颖的钱多5块 statement.setDouble(1, 1005); statement.setString(2, "赵丽颖"); statement.executeUpdate(); //事务使用步骤三:结束事务 connection.commit();//如果执行到该处,说明上面操作没有异常,则可以正常提交事务 } catch (SQLException e) { try { connection.rollback();//如果执行到该处,说明try块操作有异常,则需要回滚! } catch (SQLException e1) { e1.printStackTrace(); } } finally { //3.关闭连接 JDBCUtils.close(null, statement, connection); } }

八、使用Apache提供的DBUtils

一般只是用到了DBUtils的执行sql语句,加载驱动还是用Class.forname(com.mysql.jdbc.driver),获取连接是用Druid连接池来获取来接(DruidDataSourceFactory.getConnection),通常加载驱动、获取连接、关闭连接都是封装在Utils下。

QueryRunner类封装了SQL的执行,是线程安全的。

可以实现增、删、改、查、批处理考虑了事务处理需要共用Connection该类最主要的就是简单化了SQL查询,它与ResultSetHandler组合在一起使用可以完成大部分的数据库操作,能够大大减少编码量

重要的API:

QueryRunner下的update(执行增删改)和query(执行查询)

query的构造器:qr.query(connection, sql, new ResultSetHandler(),objects);

ResultSetHandler的实现类:

BeanHandler:用于查询单行BeanListHandler:用于查询多行ScalarHandler:用于查询单个值 public class BasicDaoByUtils<T> { QueryRunner qr = new QueryRunner(); public int update(String sql,Object...objects) { /** * 功能:执行增删改 */ Connection connection = null; try { connection = JDBCUtils.getConnection(); return qr.update(connection, sql, objects); } catch (Exception e) { throw new RuntimeException(e); } finally { JDBCUtils.close(null, null, connection); } } public T querySingle(Class<T> clazz,String sql,Object...objects) { Connection connection = null; try { connection = JDBCUtils.getConnection(); return qr.query(connection, sql, new BeanHandler<T>(clazz),objects); } catch (Exception e) { throw new RuntimeException(e); } finally { JDBCUtils.close(null, null, connection); } } /** * 功能:查询多条 */ public List<T> queryMulti(Class<T> clazz,String sql,Object...objects) { Connection connection = null; try { connection = JDBCUtils.getConnection(); return qr.query(connection, sql, new BeanListHandler<T>(clazz),objects); } catch (Exception e) { throw new RuntimeException(e); } finally { JDBCUtils.close(null, null, connection); } } /** * 功能:查询单个值 */ public Object scalar(String sql,Object...objects) { Connection connection = null; try { connection = JDBCUtils.getConnection(); return qr.query(connection, sql, new ScalarHandler(),objects); } catch (Exception e) { throw new RuntimeException(e); } finally { JDBCUtils.close(null, null, connection); } } }
转载请注明原文地址: https://www.6miu.com/read-4932281.html

最新回复(0)