JavaWeb开发与代码的编写(二十四)
JNDI数据源的配置
数据源的由来
在Java开发中,使用JDBC操作数据库的四个步骤如下:
①加载数据库驱动程序(Class.forName("数据库驱动类");) ②连接数据库(Connection con = DriverManager.getConnection();) ③操作数据库(PreparedStatement stat = con.prepareStatement(sql);stat.executeQuery();) ④关闭数据库,释放连接(con.close();) 也就是说,所有的用户都需要经过此四步进行操作,但是这四步之中有三步(①加载数据库驱动程序、②连接数据库、④关闭数据库,释放连接)对所有人都是一样的,而所有人只有在操作数据库上是不一样,那么这就造成了性能的损耗。 那么最好的做法是,准备出一个空间,此空间里专门保存着全部的数据库连接,以后用户用数据库操作的时候不用再重新加载驱动、连接数据库之类的,而直接从此空间中取走连接,关闭的时候直接把连接放回到此空间之中。 那么此空间就可以称为连接池(保存所有的数据库连接),但是如果要想实现此空间的话,则必须有一个问题要考虑? 1、 如果没有任何一个用户使用连接,那么那么应该维持一定数量的连接,等待用户使用。 2、 如果连接已经满了,则必须打开新的连接,供更多用户使用。 3、 如果一个服务器就只能有100个连接,那么如果有第101个人过来呢?应该等待其他用户释放连接 4、 如果一个用户等待时间太长了,则应该告诉用户,操作是失败的。 如果直接用程序实现以上功能,则会比较麻烦,所以在Tomcat 4.1.27之后,在服务器上就直接增加了数据源的配置选项,直接在服务器上配置好数据源连接池即可。在J2EE服务器上保存着一个数据库的多个连接。每一个连接通过DataSource可以找到。DataSource被绑定在了JNDI树上(为每一个DataSource提供一个名字)客户端通过名称找到在JNDI树上绑定的DataSource,再由DataSource找到一个连接。如下图所示:
那么在以后的操作中,除了数据库的连接方式不一样之外,其他的所有操作都一样,只是关闭的时候不是彻底地关闭数据库,而是把数据库的连接放回到连接池中去。
如果要想使用数据源的配置,则必须配置虚拟目录,因为此配置是在虚拟目录之上起作用的。需要注意的是,如果要想完成以上的功能,在Tomcat服务器上一定要有各个数据库的驱动程序。
JNDI+Tomcat配置数据源的两种方式
全局jndi配置
此种配置需要在server.xml中配置数据源,具体的配置步骤如下:
1、在tomcat服务器的lib目录下加入数据库连接的驱动jar包
2、修改tomcat服务器的conf目录下server.xml配置文件
打开server.xml配置文件,可以看到里面自带的一个全局JNDI配置,如下图所示:
编辑server.xml文件,添加全局JNDI数据源配置,配置如下:
经过以上的两个步骤,全局JNDI数据源就配置好了,在上述的server.xml文件中,分别配置了Oracle、MySQL、SQLServer这三种数据库的全局JNDI数据源。
全局jndi数据源测试
1、创建一个JNDI测试项目JNDITest,在web.xml中添加JNDI配置的资源引用
web.xml的配置如下:
index.jsp
Oracle DB Connection
oracleDataSource
javax.sql.DataSource
Container
MySQL DB Connection
mysqlDataSource
javax.sql.DataSource
Container
SQLServer DB Connection
sqlserverDataSource
javax.sql.DataSource
Container
2、映射JNDITest项目的虚拟目录
在tomcat的\conf\Catalina\localhost下(没有目录就新建)创建一个xml文件,文件名必须和项目名相同,测试JNDI数据源的web项目的项目名称是:JNDITest,所以xml文件的命名就叫JNDITest.xml,如下图所示:
编辑JNDITest.xml,配置如下:
3、测试从JNDI数据源获取数据库连接
配置完数据源连接池之后,就可以按照以下的步骤进行访问:
• 初始化名称查找上下文 • 通过JNDI名称找到DataSource • 通过DataSource取得一个连接 • 操作数据库 • 关闭数据库,关闭的时候是将连接放回到连接池之中
jsp测试页面代码如下:
JNDI数据源测试
运行结果如下:
除了可以在Jsp页面中编写java代码测试JNDI数据源连接之外,还有一种比较简单的方式就是使用JSTL标签库提供的sql标签进行测试,测试代码如下:
4
JNDI数据源连接测试
Oracle JNDI数据源测试
SELECT * FROM LEAD_OAMS_DBSOURCES
${row.RESOURCEID}---${row.DBSOURCE_NAME}---${row.DBSOURCE_TYPE}
MySQL JNDI数据源测试
select * from ld_user
${row.id}---${row.username}---${row.password}
SQLServer JNDI数据源测试
select * from t_demo
${row.id}---${row.time}
运行结果如下:
非全局jndi配置
非全局JNDI数据源是针对某一个Web项目配置的数据源,具体的配置步骤如下:
1、在tomcat服务器的lib目录下加入数据库连接的驱动jar包
2、针对具体的web项目映射虚拟目录,然后在虚拟目录映射的配置文件中配置JNDI数据源
还是以上面的JNDITest项目为例子进行说明
在tomcat目录下的\conf\Catalina\localhost目录下创建一个JNDITest.xml文件,如下图所示:
编辑JNDITest.xml文件,添加对JNDITest项目的虚拟目录的映射和JNDI数据源的配置
3、在web项目的web.xml文件中引用配置好的JNDI数据源
Oracle DB Connection
oracleDataSource
javax.sql.DataSource
Container
MySQL DB Connection
mysqlDataSource
javax.sql.DataSource
Container
SQLServer DB Connection
sqlserverDataSource
javax.sql.DataSource
Container
经过以上3个步骤,针对具体web项目配置的JNDI数据源就算是配置好了。具体的测试和上述测试全局JNDI数据源的方式是一样的!
Tomcat下使用C3P0配置JNDI数据源
C3P0下载
C3P0下载地址:http://sourceforge.net/projects/c3p0/files/?source=navbar
下载完成之后得到一个压缩包。
使用C3P0配置JNDI数据源
Tomcat6.x中配置JNDI数据源时默认使用的是Tomcat6.x自带的DBCP连接池,Tomcat6.x使用DBCP连接池配置JNDI数据源如下:
如果想让Tomcat6.x使用C3P0连接池配置JNDI数据源,在配置时,以下配置项需要修改
1、 type和factory的值发生变化
2、username=>user
3、url=>jdbcUrl
4、driverClassName=>driverClass
创建一个Web测试项目C3P0_JNDI_Config,解压压缩包,找到c3p0-0.9.5-pre9\lib目录下的相关Jar包如下图所示:
将C3P0的相关Jar包添加到项目中,在项目的META-INF目录下创建一个context.xml文件,目录结构如下图所示:
在tomcat服务器的lib目录下添加Oracle、MySQL、SQLServer三种数据库的驱动jar包,如下图所示:
1、在context.xml文件中加入如下配置信息
2.在web.xml引用JDNI数据源:
index.jsp
Oracle DB Connection
jdbc/OracleDataSource
javax.sql.DataSource
Container
MySQL DB Connection
jdbc/MysqlDataSource
javax.sql.DataSource
Container
SQLServer DB Connection
jdbc/SqlServerDataSource
javax.sql.DataSource
Container
3.部署C3P0_JNDI_Config Web应用到Tomcat服务器测试JNDI数据源
部署到tomcat服务器的webapps目录之后,tomcat服务器就会自动在\conf\Catalina\localhost目录下生成一个C3P0_JNDI_Config.xml文件,如下图所示:
C3P0_JNDI_Config.xml文件中的内容就是我们在META-INF目录的context.xml文件中配置的那些内容。
jsp测试页面如下:
C3P0配置JNDI数据源连接测试
针对MySQL数据库JNDI数据源测试
select * from ld_user
${row.id}---${row.username}---${row.password}
针对Oracle数据库JNDI数据源测试
SELECT * FROM LEAD_OAMS_DBSOURCES
${row.RESOURCEID}---${row.DBSOURCE_NAME}---${row.DBSOURCE_TYPE}
C3P0 自动重连相关参数 idleConnectionTestPeriod:C3P0会有一个Task检测pool内的连接是否正常,此参数就是Task运行的频率。默认值为0,表示不进行检测 acquireRetryAttempts:pool请求取新连接失败后重试的次数 C3P0目前存在问题: 当数据库重启后,C3P0不会自动重新初始化数据库连接池,当新的请求需要访问数据库的时候,此时回报错误(因为数据库重启,连接失效),同时刷新数据库连接池,丢弃掉已经失效的连接,当第二个请求到来时恢复正常。 C3P0目前没有提供当获取已建立连接失败后重试次数的参数,只有获取新连接失败后重试次数的参数(acquireRetryAttempts )。 要解决此问题,可以通过设置idleConnectionTestPeriod 参数折中解决,该参数的作用是设置系统自动检查连接池中连接是否正常的一个频率参数,时间单位是秒。
Tomcat下使用Druid配置JNDI数据源
com.alibaba.druid.pool.DruidDataSourceFactory实现了javax.naming.spi.ObjectFactory,可以作为JNDI数据源来配置。
下载Druid的jar包下载地址:http://mvnrepository.com/artifact/com.alibaba/druid/1.0.9,如下图所示:
druid.jar依赖log4j的jar包,所以还需要下载log4j的jar包。
log4j的下载地址如下:http://mvnrepository.com/artifact/log4j/log4j/1.2.17,如下图所示:
使用Druid配置JNDI数据源
前期准备工作
创建一个Web测试项目Druid_JNDI_Config,将下载下来druid-1.0.9.jar和log4j-1.2.17.jar添加到项目中,在项目的META-INF目录下创建一个context.xml文件
目录结构如下图所示:
在tomcat服务器的lib目录下添加Oracle、MySQL、SQLServer三种数据库的驱动jar包,如下图所示:
在context.xml文件中加入JNDI的配置信息
在context.xml文件中加入如下配置信息
配置项中指定了各个参数后,在连接池内部是这么使用这些参数的。数据库连接池在初始化的时候会创建initialSize个连接,当有数据库操作时,会从池中取出一个连接。如果当前池中正在使用的连接数等于maxActive,则会等待一段时间,等待其他操作释放掉某一个连接,如果这个等待时间超过了maxWait,则会报错;如果当前正在使用的连接数没有达到maxActive,则判断当前是否空闲连接,如果有则直接使用空闲连接,如果没有则新建立一个连接。在连接使用完毕后,不是将其物理连接关闭,而是将其放入池中等待其他操作复用。同时连接池内部有机制判断,如果当前的总的连接数少于miniIdle,则会建立新的空闲连接,以保证连接数得到miniIdle。如果当前连接池中某个连接在空闲了timeBetweenEvictionRunsMillis时间后任然没有使用,则被物理性的关闭掉。有些数据库连接的时候有超时限制(mysql连接在8小时后断开),或者由于网络中断等原因,连接池的连接会出现失效的情况,这时候设置一个testWhileIdle参数为true,可以保证连接池内部定时检测连接的可用性,不可用的连接会被抛弃或者重建,最大情况的保证从连接池中得到的Connection对象是可用的。当然,为了保证绝对的可用性,你也可以使用testOnBorrow为true(即在获取Connection对象时检测其可用性), 不过这样会影响性能。
在web.xml引用JDNI数据源
在web.xml文件中加入如下的配置引用JNDI数据源
index.jsp
Oracle DB Connection
jdbc/OracleDataSource
javax.sql.DataSource
Container
MySQL DB Connection
jdbc/MysqlDataSource
javax.sql.DataSource
Container
SQLServer DB Connection
jdbc/SqlServerDataSource
javax.sql.DataSource
Container
测试JNDI数据源
部署Druid_JNDI_Config Web应用到Tomcat服务器测试JNDI数据源,如下图所示:
部署到tomcat服务器的webapps目录之后,tomcat服务器就会自动在\conf\Catalina\localhost目录下生成一个Druid_JNDI_Config.xml文件,如下图所示:
Druid_JNDI_Config.xml文件中的内容就是我们在META-INF目录的context.xml文件中配置的那些内容。
jsp测试页面如下:
DRUID配置JNDI数据源连接测试
针对MySQL数据库JNDI数据源测试
select * from lead_oams_applications
${row.resourceid}---${row.app_name}
针对Oracle数据库JNDI数据源测试
SELECT * FROM LEAD_OAMS_DBSOURCES
${row.RESOURCEID}---${row.DBSOURCE_NAME}---${row.DBSOURCE_TYPE}
SQLServer JNDI数据源测试
select * from t_demo
${row.id}---${row.name}
运行结果如下:
在Java代码中获取JNDI中的数据源
获取JNDI中的数据源
编写一个JdbcUtil工具类,JdbcUtil工具类负责从JNDI容器中获取DataSource,再通过DataSource获取数据库连接。
代码如下:
package me.gacl.util;
/**
* ClassName: JdbcUtil
*
Description: 从JNDI容器中获取DataSource,再通过DataSource获取数据库连接
*/
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import com.alibaba.druid.pool.DruidDataSource;
public class JdbcUtil {
/*
web.xml文件中的JNDI数据源引用配置
Oracle DB Connection
jdbc/OracleDataSource
javax.sql.DataSource
Container
MySQL DB Connection
jdbc/MysqlDataSource
javax.sql.DataSource
Container
SQLServer DB Connection
jdbc/SqlServerDataSource
javax.sql.DataSource
Container
*/
//Oracle数据库配置的JNDI数据源连接名,后面跟的是DataSource名,DataSource名在web.xml文件中的进行了配置
private static final String ORACLE_DB_JNDINAME = "java:comp/env/jdbc/OracleDataSource";
//MySQL数据库配置的JNDI数据源连接名,java:comp/env是必须加的,后面跟的是DataSource名
private static final String MYSQL_DB_JNDINAME = "java:comp/env/jdbc/MysqlDataSource";
//SQLServer数据库配置的JNDI数据源连接名,java:comp/env是必须加的,后面跟的是DataSource名
private static final String SQLSERVER_DB_JNDINAME = "java:comp/env/jdbc/SqlServerDataSource";
private static DruidDataSource dsOracle = null;
private static DruidDataSource dsMySql = null;
private static DruidDataSource dsSqlServer = null;
static{
try {
//1、初始化名称查找上下文
Context ctx = new InitialContext();
//2、通过JNDI名称找到DataSource
dsOracle = (DruidDataSource) ctx.lookup(ORACLE_DB_JNDINAME);
dsMySql = (DruidDataSource) ctx.lookup(MYSQL_DB_JNDINAME);
dsSqlServer = (DruidDataSource) ctx.lookup(SQLSERVER_DB_JNDINAME);
} catch (NamingException e) {
e.printStackTrace();
}
}
/**
* MethodName: getOracleConnection
* Description: 获取Oracle数据库连接
* @return
* @throws SQLException
*/
public static Connection getOracleConnection() throws SQLException {
return dsOracle.getConnection();
}
/**
* MethodName: getMySqlConnection
* Description: 获取MySQL数据库连接
* @return
* @throws SQLException
*/
public static Connection getMySqlConnection() throws SQLException {
return dsMySql.getConnection();
}
/**
* MethodName: getSqlServerConnection
* Description: 获取SQLServer数据库连接
* @return
* @throws SQLException
*/
public static Connection getSqlServerConnection() throws SQLException {
return dsSqlServer.getConnection();
}
/**
* @Method: release
* @Description: 释放资源,
* 要释放的资源包括Connection数据库连接对象,负责执行SQL命令的Statement对象,存储查询结果的ResultSet对象
*
* @param conn
* @param st
* @param rs
*/
public static void release(Connection conn,Statement st,ResultSet rs){
if(rs!=null){
try{
//关闭存储查询结果的ResultSet对象
rs.close();
}catch (Exception e) {
e.printStackTrace();
}
rs = null;
}
if(st!=null){
try{
//关闭负责执行SQL命令的Statement对象
st.close();
}catch (Exception e) {
e.printStackTrace();
}
}
if(conn!=null){
try{
//关闭Connection数据库连接对象
conn.close();
}catch (Exception e) {
e.printStackTrace();
}
}
}
}
测试JNDI数据源
编写一个测试的Servlet,测试代码如下:
package me.gacl.test;
import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import me.gacl.util.JdbcUtil;
/**
* ClassName: JNDITestServlet
*
Description:
*
Company:广州利迪网络科技有限公司
* @author xudp
* @version 1.0 V
* @createTime 2014-10-23 上午09:32:52
*/
public class JNDITestServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//Oracle数据库连接
Connection oracleConnection = null;
//MySql数据库连接
Connection mySqlConnection = null;
//SQLServer数据库连接
Connection sqlServerConnection = null;
//负责执行SQL的PreparedStatement对象
PreparedStatement pstmtOracle = null;
PreparedStatement pstmtMySQL = null;
PreparedStatement pstmtSqlServer = null;
//查询出来的结果集
ResultSet rsOracle = null;
ResultSet rsMySQL = null;
ResultSet rsSqlServer = null;
//存储查询出来的数据,每一行数据映射成一个Map,字段名作为key,字段的值作为value
List oracleDataList = new ArrayList();
List mySqlDataList = new ArrayList();
List sqlServerDataList = new ArrayList();
try {
//获取Oracle数据库连接
oracleConnection = JdbcUtil.getOracleConnection();
//获取MySql数据库连接
mySqlConnection = JdbcUtil.getMySqlConnection();
//获取SQLServer数据库连接
sqlServerConnection =JdbcUtil.getSqlServerConnection();
String oracleDb_Sql = "SELECT * FROM LEAD_OAMS_DBSOURCES";
String mySqlDb_Sql = "SELECT * FROM LEAD_OAMS_APPLICATIONS";
String sqlServerDb_Sql = "SELECT * FROM T_DEMO";
pstmtOracle = oracleConnection.prepareStatement(oracleDb_Sql);
pstmtMySQL = mySqlConnection.prepareStatement(mySqlDb_Sql);
pstmtSqlServer = sqlServerConnection.prepareStatement(sqlServerDb_Sql);
//执行查询,查询结果存储到ResultSet结果集中
rsOracle = pstmtOracle.executeQuery();
rsMySQL = pstmtMySQL.executeQuery();
rsSqlServer = pstmtSqlServer.executeQuery();
//循环结果集中的数据
while(rsOracle.next()){
Map oracleDataMap = new LinkedHashMap();
//取出结果集中的数据,每一行数据映射成一个map集合
oracleDataMap.put("resourceid", rsOracle.getString("RESOURCEID"));
oracleDataMap.put("dbsource_name", rsOracle.getString("DBSOURCE_NAME"));
oracleDataMap.put("dbsource_type", rsOracle.getString("DBSOURCE_TYPE"));
//将代表每一行数据的Map集合添加到List集合中
oracleDataList.add(oracleDataMap);
}
while(rsMySQL.next()){
Map mySqlDataMap = new LinkedHashMap();
mySqlDataMap.put("resourceid", rsMySQL.getString("resourceid"));
mySqlDataMap.put("app_name", rsMySQL.getString("app_name"));
mySqlDataList.add(mySqlDataMap);
}
while(rsSqlServer.next()){
Map sqlServerDataMap = new LinkedHashMap();
sqlServerDataMap.put("id", rsSqlServer.getString("id"));
sqlServerDataMap.put("name", rsSqlServer.getString("name"));
sqlServerDataList.add(sqlServerDataMap);
}
//将数据集合存储到request对象发送到页面进行显示
request.setAttribute("oracleDataList", oracleDataList);
request.setAttribute("mySqlDataList", mySqlDataList);
request.setAttribute("sqlServerDataList", sqlServerDataList);
//跳转到JNDITest.jsp页面显示数据
request.getRequestDispatcher("/JNDITest.jsp").forward(request, response);
} catch (SQLException e) {
e.printStackTrace();
}finally{
//释放资源
JdbcUtil.release(oracleConnection, pstmtOracle, rsOracle);
JdbcUtil.release(mySqlConnection, pstmtMySQL, rsMySQL);
JdbcUtil.release(sqlServerConnection, pstmtSqlServer, rsSqlServer);
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request,response);
}
}
JNDITest.jsp页面代码如下:
JNDI测试
从Oracle数据库中取出来的数据
${oracleDataMap.resourceid}---${oracleDataMap.dbsource_name}---${oracleDataMap.dbsource_type}
从mySql数据库中取出来的数据
${mySqlDataMap.resourceid}---${mySqlDataMap.app_name}
从sqlServer数据库中取出来的数据
${sqlServerDataMap.id}---${sqlServerDataMap.name}
运行结果如下: