JDBC学习笔记…
目录
JDBC概述
基本介绍:
JDBC为访问不同的数据库提供了统一的接口,为使用者屏蔽细节问题;通过使用JDBC,可以连接任意提供了JDBC驱动程序的数据库系统,从而完成对数据库的操作。
获取数据库连接(mysql8.0)
//通过配置文件灵活获取信息(用户名、密码、驱动、连接)
Properties properties = new Properties();
properties.load(new FileInputStream("src\\mysql.properties"));
String user = properties.getProperty("user");
String password = properties.getProperty("password");
String driver = properties.getProperty("driver");
String url = properties.getProperty("url");
Class.forName(driver);//通过反射完成自动注册
Connection connection = DriverManager.getConnection(url, user, password);
Statement statement = connection.createStatement();
String sql1 = "insert into news values(1,'tom','男')";
int row1 = statement.executeUpdate(sql1);//执行sql语句(DML语句)
statement.close();//关闭连接资源
connection.close();
ResultSet结果集
基本介绍:
- 表示数据库结果集的数据表,通常通过执行DDL语句生成
- ResultSet对象保持一个光标指向当前数据行。起始位置位于第一行数据之前
- next方法将光标移动到下一行,若返回 false 代表对象中没有更多行。previous方法将光标移动到上一行。
- getXxx方法可以根据列的索引或列名返回对应列的值;getObject方法同理,但返回Object类型
Statement
基本介绍:
- Statement对象指执行静态SQL语句并返回其生成的结果的对象
- 连接建立后,访问数据库有以下三种方式:
- Statement:存在SQL注入问题
- PreparedStatement:预处理
- CallableStatement:存储过程
- SQL注入是Statement对象执行SQL语句存在的问题,即利用某些系统没有对用户输入的数据进行充分的检查,而在用户输入数据中注入非法的SQL语句段或命令,进而使数据库受到攻击和破坏。
- 使用PreparedStatement可以防范SQL注入
PreparedStatement
基本介绍:
- 执行的SQL语句中的参数用问号表示,通过调用setXxx方法来设置这些参数,问号有对应的索引(从1开始)
- 调用executeQuery() 返回ResultSet对象
- 调用executeUpdate() 执行增删改
- 优点:不再使用 +号拼接sql语句,减少语法错误;有效解决sql注入问题;减少编译次数,提高效率(预处理)
封装JDBCUtils
基本介绍:
通常我们需要频繁的获取连接和关闭连接,我们可以将这类操作封装起来,用到该类操作使用工具类即可
//工具类,完成对mysql的连接和关闭
public class JdbcUtils {
private static String url;
private static String user;
private static String password;
private static String driver;
//使用静态代码块进行初始化
static {
try {
Properties properties = new Properties();
properties.load(new FileInputStream("src\\mysql.properties"));
user = properties.getProperty("user");
password = properties.getProperty("password");
driver = properties.getProperty("driver");
url = properties.getProperty("url");
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static Connection getConnection() {
try {
return DriverManager.getConnection(url, user, password);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public static void close(ResultSet resultSet, Statement statement, Connection connection) {
try {
if (resultSet != null) {
resultSet.close();
}
if (statement != null) {
statement.close();
}
if (connection != null) {
connection.close();
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
事务
基本介绍:
- JDBC程序中,Connection对象创建时,默认情况下会自动提交事务,每成功执行一个sql语句,就会向数据库自动提交,而不能使用回滚
- 使用Connection中的setAutoCommit方法可以取消自动提交提交事务(设为false),即开启事务
- 在所有sql语句成功执行后,使用commit方法提交,使用rollback方法回滚
批处理
基本介绍:
- 有时需要我们进行大量插入或更新记录的操作,我们可以使用java的批量更新机制,将多条语句一次性提交给数据库进行批量处理,这样做效率更高
- 常用方法:
- addBatch():添加需要批量处理的sql语句或参数;
- executeBatch():执行批量处理语句;
- clearBatch():清空批处理包的语句
- 使用批处理时,url中需要添加参数:?rewriteBatchedStatements=true
- 批处理常与PreparedStatement搭配使用,减少编译次数的同时减少运行次数,大大提高效率
数据库连接池
传统JDBC连接数据库的弊端:
- 使用DriverManager获取连接,每次向数据库建立连接时,都需要将Connection加载到内存中,再验证IP地址、用户名和密码。当频繁进行数据库连接操作时,会大量占用系统资源,容易造成服务器崩溃
- 每次数据库连接使用结束后需要关闭连接,若程序出现异常,连接未能正常关闭时,会导致数据库内存泄漏,最终导致重启数据库
- 无法控制连接数量
数据连接池可以解决以上问题:
- 提供连接池维护与mysql的连接,连接池控制初始连接数量、最小连接数量和最大连接数量,可以根据需要对连接数量及时扩容
- 当程序需要建立连接时,从连接池中获取链接,使用完毕释放连接即可(连接可被复用);若连接均被占用,则加入等待队列
- 连接池种类:C3P0(稳定性高)、DBCP、Proxool、BoneCP、Druid德鲁伊
德鲁伊工具类
集多种连接池优点于一身的连接池,速度快
public class DruidUtils {
private static DataSource ds;
//利用静态代码块对ds进行初始化
static {
Properties properties = new Properties();
try {
properties.load(new FileInputStream("src\\druid.properties"));
ds = DruidDataSourceFactory.createDataSource(properties);
} catch (Exception e){
e.printStackTrace();
}
}
//获取连接
public static Connection getConnection() throws SQLException {
return ds.getConnection();
}
//释放连接
public static void close(ResultSet resultSet, Statement statement,Connection connection){
try {
if (resultSet != null) {
resultSet.close();
}
if (statement != null) {
statement.close();
}
if (connection != null) {
connection.close();
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
//配置文件中的参数设置
#key=value
driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true
#url=jdbc:mysql://localhost:3306/girls
username=root
password=1234
#initial connection Size
initialSize=10
#min idle connecton size
minIdle=5
#max active connection size
maxActive=50
#max wait time (5000 mil seconds)
maxWait=5000
Apache-DBUtils
使用ResultSet结果集存在的问题:
- 关闭Connection后,ResultSet结果集无法再使用
- ResultSet结果集不利于数据的管理
解决办法:
- 创建JavaBean对象保存每一条查询记录中字段对应的值
- 将所有的记录保存在ArrayList中,限制保存类型为对应的JavaBean类
- 这样对结果集进行了封装,并可以复用
========》DBUtils实现了以上的功能
DButils基本介绍:
- 开源的JDBC工具类库,可以极大简化jdbc编码的工作量
- DBUtils:
- QueryRunner类:封装了SQL的执行,线程安全,可以实现CRUD和批处理
- ResultHandler接口:用于处理ResultSet结果集,将数据按要求转换成另一种形式
具体工具类:
- ArrayHandler:把结果集中的第一行数据转成对象数组。
- ArrayListHandler:把结果集中的每一行数据都转成一个数组,再存放到List中。
- BeanHandler:将结果集中的第一行数据封装到一个对应的JavaBean实例中。
- BeanListHandler:将结果集中的每一行数据都封装到一个对应的JavaBean实例中,存放到List里。
- ColumnListHandler:将结果集中某一列的数据存放到List中。
- KeyedHandler(name):将结果集中的每行数据都封装到Map里,再把这些map再存到一个map里,其key为指定的key。
- MapHandler:将结果集中的第一行数据封装到一个Map里,key是列名,value就是对应的值。
- MapListHandler:将结果集中的每一行数据都封装到一个Map里,然后再存放到List
DBUtils和德鲁伊连接池的实现:
//先创建好和表对应的Ja
public void testDB() throws SQLException {
Connection connection = DruidUtils.getConnection();
QueryRunner queryRunner = new QueryRunner();
String sql = "select * from emp where deptno = ?";
//传入参数,这里可以根据需要使用各种不同的Handler
//query方法执行DQL语句 update方法执行DML语句
//int row = queryRunner.update(connection,sql,params...);
List<emp> empList = queryRunner.query(connection, sql, new BeanListHandler<>(emp.class), 10);
for (emp emp:empList) {
System.out.println(emp);
}
DruidUtils.close(null,null,connection);
}
//输出
/*emp{empno=7782, ename='CLARK', job='MANAGER', mgr=7839, hiredate=1981-06-09, sal=2695.0, comm=null, deptno=10}
emp{empno=7839, ename='KING', job='PRESIDENT', mgr=null, hiredate=1981-11-17, sal=5500.0, comm=null, deptno=10}
emp{empno=7934, ename='MILLER', job='CLERK', mgr=7782, hiredate=1982-01-23, sal=1430.0, comm=null, deptno=10} */
query方法源码分析:
private <T> T query(Connection conn, boolean closeConn, String sql, ResultSetHandler<T> rsh, Object... params) throws SQLException {
if (conn == null) {
throw new SQLException("Null connection");
} else if (sql == null) {
if (closeConn) {
this.close(conn);
}
throw new SQLException("Null SQL statement");
} else if (rsh == null) {
if (closeConn) {
this.close(conn);
}
throw new SQLException("Null ResultSetHandler");
} else {
PreparedStatement stmt = null;//定义PreparedStatement
ResultSet rs = null;//定义ResultSet用于接收返回的结果集
Object result = null;//用于接收返回的Arraylist
try {
stmt = this.prepareStatement(conn, sql);//创建PreparedStatement对象
this.fillStatement(stmt, params);//设置sql语句的参数即对?赋值
rs = this.wrap(stmt.executeQuery());//执行sql语句返回ResultSet
result = rsh.handle(rs);//将结果集封装到ArrayList中(利用反射)
} catch (SQLException var33) {
this.rethrow(var33, sql, params);
} finally {
try {
this.close(rs);//关闭ResultSet
} finally {
this.close(stmt);//关闭PreparedStatement
if (closeConn) {
this.close(conn);
}
}
}
return result;
}
}
DAO和增删改查通用方法-BasicDao
通过apache-dbutil配合druid简化了JDBC开发,但仍然存在不足
问题分析:
- sql语句固定,无法通过参数传入
- 对DQL语句,如果有返回值,返回类型不能固定,需要使用泛型
- 仅靠单个javabean无法完成复杂的业务
基本介绍:
- DAO:data access object,数据访问对象
- BasicDAO,即通用类,专门和数据库交互,并完成对表的CRUD
- 在BasicDAO的基础上,实现一张表对应一个DAO
DAO以及BasicDAO的好处:
- BasicDAO将所有DAO的共同操作提取出来,提高可读性和维护性
- 各个DAO通过继承BasicDAO完成对应的表的CRUD操作,并可以对不同表执行特有的操作
//BasciDAO
public class BasicDAO<T> {
private QueryRunner qr = new QueryRunner();
//通用的DML方法,针对任意的表
public int update(String sql,Object... params){
Connection connection = null;
try {
connection = DruidUtils.getConnection();
int update = qr.update(connection,sql,params);
return update;
} catch (SQLException e) {
throw new RuntimeException(e);
}finally {
DruidUtils.close(null,null,connection);
}
}
//查询多行,返回多个对象,针对任意表
public List<T> queryMany(String sql,Class<T> cls,Object... params){
Connection connection = null;
try {
connection = DruidUtils.getConnection();
return qr.query(connection,sql,new BeanListHandler<T>(cls),params);
} catch (SQLException throwables) {
throw new RuntimeException(throwables);
} finally {
DruidUtils.close(null,null,connection);
}
}
//查询当行,返回单个对象,针对任意表
public Object querySingle(String sql,Object... params){
Connection connection = null;
try {
connection = DruidUtils.getConnection();
return qr.query(connection,sql,new ScalarHandler(),params);
} catch (SQLException throwables) {
throw new RuntimeException(throwables);
} finally {
DruidUtils.close(null,null,connection);
}
}
}