• 持久化(persistence):把数据保存到可掉电式存储设备中以供之后使用,持久化的实现过程大多通过各种关系型数据库来完成
  • JPA(Java Persistence API):Java 的持久化 API,为对象关系映射提供了一种基于 POJO 的持久化模型
  • JPA 的实现:Hibernate、Toplink、OpenJPA、Eclipselink 等
  • Spirng Data JPA 是 Spring 提供的一套简化 JPA 开发的框架,可以理解为对 JPA 规范的再次封装抽象
  • JDBC 即 Java 数据库连接(Java DataBase Connectivity),一组可以执行 SQL 语句的 Java API(Java 数据库的统一访问接口)

JDBC 完成 CRUD 操作

  • 在 java.sql 包下

  • 步骤

    • 加载数据库驱动,使用 DriverManager 获取连接对象
    • 使用 Connection 创建 PreparedStatement 语句对象
    • 设置 SQL 语句中的参数值,执行 SQL 语句
    • 操作结果集
    • 回收数据库资源,包括关闭 ResultSet、Statement 和 Connection 等资源
  1. Class.forName("com.mysql.jdbc.Driver"); // 从 Java6(JDBC 4.0)开始,无需加载数据库驱动
  2. Connection conn = DriverManager.getConnection(String url, String user, String password); // 建立到指定数据库 URL 的连接
  3. // url = "jdbc:mysql://localhost:3306/mydb"
  4. // 如果连接的数据库服务器在本机上,并且端口是3306,则可以简写:url = "jdbc:mysql:///mydb"
  • 使用 PreparedStatement 比使用 Statement 的好处:

    • 无须“拼接”SQL 语句,编程更简单
    • 预编译 SQL 语句,性能更好(MySQL 不支持 PreparedStatement 的性能优化)
    • 可以防止 SQL 注入,安全性更好
  • Configuration Properties for Connector/J 常见参数:user、password、useSSL、serverTimezone、autoReconnect、rewriteBatchedStatements

常用 API

Connection 接口

  • Connection 接口:代表数据库连接对象(一个物理连接会话)

  • 创建语句对象Statement createStatement():创建一个 Statement 对象PreparedStatement prepareStatement(String sql):创建一个 PreparedStatement 对象PreparedStatement prepareStatement(String sql, int autoGeneratedKeys):创建一个 PreparedStatement 对象,并设置该对象是否能获取自动生成的主键(int 常量:Statement.RETURN_GENERATED_KEYS)

  • 控制事务void setAutoCommit(boolean autoCommit):false 为关闭自动提交,开启事务(MySQL 默认打开自动提交)void commit():提交事务,并释放所持有的数据库锁void rollback():回滚事务,并释放所持有的数据库锁(在 catch 块中回滚事务)

Statement 接口

  • Statement 接口:用于执行静态 SQL 语句(将 SQL 语句发送到数据库)

  • int 常量:RETURN_GENERATED_KEYS、NO_GENERATED_KEYS

  • 执行语句int executeUpdate(String sql):执行 DML 语句时返回受影响的行数;执行 DDL 语句时返回 0int executeUpdate(String sql, int autoGeneratedKeys):执行给定的 DML 语句,并设置此 Statement 生成的自动生成键是否能用于获取ResultSet executeQuery(String sql):执行 DQL 语句,并返回査询结果对应的 ResultSet 对象

  • 批量更新void addBatch(String sql):将给定的 SQL 命令添加到此 Statement 对象的当前命令列表中int[] executeBatch():执行一批命令void clearBatch():清空此 Statement 对象的当前 SQL 命令列表

  • 获取自动生成主键ResultSet getGeneratedKeys():获取由于执行此 Statement 对象而创建的所有自动生成的主键

PreparedStatement 接口

  • PreparedStatement 接口,Statement 的子接口,用于执行带占位符(?)参数的 SQL 语句

  • 给参数设值即添加到批处理viod setXxx(int parameterIndex, Xxx value):根据索引(从 1 开始)将 SQL 语句中指定位置的参数设置为 value 值,如果不清楚预编译 SQL 语句中各参数的类型,可以使用 setObject() 方法来传入参数void setBlob(int parameterIndex, InputStream inputStream):将指定参数设置为输入流对象void addBatch():将一组参数添加到此 PreparedStatement 对象的批处理命令中(调用前需先为参数设置)

  • 执行语句int executeUpdate():执行 DML 语句或 DDL 语句(无须接收 SQL 字符串)ResultSet executeQuery():执行 DQL 语句(无须接收 SQL 字符串)

ResultSet 接口:结果集对象

boolean next():将光标从当前行移动到下一行(光标的初始位置是第一行之前)Xxx getXxx(int columnIndex):获取当前行中的指定列索引(从 1 开始)的数据Xxx getXxx(String columnName):获取当前行中的指定列名的数据

  • Xxx 表示 Java 中与当前列的数据类型对应的数据类型,也可以用 getObject() 获取任意类型的值,或 getString() 方法获取除 Blob 之外的任意类型列的值

接口 Blob

InputStream getBinaryStream():以流的形式获取此 Blob 实例指定的 BLOB 值

SQL数据类型和Java数据类型对比
图 1 SQL数据类型和Java数据类型对比

