完整JavaWeb项目笔记 第七部分-JNDI数据源配置及DBUtil封装

一 何为JNDI

  按百度百科的说法,是SUN公司提供的一种标准的Java命名系统接口。呃……再细点说我也不知道该说点啥了。因为我也只是在配置数据源的时候才会用上,或许都不一定会用上。

  那说说为什么会用到JNDI来配置数据源,如果不用JNDI我们在做Java开发的时候,自然而然的会选择JDBC,然而JDBC配置有其天然的缺陷,因为数据源的URL、数据源类型或者其他配置参数都有可能随环境变动而发生改变。

  对于程序员来说,我们更为关心的应该是业务需求的实现,而非配置上的管理,这就有了配置透明的需求,而JNDI则很好的解决了这些问题,它把这些问题转移到J2EE容器来管理,开发者仅仅需要根据需求来实现设计即可。

  回忆一下假如我们使用的是如Mybatis这样的ORM框架呢?似乎也只需要进行一些简单的配置即可,给我一个使用JNDI的理由:针对不同的应用,或者不同类型的数据源,或者其他更为复杂的集群配置管理,我需要更为统一的、标准化的实现,JNDI是由J2EE容器提供的,是J2EE的规范之一。标准、规范、约定,这些词是我考量技术选型的重要指标。

二 为单独的项目JNDI配置

  JNDI数据源配置大致分为全局配置及局部配置,其配置方法略有不同,更为具体的介绍读者可自行搜索。这里我选择为Web项目单独配置,这样做可能更好的对配置进行管理,而且不会与Tomcat服务器发生过多交集,解耦、扩展、维护,这三个词也是我选择方案时的重要参考。

  配置上只要思路明确了,就比较好实现,这里我简单介绍下当前的项目配置:

  1. 数据库为Mysql
  2. 数据库连接池选用阿里的Druid

  首先进行单项目的JNDI数据源配置,需要编辑一份Context.xml配置文件,虽然我仅使用了Mysql数据库,但是为了以后的扩展需要,我依然配置了Oracle、SqlServer数据库配置,简单贴上配置文件,涉及敏感信息的部分我使用*来替代:

<?xml version="1.0" encoding="UTF-8"?>
<Context>
     <!-- 使用阿里巴巴的DruidDataSource配置针对Oracle数据库的JNDI数据源 -->
     <Resource 
         name="jdbc/OracleDataSource"
         factory="com.alibaba.druid.pool.DruidDataSourceFactory"
         auth="Container"
         type="javax.sql.DataSource"
         driverClassName="oracle.jdbc.OracleDriver"
         url="jdbc:oracle:thin:@*(IP):*(PORT):*(DBNAME)"
         username="*"
         password="*"
         maxActive="50"
         maxWait="10000"
         removeabandoned="true"
         removeabandonedtimeout="60"
         logabandoned="false"
         filters="stat"/>
         
     <!-- 使用阿里巴巴的DruidDataSource配置针对MySQL数据库的JNDI数据源 -->
      <Resource 
         name="jdbc/MysqlDataSource"
         factory="com.alibaba.druid.pool.DruidDataSourceFactory"
         auth="Container"
         type="javax.sql.DataSource"
         driverClassName="com.mysql.jdbc.Driver"
         url="jdbc:mysql://127.0.0.1:3306/*(DBNAME)?useUnicode=true&amp;characterEncoding=utf-8"
         username="*"
         password="*"
         maxActive="50"
         maxWait="10000"
         removeabandoned="true"
         removeabandonedtimeout="60"
         logabandoned="false"
         filters="stat"/>
         
     <!--使用阿里巴巴的DruidDataSource配置针对SQLServer数据库的JNDI数据源-->
     <Resource 
         name="jdbc/SqlServerDataSource"
         auth="Container"
         factory="com.alibaba.druid.pool.DruidDataSourceFactory" 
         type="javax.sql.DataSource"
         driverClass="com.microsoft.sqlserver.jdbc.SQLServerDriver"
         url="jdbc:sqlserver://*(IP):*(PORT);DatabaseName=*"
         username="*" 
         password="*"
         maxActive="50"
         maxWait="10000"
         removeabandoned="true"
         removeabandonedtimeout="60"
         logabandoned="false"
         filters="stat"/>
 </Context>

  因为是单独为当前项目所作的配置,所以我们需要在META-INF目录下放置这个配置文件:

context配置文件位置

三 DBUtil封装

  配置完数据源,我们需要获取数据源,并从其获取数据库连接,JNDI对外提供的访问方法如下:

Context ctx = new InitialContext();
ctx.lookup("NAME");

  这里的NAME指的就是Context.xml中配置的Resource.name节点属性值, 我们需要封装一个工具类来获取数据源,并且从数据源获取数据库连接,为便于程序编写,还需提供对连接的关闭等管理方法,综上实现如下:

package com.bubbling.util;

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;
import com.bubbling.common.Constant;

/**
 * 数据源操作工具,实现数据源对象的初始化操作,对外提供数据库连接,及关闭连接等公共访问方法
 * 
 * @author 胡楠
 *
 */
public class DBUtil
{
	private static final String STR_MYSQL_DATASOURCE_NAME = "java:comp/env/jdbc/MysqlDataSource";
	private static final String STR_ORACLE_DATASOURCE_NAME = "java:comp/env/jdbc/OracleDataSource";
	private static final String STR_SQLSERVER_DATASOURCE_NAME = "java:comp/env/jdbc/SqlServerDataSource";

	private static DruidDataSource dsMySql = null;
	private static DruidDataSource dsOracle = null;
	private static DruidDataSource dsSqlServer = null;

	static
	{
		try
		{
			Context ctx = new InitialContext();
			if (Constant.B_IS_MYSQL)
			{
				dsMySql = (DruidDataSource) ctx.lookup(STR_MYSQL_DATASOURCE_NAME);
			}
			if (Constant.B_IS_ORACLE)
			{
				dsOracle = (DruidDataSource) ctx.lookup(STR_ORACLE_DATASOURCE_NAME);
			}
			if (Constant.B_IS_SQLSERVER)
			{
				dsSqlServer = (DruidDataSource) ctx.lookup(STR_SQLSERVER_DATASOURCE_NAME);
			}
		}
		catch (NamingException e)
		{
			e.printStackTrace();
		}
	}

	public static Connection getConnection() throws SQLException
	{
		if (Constant.B_IS_MYSQL)
		{
			return getMySqlConnection();
		}
		if (Constant.B_IS_ORACLE)
		{
			return getOracleConnection();
		}
		if (Constant.B_IS_SQLSERVER)
		{
			return getSqlServerConnection();
		}

		throw new SQLException("无可用数据连接,检查数据源配置!");
	}

	private static Connection getMySqlConnection() throws SQLException
	{
		return dsMySql.getConnection();
	}

	private static Connection getOracleConnection() throws SQLException
	{
		return dsOracle.getConnection();
	}

	private static Connection getSqlServerConnection() throws SQLException
	{
		return dsSqlServer.getConnection();
	}

	public static void close(Connection conn)
	{
		close(conn, null, null);
	}

	public static void close(Connection conn, Statement st)
	{
		close(conn, st, null);
	}

	public static void close(Connection conn, Statement st, ResultSet rs)
	{
		if (rs != null)
		{
			try
			{
				rs.close();
			}
			catch (Exception e)
			{
				e.printStackTrace();
			}
			rs = null;
		}
		if (st != null)
		{
			try
			{
				st.close();
			}
			catch (Exception e)
			{
				e.printStackTrace();
			}
		}

		if (conn != null)
		{
			try
			{
				conn.close();
			}
			catch (Exception e)
			{
				e.printStackTrace();
			}
		}
	}
}

  注意,这里有几点需要留心:

  1. 对JNDI的命名空间访问,其完整访问名为“java:comp/env/jdbc/MysqlDataSource”,而Conext.xml配置中的name节点属性名为“jdbc/MysqlDataSource”,如果读者发现实例化数据源时报错,可以先对访问名进行检查;
  2. 按网上的一些说法,需要对web.xml进行编辑,引入JDNI配置,讲真,不需要;
  3. DBUtil中我使用了一个全局上下文Constant.java,这是一个静态参数配置类,在前几部分我也没有提到过,因为这里放置了整个项目中涉及到的参数配置,包括静态字符串、状态标志位等等,因各人逻辑实现习惯不一样,其内容可有可无,可多可少,所以我没有细说。

四 DBUtil缺陷

  虽然我们依着JNDI的配置封装了DBUtil,但是这里是有问题的,因为数据库连接仅仅在应用部署启动后才能获取。这对开发过程中的测试、调试是一种阻碍。

  解决这个问题有很多方案可以实现,简单提供一种,在本地及服务器进行环境变量的配置,或者仅仅在Constans.java中进行参数配置,如果是开发模式则从JDBC获取连接,如果是服务端模式则从JNDI获取连接,其具体实现我不做介绍了。算了,以后有时间还是写一写吧……我总是觉得这些东西,说实在的有点low,自己都不是很能耐得住性子写下去。

猜你喜欢

转载自blog.csdn.net/o983950935/article/details/85384572