Java的JDBC原生态学习以及连接池的用法
JDBC是什么
JDBC(Java Data Base Connectivity)是Java访问数据库的桥梁,但它只是接口规范,具体实现是各数据库厂商提供的驱动程序(Driver)。
应用程序、JDBC、JDBC驱动、数据库之间的关系如上图:应用程序通过JDBC访问数据库,而JDBC驱动来具体实现JDBC访问数据库的动作。
JDBC版本
JDBC的版本和JDK版本是独立的,他们的对应关系:
JDK版本 | DB版本 |
---|---|
JDK 1.1 | JDBC1 |
JDK 1.2, 1.3 | JDBC2 |
JDK 1.4, 1.5 | JDBC3 |
JDK 1.6 | JDBC4 |
JDK 1.7 | JDBC4.1 |
JDBC4.0新特性
JDBC 4.0中增加的主要特性包括:
- JDBC驱动类的自动加载;
不必再使用Class.forName()方法明确加载JDBC驱动。当调用getConnection方法 时,DriverManager会尝试从初始化时已经加载的JDBC驱动程序库中选择合适的驱动,以及他在当前应用的同一个类加载器中明确加载使用过的驱动。 - 连接管理的增强
- 在JDBC 4.0之前,我们依靠JDBC URL来定义一个数据源连接。现在我们只需为标准连接工厂机制提供一组参数,就能获取与任何数据源的连接。
- 对RowId SQL类型的支持
增加了RowID接口以支持ROWID数据类型。 - SQL的DataSet实现使用了Annotations;
JDBC 4.0对标注(Java SE 5新增)作了进一步规范和补充,他允许开发人员不必再写大量代码就能达到联系SQL查询和Java类的目的。 - SQL异常处理的增强;
JDBC 4.0在SQLException处理中有不少增强,在处理SQLException时提供了更好的开发体验,如新的SQLException子类、对因果关系的支持、对改进的for-each循环的支持。 - 对SQL XML的支持;
JDBC链接步骤
- 链接数据库;
- 创建执行声明;
- 执行Sql语句;
- 处理结果;
- 释放资源;
pom.xml添加MySql驱动依赖:
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.30</version>
</dependency>
完整的App.java代码
package net.oseye.DbDemo; import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement; public class App
{
public static void main( String[] args ) throws Exception
{
//链接数据库
Connection conn=DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root","root");
//创建执行声明
Statement stmt=conn.createStatement();
//执行Sql语句
ResultSet rs=stmt.executeQuery("select user_id,user_name from user");
//处理结果集
while(rs.next()){
System.out.println("用户ID"+rs.getInt(1)+"\t用户名:"+rs.getString(2));
}
//释放资源
rs.close();
stmt.close();
conn.close();
}
}
相关API在此不做详细介绍,请查看JDK API文档。
JDBC预处理PreparedStatement
上面的示例有两个缺点:
- 使用DriverManager获得数据库连接。这种方式效率低,并且其性能、可靠性和稳定性随着用户访问量得增加逐渐下降,应该使用连接池取而代之(后文再讲);
- 应该使用PreparedStatement取代Statement,因为Statement的缺点也非常明显:
- 执行时发送sql,影响效率.
- 同样的sql,每次都要发送,不能进行有效的缓存,是一种资源的浪费.
- 示例代码中演示可以看出,为了防止恶意数据我们还需要编写附加的程序(过滤器)带来不必要的开支.
- 拼接sql字符串很容易出现错误.
PreparedStatement是Statement的子接口,它在执行sql之前首先准备好sql语句,将其中的条件通过?进行占位,还有一个好处就是,同样的sql会被PreparedStatement有效的缓存,也就是说,数据库会减少校验的过程,减少消耗,这就是我们常说的预处理命令方式。
package net.oseye.DbDemo; import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet; public class App
{
public static void main( String[] args ) throws Exception
{
//链接数据库
Connection conn=DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root","root");
//创建执行声明
PreparedStatement pstmt=conn.prepareStatement("select user_id,user_name from user where user_id < ?");
//填充占位符?
pstmt.setInt(1, 3);
//执行Sql语句
ResultSet rs=pstmt.executeQuery();
//处理结果集
while(rs.next()){
System.out.println("用户ID"+rs.getInt(1)+"\t用户名:"+rs.getString(2));
}
//释放资源
rs.close();
pstmt.close();
conn.close();
}
}
JDBC调用存储过程请使用“CallableStatement”接口。
JDBC事务
数据库的事务是保证数据完整性的一种机制,简而言之,就是怎样确保数据的执行过程要么都成功,要么都失败
- 原子性(atomicity):组成事务处理的语句形成了一个逻辑单元,不能只执行其中的一部分。
- 一致性(consistency):在事务处理执行前后,数据库是一致的(两个账户要么都变,或者都不变)。
- 隔离性(isolcation):一个事务处理对另一个事务处理没有影响。
- 持续性(durability):事务处理的效果能够被永久保存下来 。
Jdbc的事务默认是打开的,也就是说执行每一步操作的话,事务都会隐式的进行提交,可以取消默认事务:
connection.setAutoCommit(false);
Demo
package net.oseye.DbDemo; import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet; public class App
{
public static void main( String[] args ) throws Exception
{
Connection conn=DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root","root"); //取消默认事务
conn.setAutoCommit(false);
PreparedStatement pstmt=conn.prepareStatement("update user set user_name=? where user_id = ?");
pstmt.setString(1, "kevin");
pstmt.setInt(2, 1);
pstmt.execute(); pstmt=conn.prepareStatement("select user_name from user where user_id=?");
pstmt.setInt(1,1);
ResultSet rs=pstmt.executeQuery();
//提交事务
conn.commit(); if(rs.next()){
System.out.println("用户名:"+rs.getString(1));
}
rs.close();
pstmt.close();
conn.close();
}
}
这里没有对事务做深入讨论,看到这儿容易让你产生误会,以为事务如此简单,请正式生产前一定找相关专业书籍学习,切记切记。
JDBC的DataSource
前文提到使用DriverManager获取数据库连接的低效,是因为数据库连接是一种关键的有限而昂贵的资源。所以JDK为我们提供了一个DataSource接口。DataSource作为 DriverManager 工具的替代项,DataSource 对象是获取连接的首选方法。DataSource接口:
//尝试建立与此 DataSource 对象所表示的数据源的连接。
Connection getConnection() //尝试建立与此 DataSource 对象所表示的数据源的连接。
Connection getConnection(String username, String password)
有很多容器如Tomcat都实现DataSource接口而提供了连接池功能,现在我们来简单实现一个连接池:
/**
* 实现DataSource接口的简单连接池
*/
public class ConnectPool implements DataSource {
private static final String url = "jdbc:mysql://127.0.0.1:3306/test";
private static final String user = "root";
private static final String pswd = "root"; // 连接池队列
private static LinkedList<Connection> pool = new LinkedList<Connection>();
private static ConnectPool instance = new ConnectPool(); /**
* 获取数据源单例
*/
public static ConnectPool instance() {
if (instance == null)
instance = new ConnectPool();
return instance;
} /**
* 获取一个数据库连接
*/
public Connection getConnection() throws SQLException {
synchronized (pool) {
if (pool.size() > 0) {
return pool.removeFirst();
} else {
return DriverManager.getConnection(url, user, pswd);
}
}
} /**
* 连接归池,这里的实现思想是使用过的线程入池以备下次使用
*/
public static void freeConnection(Connection conn) {
pool.addLast(conn);
} public Connection getConnection(String username, String password)
throws SQLException {
// TODO Auto-generated method stub
return null;
} public PrintWriter getLogWriter() throws SQLException {
// TODO Auto-generated method stub
return null;
} public void setLogWriter(PrintWriter out) throws SQLException {
// TODO Auto-generated method stub } public void setLoginTimeout(int seconds) throws SQLException {
// TODO Auto-generated method stub } public int getLoginTimeout() throws SQLException {
// TODO Auto-generated method stub
return 0;
} public Logger getParentLogger() throws SQLFeatureNotSupportedException {
// TODO Auto-generated method stub
return null;
} public <T> T unwrap(Class<T> iface) throws SQLException {
// TODO Auto-generated method stub
return null;
} public boolean isWrapperFor(Class<?> iface) throws SQLException {
// TODO Auto-generated method stub
return false;
}
}
使用连接池
public class App {
public static void main(String[] args) throws Exception {
//没有使用连接池
long start=System.currentTimeMillis();
for (int i = 0; i < 100; i++) {
Connection conn = DriverManager.getConnection(
"jdbc:mysql://127.0.0.1:3306/test",
"root", "root"); PreparedStatement pstmt = conn
.prepareStatement("insert into user(user_name) values(?)");
pstmt.setString(1, String.valueOf(System.currentTimeMillis()));
pstmt.execute(); pstmt.close();
conn.close();
}
System.out.println("没有使用连接池花费时间:"+(System.currentTimeMillis()-start));
//使用连接池
start=System.currentTimeMillis();
for (int i = 0; i < 100; i++) {
Connection conn=ConnectPool.instance().getConnection(); PreparedStatement pstmt = conn
.prepareStatement("insert into user(user_name) values(?)");
pstmt.setString(1, String.valueOf(System.currentTimeMillis()));
pstmt.execute(); pstmt.close();
ConnectPool.freeConnection(conn);
}
System.out.println("使用连接池花费时间:"+(System.currentTimeMillis()-start));
}
}
输出
没有使用连接池花费时间:4625
使用连接池花费时间:2782
示例是使用的单线程就能足以展现出差距了,如果使用多线程会更明显,但多线程就要对连接池进行同步操作,实现起来会更复杂。实例中的连接池实现虽然使用了同步(synchronized ),但是向当地弱,此实现只是简单展示连接池的实现以及使用,禁止把如此简单实现投入生产,切记切记。
开源连接池
有很多产品都实现了自己的连接池,如Jboss、Tomcat等容器,业界也有很多著名的开源连接池供大家使用,如:C3P0、BoneCP、DBCP 、Proxool等。我们以BoneCP连接池为例来展示。
pom.xml增加BoneCP的依赖配置(BoneCP使用了SLF4J,这里暂时没做配置)
<dependency>
<groupId>com.jolbox</groupId>
<artifactId>bonecp</artifactId>
<version>0.8.0.RELEASE</version>
</dependency>
App.java代码
package net.oseye.DbDemo; import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException; import com.jolbox.bonecp.BoneCP;
import com.jolbox.bonecp.BoneCPConfig; public class App {
public static void main(String[] args) {
long start=System.currentTimeMillis();
BoneCP connectionPool = null;
Connection connection = null;
try {
//连接池配置
BoneCPConfig config = new BoneCPConfig();
config.setJdbcUrl("jdbc:mysql://127.0.0.1:3306/test");
config.setUsername("root");
config.setPassword("root");
config.setMinConnectionsPerPartition(5);
config.setMaxConnectionsPerPartition(10);
config.setPartitionCount(1); connectionPool = new BoneCP(config);
for (int i = 0; i < 100; i++) {
//获取链接
connection = connectionPool.getConnection();
if (connection != null) {
PreparedStatement pstmt = connection
.prepareStatement("insert into user(user_name) values(?)");
pstmt.setString(1,
String.valueOf(System.currentTimeMillis()));
pstmt.execute();
pstmt.close();
connection.close();
}
}
connectionPool.shutdown();
System.out.println("使用BoneCP连接池花费时间:"+(System.currentTimeMillis()-start));
} catch (SQLException e) {
e.printStackTrace();
}
}
}
输出
使用BoneCP连接池花费时间:3531
Java的JDBC原生态学习以及连接池的用法的更多相关文章
- 走进JavaWeb技术世界3:JDBC的进化与连接池技术
走进JavaWeb技术世界3:JDBC的进化与连接池技术 转载公众号[码农翻身] 网络访问 随着 Oracle, Sybase, SQL Server ,DB2, Mysql 等人陆陆续续住进数据库 ...
- jdbc连接MySQL数据库+简单实例(普通JDBC方法实现和连接池方式实现)
jdbc连接数据库 总结内容 1. 基本概念 jdbc的概念 2. 数据库连接 数据库的连接 DAO层思想 重构设计 3. 事务 概念 事务的ACID属性 事务的操作 4. 连接池 为什么要使用连接池 ...
- jdbc连接数据库以及简单实现(普通JDBC方法实现和连接池方式实现)
@ 目录 总结内容 1. 基本概念 jdbc的概念 2. 数据库连接 数据库的连接 DAO层思想 重构设计 3. 事务 概念 事务的ACID属性 事务的操作 4. 连接池 为什么要使用连接池 连接池分 ...
- java基础之JDBC八:Druid连接池的使用
基本使用代码: /** * Druid连接池及简单工具类的使用 */ public class Test{ public static void main(String[] args) { Conne ...
- java基础之JDBC七:C3P0连接池的使用
使用C3P0的前提是需要引入jar包 具体使用如下: /** * c3p0的应用 * 前提:引入c3p0的jar包 */ public class Test { public static void ...
- JDBC基础学习(六)—数据库连接池
一.数据库连接池介绍 1.数据库连接池的缘由 对于一个简单的数据库应用,由于对于数据库的访问不是很频繁.这时可以简单地在需要访问数据库时,就新创建一个连接,用完后就关闭它,这样做也不会带来什 ...
- JDBC数据源 使用JNDI连接池实现数据库的连接
0.引言 许多Web应用程序需要通过JDBC驱动程序访问数据库,以支持该应用程序所需的功能.Java EE平台规范要求Java EE应用程序服务器为此目的提供一个DataSource实现(即,用于JD ...
- JDBC 连接数据库,包含连接池
1.不使用连接池方式(Jdbc) 1.1 工具类(JdbcUtil.java) package com.jdbc.util; import java.io.IOException;import jav ...
- JDBC、事务和连接池
一:JDBC 1.什么是JDBC JDBC(Java Data Base Connectivity)SUN公司提供的一套操作数据库的标准规范.具体来讲是一种用于执行SQL语句的Java API,为多种 ...
随机推荐
- mySQL内存及虚拟内存优化设置
为了装mysql环境测试,装上后发现启动后mysql占用了很大的虚拟内存,达8百多兆.网上搜索了一下,得到高人指点my.ini.再也没见再详细的了..只好打开my.ini逐行的啃,虽然英文差了点,不过 ...
- --@angularJS--指令之单个点击展开demo
1.expander.html: <!DOCTYPE HTML><html ng-app="app"><head> <title&g ...
- 解决Centos 7 下 tomcat字体异常 Font '宋体' is not available to the JVM
错误提示: SEVERE: Servlet.service() for servlet [example] in context with path [/myproject] threw except ...
- VAST3.0规范
VAST3.0视频广告投放规范 Posted on 2014年2月15日 1.术语 随着视频广告行业的发展,某些术语已经得到了广泛的采用.以下定义该文档中与视频广告投放相关的一些术语: 广告荚(Ad ...
- storm遇到问题汇总
http://www.reader8.cn/jiaocheng/20131023/2139887.html 错误1:在windows下运行ExclamationTopology或者WordCountT ...
- Canvas createRadialGradient API
Canvas createRadialGradient API <!DOCTYPE html> <html lang="en"> <head> ...
- JS-鼠标滚轮事件 和 阻止默认行为
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...
- CodeForces757A
A. Gotta Catch Em' All! time limit per test 1 second memory limit per test 256 megabytes input stand ...
- JAVA finally字句的异常丢失和返回值覆盖解析
转载:http://blog.csdn.net/sureyonder/article/details/5560538 Java虚拟机在每个try语句块和与其相关的catch子句的结尾 处都会“调用”f ...
- ubuntu linux 下 mysql 学习笔记
'#' 后为注释 以下为在shell中的操作: 打开终端(terminal)1.登录MySQL mysql-u root -p 输入密码:******2.几个简单的命令 (1)show databas ...