JDBC和连接池

持久化和JDBC

持久化

持久化就是把数据保存到可掉电式存储设备中以供以后使用。

大多数情况下,特别是企业级应用,数据持久化意味着将内存中的数据保存到硬盘上加以“固化”,而持久化的实现过程大多通过各种关系数据库来完成的。持久化的主要应用是将内存中的数据存储在关系型数据库中,当然也可以存储在磁盘文件、XML数据文件中。

在Java中,数据库存取技术只能通过JDBC访问数据库。

JDBC访问数据库的形式主要有两种:

  • 直接使用JDBC的API去访问数据库服务器(MySQL/Oracle)
  • 间接地使用JDBC地API去访问数据库服务器

第三方O/R Mapping工具,如Hibernate,MyBatis等(底层依然是JDBC)
JDBC是Java访问数据库地基石,其他技术都是对JDBC地封装。

JDBC

在Java中,访问数据库只能使用JDBC,其他诸如Hibernate等ORM框架或MyBatis等SQL框架,都只是对JDBC的封装

JDBC(Java Database Connectivity)是一个独立于特定数据库管理系统、通用的SQL数据库存取和操作的公共接口(一组API).JDBC本身是java连接数据库的一个标准,是进行数据库连接的抽象层,由java编写的一组类和接口组成,接口的实现由各个数据库厂商来完成。通常将数据库厂商提供的实现叫做驱动程序。

操作指定的数据库,必须先准备指定数据库的驱动才可以。

使用JDBC准备:

  • 拷贝MySQL的驱动包到项目中去:mysql-connector-java-5.1.x-bin.jar
  • build path,告诉项目去哪儿找字节码文件

操作JDBC思路步骤

  1. 加载注册驱动
  2. 获取连接对象
  3. 获取/创建语句对象
  4. 执行SQL
  5. 释放资源

JDBC操作基础上

连接JDBC

@Test
public void testConn() throws Exception{
    String url = "jdbc:mysql://localhost:3306/jdbcdemo";
    String user = "root";
    String password = "admin";
    //将驱动注册到驱动管理器中(其实就是让该类中的静态代码块执行)
    Class.forName("com.mysql.jdbc.Driver");
    //从驱动管理器中获取连接
    Connection connection = DriverManager.getConnection(url, user, password);
}

JDBC的DDL/DML操作

数据库连接被用于向数据库服务器发送命令和SQL语句,在连接建立后,需要对数据库进行访问,执行SQL语句。

在java.sql包中有3个接口分别定义了对数据库的调用的不同方式:

  • Statement(执行静态SQL)
  • PreparedStatement(执行预编译SQL)
  • CallableStatement(执行存储过程)

JDBC和DQL操作

表示数据库结果集的数据表,通常通过执行查询数据库的语句生成。

Result对象具有指向其当前数据行的光标。最初,光标被置于第一行之前。next方法将光标移动到下一行;因为该方法在ResultSet对象没有下一行时返回false,所以可以在while循环中使用它来迭代结果集

ResultSet接口提供用于从当前行获取列值的方法。可以使用列的索引编号或列的名称获取值。一般情况下,使用列索引值较为高效。列从1开始编号。为了获得最大的可移植性,应该从左到右的顺序读取每行中的结果集列,每列只能读取一次

PreparedStatement

PreparedStatement接口是Statement的子接口,它表示一条预编译过的SQL语句。PreparedStatement对象所代表的SQL语句中的参数用问好(?)来表示,调用PreparedStatement对象的setXxx()方法来设置这些参数。setXxx()方法有两个参数,第一个参数是要设置的SQL语句中的参数的索引(从1开始),第二个是设置的SQL语句中的参数的值。

PreparedStatement和Statement的区别

  • PreparedStatement具有代码的可读性和可维护性
  • PreparedStatement能最大可能提高性能

部分数据库服务器会对预编译语句提供性能优化。因为预编译语句有可能会被重复使用,所以语句DB被Server的编译器编译后生成的执行代码被缓存下来,那么下次调用时只要是相同的预编译语句就不需要翻译,只需将参数传入执行代码就会得到执行。

而在Satement语句里,即使是相同操作但因为数据的内容不一样,所以整个语句本身不能匹配,没有缓存语句的意义。实际上没有数据库会把普通语句编译后的执行代码进行缓存。这样每执行一次都要对传入的语句进行编译。

MySQL不支持PreparedStatement性能优化。

PreparedStatement能保证安全性,避免简单的SQL注入。

DAO设计思想

DAO(Data Access Object)是一个数据访问接口,数据访问:顾名思义就是与数据库打交道。夹在业务逻辑与数据库资源中间。

