jndi+多数据源+事务处理

1:介绍jndi  并且 非全局的JNDI配置:他只针对某一个Web项目的数据源的配置

一、JNDI:

JNDI就是(java Naming and Directory Inteface)java名称目录接口。

JNDI的作用:就是将资源引入到服务器中。可以将JNDI当成一个仓库。将Java对象放入到JNDI中去。

二、数据源的由来:

在java开发中,使用JDBC操作数据库的几个步骤:

1.使用Class.forName(类的全路径名称):用于加载数据库驱动程序。

2.获得数据库的Connection连接对象。DriverManager.getConnection()。

3.操作数据库:查询数据库,或者更新数据库内容,

4.关闭数据库连接:使用close方法。

注意:每次获取一个数据库连接的要经过这4个步骤,但是其中【1】,【2】,【4】是所有操作数据库的公共操作,只有【3】是操作数据库的不同步骤。并且获得数据库的connection对象和关闭数据库的连接都是要一定的时间。造成性能较差。

如果我们一开始就有已经创建好了多个connection对象,放在一个公共地方,当有一个连接数据库的请求,就从这个公共地方中取出一个connection,操作数据库,操作完成数据库,不关闭connection,而是放入到公共仓库中去,这就出现了数据库连接池的东西,就是存放多个Connection对象的地方。

三、使用JNDI配置数据库的连接池:有两种方式:(全局的JNDI配置和非全局的JNDI配置)

(一)非全局的JNDI配置:他只针对某一个Web项目的数据源的配置

1.导入要链接数据库的jar包文件。

例如sqlserver导入:sqljdbc4.jar包

oracle导入:ojdbc14.jar包

mySQL导入:mysql-connector-java-5.0.8.jar包

2.在JNDI中配置数据库的连接池:

在WEB项目中的的META-INF中创建一个context.xml文件。用于设置数据库的连接池信息

[html]  view plain  copy
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <Context>  
  3. <!--       其中Resource标签就是配置资源,他的属性值如下:  
  4.   |- name:表示以后要查找的名称。通过此名称可以找到DataSource,此名称任意更换,但是程序中最终要查找的就是此名称,  
  5.            为了不与其他的名称混淆,所以使用jdbc/oracle,现在配置的是一个jdbc的关于oracle的命名服务。  
  6.   |- auth:由容器进行授权及管理,指的用户名和密码是否可以在容器上生效,可以使用Container  
  7.   |- type:此名称所代表的类型,现在为javax.sql.DataSource(不用变)  
  8.   |- maxActive:表示一个数据库在此服务器上所能打开的最大连接数  
  9.   |- maxIdle:表示一个数据库在此服务器上维持的最小连接数  
  10.   |- maxWait:最大等待时间。10000毫秒  
  11.   |- username:数据库连接的用户名  
  12.   |- password:数据库连接的密码  
  13.   |- driverClassName:数据库连接的驱动程序  
  14.   |- url:数据库连接的地址  
  15. -->  
  16. <!--配置Oracle数据库的JNDI数据源-->  
  17. <Resource   
  18.         name="jdbc/oracle"  
  19.         auth="Container"   
  20.         type="javax.sql.DataSource"  
  21.         maxActive="100"   
  22.         maxIdle="30"   
  23.         maxWait="10000"  
  24.         username="lead_oams"   
  25.         password="p"  
  26.         driverClassName="oracle.jdbc.driver.OracleDriver"  
  27.         url="jdbc:oracle:thin:@192.168.1.229:1521:lead"/>  
  28.   
  29. <!--配置MySQL数据库的JNDI数据源-->  
  30. <Resource   
  31.         name="jdbc/mysql"  
  32.         auth="Container"   
  33.         type="javax.sql.DataSource"  
  34.         maxActive="100"   
  35.         maxIdle="30"   
  36.         maxWait="10000"  
  37.         username="root"   
  38.         password="root"  
  39.         driverClassName="com.mysql.jdbc.Driver"  
  40.         url="jdbc:mysql://192.168.1.144:3306/leadtest?useUnicode=true&amp;characterEncoding=utf-8"/>  
  41.   
  42. <!--配置SQLServer数据库的JNDI数据源-->  
  43. <Resource   
  44.         name="jdbc/sqlserver"  
  45.         auth="Container"   
  46.         type="javax.sql.DataSource"  
  47.         maxActive="100"   
  48.         maxIdle="30"   
  49.         maxWait="10000"  
  50.         username="sa"   
  51.         password="123456"  
  52.         driverClassName="com.microsoft.sqlserver.jdbc.SQLServerDriver"  
  53.         url="jdbc:sqlserver://192.168.1.51:1433;DatabaseName=demo"/>  
  54. </Context>  
