JAVA数据源

前言

我们在进行数据访问,声明模板和repository之前【JAVA】Spring对JDBC的支持,都需要配置数据源用来连接数据库。数据源又可以分为两大类:直连的数据源连接池的数据源 ,其中连接池的数据源又有多种,接下来就让我们来学习一下这两种数据源。当然在学习之前,我们首先需要知道连接池数据源JNDI是什么,分别从当什么角色,有什么作用。

连接池、数据源、JNDI

连接池、数据源

Java中的数据源就是连接到数据库的一条路径,数据源中并无真正的数据,它仅仅记录的是你连接到哪个数据库,以及如何连接DataSource的创建可以有不同的实现。DataSource通常被称为数据源,它包含连接池连接池管理 两部分,习惯上也经常把DataSource称为连接池。

连接池思想

在系统初始化的时候,将数据库连接对象(Connection) 存储在内存中,当用户需要访问数据库时候,并不是建立一个新的连接,而是从连接池中取出一个已经建立好的空闲连接对象。而连接池负责分配、管理、释放数据库连接对象。注意的是:连接池是由容器(比如tomcat) 提供的,同时容器也管理着连接池。

JNDI

JNDI(Java Naming and Directory Interface,Java命名和目录接口),JNDI是Java平台的一个标准扩展,提供一组接口、类和关于命名空间的概念。其功能通俗的来说,就是提供一个类似全局的map,key保存JNDI的名称,value保存你要放到里面的资源的引用(如Java对象),以后要想要获取value的资源即可通过lookup名称检索
注意:需要区分开JNDI和通过JNDI查找的数据源的概念,不能将JNDI和连接池混为一谈。

  • 这套API的主要作用在于:它可以将Java对象放在一个容器中(支持JNDI的容器例如Tomcat),并且为容器中的Java对象取一个名称,以后程序想要获得Java对象,只要通过名称检索即可。
  • 其核心API为Context,它代表JNDI容器,其lookup方法为检索容器中对应名称的对象。

使用JNDI访问Tomcat内置连接池

  • 将数据库驱动的包复制到Tomcat的安装目录/lib/中,这样Tomcat服务器才能找到数据库驱动
  • 编写访问JNDI程序,运行在Tomcat内部,所以通常运行在servlet、jsp中
  • 在Tomcat启动时,自动加载配置文件(context.xml),创建数据库连接池,该连接池由Tomcat管理。
    在这里插入图片描述

直连的数据源

用户每次请求都需要向数据库获得连接,而数据库创建连接通常需要消耗相对较大的资源,创建的时间也较长。
在这里插入图片描述

连接池的数据源

连接池的思想

数据库连接是一种关键的有限的昂贵的资源,如果每次访问数据库的时候,都需要进行数据库连接,那么势必会造成性能低下;同时,如果用户失误忘记释放数据库连接,会导致资源的浪费等。而数据库连接池就是刚好可以解决这些问题,通过管理连接池中的多个连接对象(Connection),实现连接对象(connection)重复利用,从而大大提高了数据库连接方面的性能。
在这里插入图片描述

常用的数据库连接池

Tomcat内置的连接池(Tomcat Dbcp)

Tomcat在7.0以前的版本都是使用commons-dbcp作为连接池的实现,但是由于commons-dbcp饱受诟病。因此很多人会选择一些第三方的连接池组件,例如c3p0,bonecp等。为此,Tomcat从7.0开始引入了一个新的模块:Tomcat Jdbc Pool(Tomcat Dbcp)。关于更多请看这里
连接池的配置

1
2
3
4
5
6
7
<Context>
<Resource name="jdbc/login_register" auth="Container" type="javax.sql.DataSource"
maxActive="100" maxIdle="30" maxWait="10000"
username="root" password="root" driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/db_user"/>
factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
</Context>

属性的说明

  • Resource:声明这个是资源
  • name=指定Resource(资源)的JNDI名称
  • auth=指定管理Resource的Manager(Container由容器创建和管理,Application由Web应用创建和管理)
  • factory:必需的属性,其值应为 org.apache.tomcat.jdbc.pool.DataSourceFactory
  • type=指定Resource的java类(即指定JNDI能拿到的是什么类型的数据,javax.sql.DataSource这里指定为连接池对象)。类型应为 javax.sql.DataSource 或 javax.sql.XADataSource。根据类型,将创org.apache.tomcat.jdbc.pool.DataSource 或 org.apache.tomcat.jdbc.pool.XADataSource。
  • maxActive=指定连接池中处于活动状态的数据库连接的最大数量
  • maxIdle=指定连接池中处于空闲状态的数据库的最大数量
  • maxWait=指定连接池中连接处于空闲的最长时间,超过这个时间会提示异常,取值为-1,表示可以无限期等待,单位为毫秒(ms)

我们需要注意的属性是factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
如果你没有声明以上的属性,当Tomcat读到type="javax.sql.DataSource"属性时也会自动安装DBCP,除非你指定不同的factory。

连接池的获取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
  public class DBUtil {
public static Connection getConnection(){
Connection conn = null;
try {
Context c = new InitialContext();
DataSource dataSource = (DataSource) c.lookup("java:/comp/env/jdbc/login_register");//这里的jdbc/login_register和篇配置文件中的name属性一致
conn = dataSource.getConnection();
return conn;
} catch (SQLException e) {
e.printStackTrace();
} catch (NamingException e) {
e.printStackTrace();
}

return conn;
}
}

lookup方法用于查找指定JNDI名称的连接池,java:/comp/env/jdbc/login_registejdbc/login_registe和配置文件中JNDI的名称相对应。这样既可查找到数据库连接池,也就能获得到连接对象了。当然,连接池的获取方式不只一种,比如还有通过配置文件来获取的,感兴趣的可以查阅资料。

大概流程是这样的:

  • 通过配置文件Resource声明资源为连接池类型(javax.sql.DataSource)的对象
  • 加载连接池factory,org.apache.tomcat.jdbc.pool.DataSourceFactory
  • 通过JNDI找到连接池资源

通过这个例子我们看到了JNDI的作用,JNDI你可以理解成一个水池的门牌,连接池就相当于这个水池,应用要取到要访问数据库时,通过找到JNDI,然后再通过连接池和数据库所建立的连接来访问。

C3P0连接池

C3P0是一个开源的JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。C3P0是异步操作的,缓慢的JDBC操作通过帮助进程完成。扩展这些操作可以有效的提升性能。C3P0是改写的javax.sql.DataSource

连接池的配置
配置文件:c3p0-config.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<c3p0-config>
<default-config>
//默认是mysql数据库
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql:///项目名?useUnicode=true&amp;characterEncoding=utf8</property>
<property name="user">数据库用户名</property>
<property name="password">数据库密码</property>
<property name="initialPoolSize">5</property>
<property name="maxPoolSize">10</property>
</default-config>
//也可以用oracle数据库
<named-config name="oracleConfig">
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql:///项目名</property>
<property name="user">数据库用户名</property>
<property name="password">数据库密码</property>
<property name="initialPoolSize">5</property>
<property name="maxPoolSize">10</property>
</named-config>
</c3p0-config>

连接池的获取

1
2
3
4
5
6
7
8
9
10
11
12
13
 // 创建c3p0连接池核心工具类
// 自动加载src下c3p0的配置文件【c3p0-config.xml】
ComboPooledDataSource dataSource = new ComboPooledDataSource();// 使用默认的配置
PreparedStatement pstmt = null;
// 获取连接
Connection con = dataSource.getConnection();
//执行sql语句
String sql="sql语句"
//执行
pstmt.executeUpdate();
//关闭连接
pstmt.close();
con.close();

注意:创建C3P0的连接池时候还可以在()加上名称,连接指定配置文件中的连接池。

1
2
ComboPooledDataSource dataSource = 
new ComboPooledDataSource("oracleConfig");

当然数据库连接池除了以上的配置、获取方式外,还有别的方式,例如用JNDI配置和获取的

连接池配置
在tomcat的context.xml里的标签添加标签

1
2
3
4
5
6
7
8
<Resource name="jdbc/TestDB" 
auth="Container"
factory="org.apache.naming.factory.BeanFactory"
type="com.mchange.v2.c3p0.ComboPooledDataSource"
driverClass="oracle.jdbc.driver.OracleDriver"
jdbcUrl="jdbc:oracle:thin:@localhost:1521:XE"
user="orcl"
password="newsnews"/>

不同数据源的 driverClass,jdbcUrl等 属性不同,依照连接池自身的属性配置,这里是使用的c3p0数据源,若使用其他数据源要修改属性。jdbc/TestDB为JNDI名称。

在项目的web.xml添加

1
2
3
4
5
6
<resource-ref>
<description>DB Connection</description>
<res-ref-name>jdbc/TestDB</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>

res-ref-name引入context.xml里配置的Resource,res-type都设置为javax.sql.DataSource。

在spring的applicationContext.xml配置里添加

1
2
3
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="java:/comp/env/jdbc/TestDB" />
</bean>

DBCP连接池

DBCP(Database Connection Pool) 是一个依赖Jakarta commons-pool对象池机制的数据库连接池,Tomcat的数据源使用的就是DBCP。

连接池配置
dbcpconfig.properties文件

1
2
3
4
5
6
7
8
9
10
11
12
13
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/db_user
username=root
password=root
initialSize=10
maxActive=50
maxIdle=20
minIdle=5
maxWait=60000
connectionProperties=useUnicode=true;characterEncoding=utf8
defaultAutoCommit=true
defaultReadOnly=
defaultTransactionIsolation=REPEATABLE_READ

连接池获取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class DBUtil {
public static Connection getConnection(){
Connection conn = null;

try {
conn = getDataSource().getConnection();
} catch (SQLException e) {
e.printStackTrace();
}

return conn;
}

private static DataSource getDataSource(){
DataSource dataSource=null;
Properties p = new Properties();
try {
p.load(DBUtil.class.getClassLoader().getResourceAsStream("dbcpconfig.properties"));
dataSource = BasicDataSourceFactory.createDataSource(p);
} catch (Exception e) {
throw new RuntimeException("获取DataSource对象失败");
}
return dataSource;
}
}

自定义连接池

编写连接池需要实现javax.sql.DataSource接口,具体可以网上查阅相关资料。

其他连接池

还有更多的连接池例如:Proxool、BoneCP、HikariCP、阿里druid等可以自行查阅资料。

总结

以上,介绍了几种常用的数据源连接池;这几种连接池在使用过程,即支持硬编码的方式,也支持配置文件的配置方式;在正式实用的时候,应该尽量使用配置的方式,便于维护和管理。硬编码的方式,可以做为测试使用。同时,spring框架,通过他自己的方式集成上述几种数据源,理论上来说,都支持。各个数据源连接池都有一些公有的属性,因为他们都是从javax.sql.datasource继 承而来,而且都有最大连接数,初始化连接数等概念。同时,他们又分别有各自不同的属性,做了扩展。这里只是简单的介绍,在实际使用中,想要实现高性能的数 据库连接池管理,还需要深入研究每种方式的连接属性配置;例如:根据实际需要,设置合适的最小连接数和最大连接数,等待时间等。