JDBC与连接池
JDBC与连接池
- JDBC
- 什么是JDBC
- JDBC操作数据库步骤
- 常见的JDBC组件
- JDBC注册驱动为什么是使用反射而不是创建对象直接注册?
- 为什么建议使用prepareStatement执行SQL语句?
- SQL注入(了解)
- JDBC事务
- 数据库连接池
- 为什么要使用数据库连接池?
- 连接池常见参数介绍
- 连接池的实现
JDBC
什么是JDBC
JDBC全程为Java DataBase Connection,它是JDK提供用于操作数据库的接口,换句话说,它是JDK为Java操作数据库提供的API规范。具体的数据库驱动、API的实现类是由数据库厂商实现。例如使用MySQL需要mysql-connector-java-5.1.39-bin.jar。
JDBC操作数据库步骤
- 注册数据库驱动
- 建立数据库连接
- 创建statement
- 执行SQL语句
- 处理结果
关闭连接
//省略try,catch
// 注册驱动
Class.forName(“com.mysql.jdbc.Driver”);
// 获取连接对象
Connection con = (Connection) DriverManager.getConnection(“jdbc//localhost:3306/DBName”,”username”,”password”);
//获得Statement对象
PreparedStatement statement = con.prepareStatement(“select * from user where id=1”);
//提交查询
statement.executeQuery();
//关闭连接
statement.close();
con.close();
常见的JDBC组件
(图片来自:https://www.yiibai.com/jdbc/jdbc-introduction.html)
DriverManager: 管理数据库驱动类。
Connection: 数据库连接对象,拥有与数据库交互的方法。
Statement: 此接口用于将SQL语句提交给数据库执行,并接收返回结果。
**ResultSet:**查询后返回的结果集。
JDBC注册驱动为什么是使用反射而不是创建对象直接注册?
- 低耦合。反射通过名称创建,名称可以配置在配置文件中,包括连接的URL,方便更换数据库。
查看MySQL中的驱动类源码:
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
static {
try {
java.sql.DriverManager.registerDriver(new Driver());
} catch (SQLException E) {
throw new RuntimeException("Can't register driver!");
}
}
//省略其他代码
}
可以看见static 代码块中注册了驱动对象,那么在类加载时就会执行static代码块。
为什么建议使用prepareStatement执行SQL语句?
JDBC执行SQL语句的类有,Statement和prepareStatement,为什么推荐使用prepareStatement?
方便加入参数。prepareStatement支持动态参数化的SQL执行,使用占位符?表示参数。而Statement只能使用SQL语句拼接的形式。
性能更好。prepareStatement的SQL语句会预编译在DBMS中,并进行缓存,下次执行相同结构的语句时就无需再次编译生成执行计划。而Statement执行SQL每次都需要进行编译。
更安全。prepareStatement在传入参数时会进行特殊处理,避免SQL注入。例如将整个参数条件使用引号包裹,防止恶意语句作为条件单独执行。
select file from file where name = #{ name}
SQL注入输入的name = 'test' or 1=1
语句变为:select file from file where name = 'test' or 1=1
此时1=1永远成立,那么语句将会被执行成功。
prepareStatement处理后:select file from file where name = '\'test\' or 1=1'
使用引号包裹后整个参数作为了条件。
可读性好。比起复杂的字符串拼接,动态参数化显然可读性好得多。
SQL注入(了解)
所谓SQL注入,就是通过把SQL命令插入到Web表单提交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令。
如何避免?
- 预编译语句后生成执行计划,再绑定变量,SQL的语义就不会发生变化,插入的内容只能作为字符串参数执行。
- 对非法参数进行过滤。不适用高权限用户直接连接数据库。
JDBC事务
这里只简单介绍写法,具体事务后续文章介绍。
try{
Class.forName("com.mysql.jdbc.Driver");
Connection con = (Connection) DriverManager.getConnection("jdbc:mysql://localhost:3306/DBName","username","password");
//关闭自动提交
con.setAutoCommit(false);
PreparedStatement statement = con.prepareStatement("select * from user where id=1");
statement.executeQuery();
//提交事务
con.commit();
statement.close();
con.close();
} catch(Exception e) {
// 出错回滚
con.rollback();
}
数据库连接池
为什么要使用数据库连接池?
在写JDBC的Demo时执行一次SQL的操作为:加载驱动,获取连接,执行SQL,获得结果,关闭资源。事实上建立连接是非常消耗资源
的,它可能比本身操作数据库还要慢,而我们在一次或者几次的数据库操作 中也许感受不到差别。但是如果在并发量稍大一点的系统中,频繁的建立连接,释放资源会造成大量系统资源的浪费,如果程序出现异常没有及时关闭连接,或者受到恶意攻击,无法限定连接数可能会导致内存泄漏
问题!
连接池常见参数介绍
- initSize:连接池初始化连接数
- maxActive:池中最大连接数量
- mindle:最小连接池数量
- maxWait:超时等待时间
连接池的实现
(个人的见解)
- 连接池在初始化时会根据配置的初始化连接数创建Connection放入池中。
- 当需要使用时向连接池中获取连接并将其标记为正忙,这个过程考虑了多线程的线程安全性,可能是加锁可能是CAS,便于管理可能分为空闲池和工作池。
- 当使用完连接池后不关闭,进行归还,重新放入池中。
- 配置了超时等待的时间,空闲池中空闲的线程到达超时等待时间后会被清除,直到数量为最小连接池数量。
- 当获取连接时,连接池中没有空闲连接并且连接数小于最大连接数量,线程池将创建新连接并返回。
- 当获取连接时,连接池中连接数已经到达了最大,并且都在忙,获取请求将会等待一定时间,没有拿到请求则返回null
还没有评论,来说两句吧...