3.在web.xml文件中进行配置( 可有可无 ):如果有的话:
[html]  view plain  copy
  1. <resource-ref>  
  2.     <description>my DB Connection</description>  
  3.     <res-ref-name><span style="font-family: Arial, Helvetica, sans-serif;">jdbc/sqlserver</span><span style="font-family: Arial, Helvetica, sans-serif;"></res-ref-name>这个名字要与context.xml中的name一样</span>  
  4.     <res-type>javax.sql.DataSource</res-type>  
  5.     <res-auth>Container</res-auth>  
  6. </resource-ref>  
4.如果使用Spring的情况下:在applicationContext.xml或者自己的Spring的配置文件中:导入JNDI的配置信息

//获得content.xml中JNDI配置的数据库的连接池信息。jndi-name必须与JNDI中的name值一样

<jee:jndi-lookup id="dataSource"  jndi-name="jdbc/sqlserver" />

//使用JdbcTemplate操作数据库。

<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource" />
    </bean>

在java文件中使用IOC得到jdbcTemplate对象。最后使用JdbcTemplate操作数据库

5.如果不使用Spring的话:可以在java文件中使用:

Context initContext = new InitialContext();

Context envContext  = (Context)initContext.lookup("java:/comp/env");//固定,不需要修改

 DataSource ds = (DataSource)envContext.lookup(jdbc/sqlserver);


------------------------------代码演示--------------------------------------------

dbconfig.properties

#1代表生产模式,2代表开发模式
#数据库模式
dbType=oracle
#是否验证IP
validateIp=true
#是否验证用户重复登录
validateUser=true
flag=1
driverClassName=oracle.jdbc.driver.OracleDriver
#driverClassName=com.mysql.jdbc.Driver
url=jdbc:oracle:thin:@127.0.0.1:1521:orcl
#url=jdbc:mysql://127.0.0.1:3306
#以下各JNDI配置,在TOMCAT连接池中必须都有对应配置,否则系统会报错
#数据源格式,jndi名称:用户名:密码
#第一个
first_jndi_user=first:first:first
#第二个
second_jndi_user=secode:second:secode

DBService.java

import java.io.InputStream;
import java.sql.DriverManager;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import javax.naming.InitialContext;
import javax.naming.NameNotFoundException;
import javax.sql.DataSource;

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;

import common.DBConstant;



/**
 * 数据库操作封闭对象
 */
public class DBService {
	//生产模式下标识
	private static final String PRODUCT = "1";
	//开发模式下标识
	private static final String DEVELOPMENT = "2";
	//jndi前缀
	private static final String JNDI_PREFIX = "java:comp/env/";
	
	/**平台下所有数据库用户**/
	private static final String FIRST= "first";
	private static final String SECOND= "second";
	
	//默认是生产环境
	private static String flag = "1";
	private static String driverClassName = "";
	private static String url = "";
	
	private static Map<String, JdbcTemplate> jdbcTemplateMap = new HashMap<String, JdbcTemplate>();
	private static Map<String, TransactionTemplate> transactionTemplateMap = new HashMap<String, TransactionTemplate>();
	
	private static ArrayList<String> valueList = new ArrayList<String>();
	
	static {
		InputStream inStream = DBService.class.getClassLoader().getResourceAsStream("dbconfig.properties");  
		try {
			Properties prop = new Properties(); 
			prop.load(inStream);
			inStream.close();
			
			//提取所有属性配置
			for(Object obj : prop.keySet()) {
				if(!"".equals(obj.toString()) && null!=prop.get(obj)) {
					String key = obj.toString().trim();
					String value = prop.get(key).toString().trim();
					if("dbType".equals(key)){
						if("mysql".equals(value)){
							DBConstant.IS_MYSQL=true;
						}else{
							DBConstant.IS_MYSQL=false;
						}
						DBConstant.doDBConfig();
					}
					if("validateIp".equals(key)){
						if("true".equals(value)){
							DBConstant.IS_TEST_IP=true;
						}else{
							DBConstant.IS_TEST_IP=false;
						}
					}
					if("validateUser".equals(key)){
						if("true".equals(value)){
							DBConstant.IS_TEST_USER=true;
						}else{
							DBConstant.IS_TEST_USER=false;
						}
					}
					
					if("flag".equals(key)) {
						flag = value;
					}else if("driverClassName".equals(key)) {
						driverClassName = value;
					}else if("url".equals(key)) {
						url = value;
					}else if(key.endsWith("_jndi_user")) {
						int index = value.indexOf(":");
						if(index>0 && value.split(":").length==3) {//jndi,用户名,密码都不为空。
							valueList.add(value);
						}
					}
				}
			}
			//将jndi的信息写入一份到dbconfig.pro中是方便在这里使用,这样可以不用定义死一个,可以全部加载
			for(int i=0; i<valueList.size(); i++) {
				String value = valueList.get(i);
				String jndiName = value.split(":")[0];
				String username = value.split(":")[1];
				String password = value.split(":")[2];
				DataSource ds = null;
				if(flag.equals(PRODUCT)) {//如果是生产模式,通过jndi来配置数据源。
					InitialContext ctx = new InitialContext();
					ds = (DataSource) ctx.lookup(JNDI_PREFIX+jndiName);
					ctx.close();
				}else {
					DriverManagerDataSource dds = new DriverManagerDataSource();
					dds.setDriverClassName(driverClassName);
					dds.setUrl(url);
					dds.setUsername(username);
					dds.setPassword(password);
					ds = dds;
				}
//				JdbcTemplate jdbcTemplate = new JdbcTemplate(ds);
				JdbcTemplate jdbcTemplate = new CaseInsensitiveJdbcTemplate(ds);
				jdbcTemplateMap.put(username, jdbcTemplate);
				
				DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager(
						ds);
				TransactionTemplate transactionTemplate = new TransactionTemplate(
						dataSourceTransactionManager);
				transactionTemplateMap.put(username, transactionTemplate);
			}
		}catch(NameNotFoundException e) {
			System.out.println("未找到对应的JNDI配置!");
		}catch(Exception e) {
			e.printStackTrace();
		}
	}

	private DBService() {

	}

	/**
	 * 获取jdbcTemplate
	 * @param username
	 * @return
	 */
	public static JdbcTemplate getJdbcTemplate(String username) {
		JdbcTemplate jdbcTemplate = null;
		if(jdbcTemplateMap.containsKey(username)) {
			jdbcTemplate = jdbcTemplateMap.get(username);
		}else {
			throw new Error("未配置"+username+"下的数据源!");
		}
		return jdbcTemplate;
	}
	
	/**
	 * 获取jdbcTemplate-first
	 * @return
	 */
	public static JdbcTemplate getJdbcTemplate4First() {
		return getJdbcTemplate(FIRST);
	}

	/**
	 * 获取jdbcTemplate-second
	 * @return
	 */
	public static JdbcTemplate getJdbcTemplate4Second() {
		return getJdbcTemplate(SECOND);
	}
	
	
	/**
	 * 获取TransactionTemplate
	 * @param username
	 * @return
	 */
	public static TransactionTemplate getTransactionTemplate(String username) {
		TransactionTemplate TransactionTemplate = null;
		if(transactionTemplateMap.containsKey(username)) {
			TransactionTemplate = transactionTemplateMap.get(username);
		}else {
			throw new Error("未配置"+username+"下的数据源!");
		}
		return TransactionTemplate;
	}
	
	/**
	 * 获取TransactionTemplate-first
	 * @param username
	 * @return
	 */
	public static TransactionTemplate getTransactionTemplate4First() {
		return getTransactionTemplate(FIRST);
	}

	/**
	 * 获取TransactionTemplate-second
	 * @param username
	 * @return
	 */
	public static TransactionTemplate getTransactionTemplate4Second() {
		return getTransactionTemplate(SECOND);
	}
/**
	 * 执行事务
	 * @param sqlArray  sql数组
	 * @param transactionTemplate 事务操作模版类
	 * @param jdbcTemplate  jdbc基本操作类
	 */
	public static void execTransaction(final String[] sqlArray, TransactionTemplate transactionTemplate,
			final JdbcTemplate jdbcTemplate) {
		Object result = transactionTemplate.execute(new TransactionCallback() {
			public Object doInTransaction(TransactionStatus status) {
				jdbcTemplate.batchUpdate(sqlArray);
				return null;
			}
		});
	}
/**
	 * @param sqlArray  sql数组
	 * @param sqlParameter[sqlIndex][paramIndex]  sql参数数组sqlIndex对应sql语句索引,paramIndex对应参数数组,例如:sqlParameter[0]=new Object[]{1, "23", 13.3f};
	 * @param transactionTemplate 事务操作模版类
	 * @param jdbcTemplate  jdbc基本操作类
	 * 
	 * 	 * TransactionTemplate类在调用时主要用到的方法为execute(TransactionCallback action)。有返回值的回调接口
	 * 其中TransactionCallback为我们的回调接口,它只有一个方法: T doInTransaction(TransactionStatus status),
	 * 这个方法内是有事务的。通常我们的数据库查询操作就在这个方法里完成。
	 * 方法入参TransactionStatus接口doInTransaction方法的唯一入参是TransactionStatus,它常用于查看我们当前的事务状态,它有两个常用的方法: 
	 * 1. createSavepoint():创建一个记录点。 
	 * 2. rollbackToSavepoint(savepoint):将事务回滚到特定记录点,这样从回滚处到记录点范围内所有的数据库操作都会失效。
	 * ---不推荐使用这一组方法,可以使用TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
	 * 无返回值的接口TransactionCallback
	 * 另外,doInTransaction是有返回值的,如果我们不需要返回值,我们可以使用TransactionCallback接口的一个子类TransactionCallbackWithoutResult,
	 * 它对应的抽象方法doInTransactionWithoutResult(TransactionStatus status)是没有返回值的。
	 */
	public static void execTransaction(final String[] sqlArray, final Object[][] sqlParameter, TransactionTemplate transactionTemplate, final JdbcTemplate jdbcTemplate) {
		Object result = transactionTemplate.execute(new TransactionCallback() {
			
			//doInTransaction 跟踪源码发现,A RuntimeException thrown by the callback is treated as application exception that enforces a rollback
			//意思是由回调抛出一个RuntimeException作为应用程序异常执行回滚 。 如:throw new RuntimeException();         
			//可以自己定义一个类来捕捉RuntimeException  
			// 或者  TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();   来手动回滚,上层就不同处理了
			public Object doInTransaction(TransactionStatus status) {
				Object ob=1;
				try {
					for(int i=0;i<sqlArray.length;i++){
						jdbcTemplate.update(sqlArray[i], sqlParameter[i]);
					}
				} catch (Exception e) {
					ob=2;
					TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
				}
				return ob;
			}
		});
	}
	


DBConstant.java

public class DBConstant {
	
	public static boolean IS_MYSQL = true;
	public static boolean IS_TEST_IP = true;
	public static boolean IS_TEST_USER = true;

	public static final String USER_NAME = "root";
	public static String FPOSTMAN_ROWNUM = " @rownum:=@rownum+1 ";
	public static String FPOSTMAN_ROWNUM_DECLARE = " @rownum:=0, ";
	public static String FPOSTMAN_ROWNUM_WHERE = " limit ";
	public static String FPOSTMAN_CONCAT_WS = "CONCAT_WS";
	public static void doDBConfig(){
		if(!IS_MYSQL){
			FPOSTMAN_ROWNUM=" rownum ";
			FPOSTMAN_ROWNUM_DECLARE="";
			FPOSTMAN_ROWNUM_WHERE=" and rownum< ";
			FPOSTMAN_CONCAT_WS=USER_NAME+".FPOSTMAN_CONCAT_WS";
		}
	}
}



CaseInsensitiveJdbcTemplate.java

public class CaseInsensitiveJdbcTemplate extends JdbcTemplate {
	protected RowMapper getColumnMapRowMapper() {
		return new MyColumnMapRowMapper();
	}

	public CaseInsensitiveJdbcTemplate(DataSource dataSource, boolean lazyInit) {
		super(dataSource, lazyInit);
	}

	public CaseInsensitiveJdbcTemplate(DataSource dataSource) {
		super(dataSource);
	}

	public CaseInsensitiveJdbcTemplate() {
		
	}
	
	private class MyColumnMapRowMapper extends ColumnMapRowMapper {

		@Override
		protected Map createColumnMap(int columnCount) {
			// TODO Auto-generated method stub
			return new CaseInsensitiveHashMap(columnCount);
		}
	}
	
	private static class CaseInsensitiveHashMap extends HashMap
	{
	  public CaseInsensitiveHashMap(int columnCount)
	  {
		  super(columnCount);
	  }

	  public boolean containsKey(Object key)
	  {
	    return super.containsKey(key.toString().toUpperCase());
	  }

	  public Object get(Object key)
	  {
	    return super.get(key.toString().toUpperCase());
	  }

	  public Object put(Object key, Object value)
	  {
	    return super.put(key.toString().toUpperCase(), value);
	  }

	  public void putAll(Map m)
	  {
	    Iterator iter = m.keySet().iterator();
	    while (iter.hasNext()) {
	      Object key = iter.next();
	      Object value = m.get(key);
	      put(key, value);
	    }
	  }

	  public Object remove(Object key)
	  {
	    return super.remove(key.toString().toUpperCase());
	  }
	}
}

再封装一个DBService的操作类 DBAllService

import org.springframework.jdbc.core.JdbcTemplate;

/**
 * 数据库操作封闭对象
 */
public class DBAllService{
	public static JdbcTemplate getJdbcTemplateFirst(){
		return util.DBService.getJdbcTemplate4First();
	}
	
	public static void execTransaction(String[] sqlArray){
		util.DBService.execTransaction(sqlArray, 
				util.DBService.getTransactionTemplate4First(), 
				util.DBService.getJdbcTemplate4First());
	}
	
	public static void execTransaction(String[] sqlArray,Object[][] p){
		util.DBService.execTransaction(sqlArray, p,
				util.DBService.getTransactionTemplate4First(), 
				util.DBService.getJdbcTemplate4First());
	}
	
	public static JdbcTemplate getJdbcTemplate4Second(){
		return util.DBService.getJdbcTemplate4Second();
	}
}


如有不对,请指出
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大鑫不列迭

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值