一套Java操作数据库的规范

JDBC

SUN公司为了简化、统一对数据库的操作,定义了一套Java操作数据库的规范,称之为JDBC。

JDBC(Java Database Connectivity)API,即Java数据库编程接口,是一组标准的Java语言中的接口和类,使用这些接口和类,Java客户端程序可以访问各种不同类型的数据库。比如建立数据库连接、执行SQL语句进行数据的存取操作。1

原理

JDBC是接口,JDBC驱动才是接口的实现,JDBC驱动由每个数据库厂商开发。

JDBC协议

JDBC的URL=协议名(总是jdbc)+数据库子协议名+数据源名(主机:端口/连接的数据库)。 常见数据库连接:

#mysql
url=jdbc:mysql://127.0.0.1:3306/j2ee?serverTimezone=UTC&generateSimpleParameterMetadata=true
driverClass=com.mysql.cj.jdbc.Driver

#mssql
url=jdbc:sqlserver://127.0.0.1:1433;DatabaseName=j2ee
driverClass=com.microsoft.sqlserver.jdbc.SQLServerDriver

#oracle
url=jdbc:oracle:thin:@//127.0.0.1:1521/orcl
driverClass=oracle.jdbc.driver.OracleDriver

Maven中央仓库没有Oracle驱动。

JDBC的URL中,可以不包含数据库名称,执行conn.setCatalog()可以切换数据库。2

连接数据库

// 注册驱动
Class.forName(driverClass);
// 获取连接
Connection conn = DriverManager.getConnection(url, user, password);

Statement

JDBC无法同时执行多条语句,可以通过简单的;号或其他库解析出SQL脚本再执行,或者通过Mybatis的ScriptRunner类执行SQL文件。5

PreparedStatement

PreparedStatement可以使用预编译的SQL,而Statment只能使用静态的SQL;PreparedStatement可以使用SQL缓存区,效率比Statment高;PreparedStatement可以有效防止SQL注入,而Statment不能防止SQL注入。

CallableStatement

CallableStatement用于执行存储过程。

// 准备sql,第一个?是输入参数,第二个?是输出参数
String sql = "CALL pro_findById2(?,?)";

// 预编译
stmt = conn.prepareCall(sql);

// 设置输入参数
stmt.setInt(1, 2);

// 设置输出参数(注册输出参数),参数一:参数位置;参数二:存储过程中的输出参数的jdbc类型VARCHAR(20)
stmt.registerOutParameter(2, java.sql.Types.VARCHAR);

// 执行存储过程,结果不是返回到结果集中,而是返回到输出参数中
stmt.executeQuery();

// 得到输出参数的值,getXX方法专门用于获取存储过程中的输出参数,索引值为预编译sql中的输出参数的位置
String result = stmt.getString(2);

元数据

DatabaseMetaData,获取数据库基本信息、所有的数据库6、所有的表、存储过程、索引等;ResultSetMetaData,获取查询结果集列名等;ParameterMetaData,获取参数类型等。

事务

事务ACID,原子性,一致性,隔离性,持久性。

隔离引发的问题,脏读,不可重复读,幻读。

事务隔离级别,READ UNCOMMITTED(读未提交),READ COMMITTED(读已提交),REPEATABLE READ(可重复读),SERIALIZABLE(串行化)。

String sql_zs = "UPDATE account SET money=money-1000 WHERE accountName='张三';";
// 该SQL将产生错误
String sql_ls = "UPDATE1 account SET money=money+1000 WHERE accountName='李四';";

try {
    // 设置事务为手动提交,默认为开启的隐士事务
    conn.setAutoCommit(false);

    // 第一次执行SQL
    pstmt = conn.prepareStatement(sql_zs);
    pstmt.executeUpdate();

    // 第二次执行SQL
    pstmt = conn.prepareStatement(sql_ls);
    pstmt.executeUpdate();
} catch (Exception e) {
    // 出现异常,需要回滚事务
    conn.rollback();
} finally {
    // 所有的操作执行成功, 提交事务
    conn.commit();
}

MySQL默认事务隔离级别为:REPEATABLE READ;Oracle默认事务隔离级别为:READ COMMITTED。

MySQL需要设置数据库引擎为InnoDB,否则事务不起作用。

通常在Service层处理事务。

DbUtils组件

DbUtils组件是对JDBC的简单封装,主要方法包括update(Connection conn, String sql, Object param)、update(Connection conn, String sql, Object… param)、batch(Connection conn, String sql, Object[][] params)、query(Connection conn ,String sql, ResultSetHandler rsh, Object... params)等。

// 创建DbUtils核心工具类对象
QueryRunner qr = new QueryRunner();

String sql = "delete from admin where id=?";
qr.update(conn, sql, 2);

String sql = "insert into admin (userName, pwd) values(?,?)";
qr.batch(conn, sql, new Object[][]{ {"jack1", "888"}, {"jack2", "999"} });

/**
 * DBUtils提供多个ResultSetHandler查询结果集接口的实现
 * BeanHandler,查询返回单个对象
 * BeanListHandler,查询返回List对象
 * ArrayHandler,查询返回结果记录的第一行,封装为对象数组, 即返回:Object[]
 * ArrayListHandler,把查询的每一行都封装为对象数组,再添加到list集合中
 * MapListHandler,把查询的每一行都封装为map,再添加到list集合中
 * ScalarHandler,查询返回结果记录的第一行的第一列(在聚合函数统计的时候用)
 * MapHandler,查询返回结果的第一条记录封装为map
 */
String sql = "select * from admin";
List<Admin> list = qr.query(conn, sql, new BeanListHandler<Admin>(Admin.class));

连接池

一次数据库访问对应一个物理连接,每次操作数据库都要打开、关闭该物理连接,系统性能严重受损。

数据库连接池,在每次应用应用程序请求数据库连接时,无需重新打开连接,而是从池中取出已有的连接,使用完后,不再关闭,而是归还。

主流Java数据库连接池:DBCP、C3P0、Tomcat Jdbc Pool、Druid、HikariCP。7

JNDI(Java Naming and Directory Interface),Java命名和目录接口,通过在服务器上配置资源,然后通过统一的方式来获取配置的资源,Tomcat支持配置JNDI连接池。8

数据库操作方式

JDBC、JDBCTemplate(对JDBC的封装)、JPA(Java Persistence API, 即 Java 持久化 API,基于ORM规范)、Hibernate(ORM对象关系映射框架,JPA的一种实现)、Mybatis(半ORM框架)、Spring Data JPA(基于Hibernate)。9