在核心J2EE模式中是这样介绍DAO模式的:为了建立一个健壮的J2EE应用,应该将所有对数据源的访问操作抽象封装在一个公共的API中。

用程序设计的语言来说,就是要建立一个接口,接口中定义了此应用程序中将会用到的所有事务方法。

在应用程序中,当需要和数据源进行交换的时候则使用这个接口,并编写一个单独的类来实现这个接口在逻辑上对应这个特定的数据存储操作。

JDBC操作基础下

JDBC事务

在数据库中,所谓的事务是指一组逻辑操作单元,是数据从一种状态变换到另一种状态。为确保数据库中数据的一致性,数据的操作应当是离散的成组的逻辑单元:当它全部完成时,数据的一致性可以保持,而当这个单元的一部分操作失败,整个事务应用全部视为错误,所有从起始点以后的操作应全部回退到开始状态。

事务的操作:现定义开始一个事务,然后对数据进行修改操作,这时如果提交(),这些数据就永久地保存下来,如果回退,数据库管理系统将放弃您所做地所有修改而回退到开始事务时地状态。

事务处理:保证所有事务都作为一个工作单元来执行,即使出现了故障,都不能改变这种执行方式。当在一个事务中执行多个操作时,要么所提地操作都被提交,要么整个事务回滚到最初地状态。

在JDBC中,事务默认是自动提交的,每执行一个SQL语句时,如果执行成功,就会向数据库自动提交,而不能回滚。

为了让多个SQL语句作为一个事务执行:

  • 调用Connection对象的setAutoCommit(false),以取消自动提交事务
  • 在所有地SQL语句都执行成功后,调用commit()方法,提交事务
  • 出现异常时,调用rollback()方法回滚事务

JDBC批量处理

当需要完成批量插入或者更新记录时。可以采用Java的批量更新机制,这一机制允许多条语句一次性提交给数据库批量处理。通常情况下比单独提交处理更有效率。

JDBC的批量处理语句包括下面两个方法:

  • addBatch():添加需要批量处理的SQL语句作为参数;
  • executeBatch():执行批量处理语句;

addBatch方法对应Statement时是量添加SQL,对于PreparedStatement是批量添加参数

MySQL不支持批量处理,Oracle支持批量处理和PreparedStatement性能优化

BLOB操作

MySQL中,BLOB是一个二进制大型对象,是一个可以存储大量数据的容器,它能容纳不同大小的数据。BLOB实际是个类型系列(TinyBlob、Blob、MediumBlob、LongBlob),除了在存储的最大信息量上不同外,他们是等同的。

可以通过PreparedStatement对象将二进制数据保存到数据库中。

获取自动生成的主键

Statement语句:

int executeUpdate(String sql):执行DML/DDL语句
int executeUpdate(String sql, int autoGeneratedKeys):

    参数:autoGeneratedKeys:是否需要返回自动生成的主键
        Statement.RETURN_GENERATED_KEYS:要返回
        Statement.NO_GENERATED_KEYS:不返回

ResultSet getGeneratedKeys():获取自动生成的主键

PreparedStatement语句:

PreparedStatement preparedStatement(String sql):执行DML/DQL语句
PreparedStatement preparedStatement(String sql, int autoGeneratedKeys):

    参数:autoGeneratedKeys:是否需要返回自动生成的主键
        Statement.RETURN_GENERATED_KEYS:要返回
        Statement.NO_GENERATED_KEYS:不返回

ResultSet getGeneratedKeys():获取自动生成的主键

JDBC连接池

传统模式下获取的连接不能重用,并且可能导致内存泄漏,服务器崩溃。为了避免这种情况我们采用连接池。

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

Druid:阿里巴巴开源的连接池,是Java语言中最好的数据库连接池。Druid能够提供强大的监控和扩展功能。

使用连接池和不使用连接池的区别

获取连接:

不使用连接池:

//使用DriverManager来获取Connection对象
Connection conn = DriverManager.getConnection(url, username, password);

使用连接池:直接找连接池(DataSource)对象,取出Connection即可

Connection conn = DataSource对象.getConnection();

释放连接:

connection对象.close();

使用连接池之前:直接和数据库服务器建立连接关系,而断开也是和数据库服务器断开连接
使用连接池之后:直接和连接池建立连接关系,而断开也是把Connection对象还给连接池,共其他客户端使用。

注意:没有真正的和数据库断开,如此一来,一个Connection对象就得到了充分的利用!

JDBC操作模板

CREATE TABLE `t_bidrequest` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `title` varchar(255) DEFAULT NULL,
  `amount` decimal(10,0) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;

JDBC模板:请移步:https://coding.ee/jdbc_template.html