常用 JPA 注解

  • javax.persistence
  • 实体
    • @Entity:标明该类是一个实体类
    • @MappedSuperclass
    • @Table(name):定义映射的表
  • 主键
    • @Id:标明主键
    • @GeneratedValue(strategy, generator):主键生成策略,如 @GeneratedValue(strategy = GenerationType.IDENTITY),依赖于数据库递增的策略
    • @SequenceGenerator (name, sequenceName)
  • 映射
    • @Column (name, nullable, length, insertable, updatable):定义属性和表的映射关系
    • @Enumerated
    • @JoinTable(name)、@JoinColumn(name)
    • @Convert(converter):指定使用的转换器(自定义转换器实现 AttributeConverter
  • 关系
    • @OneToOne、@OneToMany、@ManyToOne、@ManyToMany
    • @OrderBy

Hibernate 常用注解

  • org.hibernate.annotations
  • @CreationTimestamp、@UpdateTimestamp
  • @Type(type、parameters)

DAO 设计

  • DAO(Data Access Object):一个数据访问接口,夹在业务逻辑与数据库资源中间

  • 分包规范:domain:存放实体类 / 模型对象, Xxxdao:存放 DAO 接口,IXxxDAO,对实体类对象的 CRUD 的封装dao.impl:存放 DAO 实现类,XxxDAOImplutil:存放工具类,JdbcUtils,包括:在初始化时加载配置文件创建 DataSource 对象、获取连接对象、释放资源test:存放 DAO 测试类,XxxDAOTest

事务处理

  • 隐式提交事务:正常退出或者运行 DDL、DCL 语句
  • 隐式自动回滚:Connection 遇到一个未处理的 SQLException 异常,系统非正常退出(但如果程序捕获了该异常,则需要在异常处理块中显式地回滚事务)

  • 操作事务的模板:

  1. try {
  2. connection对象.setAutoCommit(false); // 取消事务自动提交
  3. DML 操作1
  4. DML 操作2
  5. ...
  6. Connection对象.commit(); // 提交事务
  7. } catch(Exception e) {
  8. // 处理异常
  9. Connection对象.rollback(); // 回滚事务
  10. } finally {
  11. 释放资源
  12. }

批处理操作

  • MySQL 不支持批量处理,但在新的JDBC驱动中,可以通过在 URL 连接中设置参数来优化:rewriteBatchedStatements=true

  • 步骤:

    • 将给定的 SQL 命令添加到此 Statement 对象的当前命令列表中,或者设置 SQL 语句中指定位置的参数值后添加到此 PreparedStatement 对象的当前命令列表中
    • 执行批处理
    • 清除缓存

大数据类型处理

  • 将 Blob 类型数据插入数据库需要调用 PreparedStatement 对象将指定参数设置为输入流对象的方法

  • 从 ResultSet 里取出 Blob 数据

    • 调用 ResultSet 的 getBlob(int columnlndex) 方法,获取一个 Blob 对象
    • 调用 Blob 对象的 getBinaryStream() 方法来获取该 Blob 数据的输入流,或调用 Blob 对象的 getBytes()方法直接取出该 Blob 对象封装的二进制数据

获取保存数据时自动生成的主键

调用 Statement 接口中的ResultSet getGeneratedKeys() 方法,获取由于执行此 Statement 对象而创建的所有自动生成的主键

使用连接池管理连接

  • 当应用程序启动时,系统主动建立足够的数据库连接,并将这些连接组成一个连接池
  • 每次应用程序请求数据库连接时,无须重新打开连接,而是从连接池中取出已有的连接使用,使用完后不再关闭数据库连接,而是直接将连接归还给连接池

  • Connection Pool

  • javax.sql.DataSource 接口,表示 JDBC 的数据库连接池

  • 常用 DataSource 的实现:DBCP、C3P0、Druid、Hikari

  1. // 使用连接池获取 Connection 对象
  2. Connection conn = DataSource对象.getConnection();
  3. // 释放数据库连接
  4. conn.close();

获取 DataSource 对象

DBCP 连接池

  • 需要的 jar 包:commons-dbcp.jar、commons-pool.jar
  • Tomcat 内置的数据库连接池也是 DBCP
  1. // 通过连接池工厂创建连接池对象
  2. // 配置文件中的 key 名要对应 BasicDataSourceFactory 类中字符串常量
  3. DataSource ds = BasicDataSourceFactory.createDataSource(Properties properties);

Druid 连接池

  1. driverClassName=com.mysql.jdbc.Driver
  2. url=jdbc:mysql://localhost:3306/jdbcdemo?useSSL=false&rewriteBatchedStatements=true&serverTimezone=Asia/Shanghai
  3. username=root
  4. password=admin
  5. # 最大连接数
  6. maxActive=10
  1. // 通过连接池工厂创建连接池对象
  2. // 配置文件中的 key 名要对应 DruidDataSourceFactory 类中字符串常量
  3. DataSource ds = DruidDataSourceFactory.createDataSource(Properties properties);

重构的思想

  • 把相同的结构提取出去,把不同内容的使用参数表示

  • JDBC 操作模板:JdbcTemplate

  • 把处理结果集的具体行为交给每一个 DAO 的实现类

  • 结果集处理器接口:IResultSetHandler

  • 结果集处理器实现类:

    • BeanHanlder:把结果集中的第一条记录封装成一个 JavaBean 对象
    • BeanListHandler:把结果集中的每一条记录都封装成一个 JavaBean 对象,再把多个 JavaBean 对象存储到 List 集合中
  • 此时要保证:表中的列名和 JavaBean 中的属性名相同,表中的列类型和 JavaBean 中属性的类型对应
  • 通过内省机制获取属性名(列名)、属性值

  • 使用注解或 XML 文件处理表名和类名、属性名和列名不同的情况(映射)