[反射机制]建立数据库连接

本文介绍如何使用反射机制和XML配置文件来动态建立数据库连接。首先定义数据源配置文件,利用jdom解析工具读取配置信息,通过反射创建数据源实例,并设置相关参数,最终实现数据库连接。

 

 

11. 利用反射机制建立数据库连接

解析数据库数据源配置文件(.xml),利用反射机制将配置文件中设置的数据源参数设置到 Class 类实例中,然后将该 Class 类实例强制转换成对应的数据源类。

 

1. 自定义数据源配置文件data-source-config.xml

简单提出一些约束条件。这些约束在解析该数据源配置文件时用到。

<?xml version="1.0" encoding="UTF-8"?>

 

<!-- quote from struts-config.xml -->

<!--

   

    数据源配置文件几点约束:

    1. <data-sources>节点下定义了一套数据源<data-source>[JDBC 2.0 Standard Extension]

    2. 节点拼写大小写敏感,目前有且只有三级节点

    3. 只允许有一个根节点<data-sources>

    4. 出现多个 id <data-source>节点,以第一个为准

   

    数据源查找方式:

    1. <data-source>节点数 < 1, 即节点数为0

       未设置数据源,报错

   

    2. 默认数据源方式

       <data-source>节点未设置 id 属性值或 id 属性值为null<data-source>

      

    3. 指定数据源方式

       匹配<data-source>节点 id 属性值

             

 -->

<data-sources>

    <data-source type="org.apache.commons.dbcp.BasicDataSource">

       <set-property property="driverClassName" value="com.mysql.jdbc.Driver" />

       <set-property property="url" value="jdbc:mysql://localhost:3306/mydb" />

       <set-property property="username" value="root" />

       <set-property property="password" value="root" />

       <set-property property="maxActive" value="30" />

       <set-property property="maxIdle" value="10" />

       <set-property property="maxWait" value="1000" />

    </data-source>

</data-sources>

 

 

2. 解析配置文件

利用 XML 解析工具包解析配置文件。本文采用 jdom 来解析配置文件。

全局变量:

/**

 * Copyright (c) 2011 Trusted Software and Mobile Computing(TSMC)

 * All right reserved.

 *

 * Created on Aug 19, 2011 6:37:38 PM

 * http://jarg.iteye.com/

 * Author: Jarg Yee <yeshaoting@gmail.com>

     */ 

 

 

    /** 配置文件路径 */

    private static final String CONFIG = "data-source-config.xml";

   

    /** 默认配置数据源 id 属性值 */

    private static final String DEFAULT_ID = "null";

   

    /** 配置文件节点属性名 */

    private static final String ATT_TYPE = "type";

    private static final String ATT_ID = "id";

    private static final String ATT_PROPERTY = "property";

    private static final String ATT_VALUE = "value";

    private static final String ATT_REF = "ref";

   

    /** 配置文件节点名 */

    private static final String NODE_DATA_SOURCES = "data-sources";

    private static final String NODE_DATA_SOURCE = "data-source";

    private static final String NODE_SET_PROPERTY = "set-property";

   

    /** 反射机制获取方法名前缀 */

    private static final String PREFIX_SET = "set";

    private static final String PREFIX_GET = "get";

   

    /** 二种基本类型 */

    private static final String TYPE_INT = "int";

    private static final String TYPE_LONG = "long";

   

 

2.1 初始化配置文件

加载自定路径的配置文件,该路径是相对于类加载目录的路径。

Eclipse C/S项目中相对于classpathbin目录,Eclipse B/S项目中相对于classpathWebRoot/classes目录。

关键代码: 

    /**

     * 初始化配置

     * */

    private void initConfig(String config)

    {

       InputStream in = this.getClass().getClassLoader().getResourceAsStream(config);

       SAXBuilder builder = new SAXBuilder();

      

      

       try

       {

           Document doc = builder.build(in);

           root = doc.getRootElement();

       }

       catch (Exception e)

       {

           System.out.println("配置文件" + config + "丢失");

           e.printStackTrace();

       }

    }

   

 

2.2 选择数据源节点

循环获取数据源节点<data-source>属性 id 值与传入的数据源节点 id 值匹配,获取对应的数据源节点。

关键代码:

    /**

     * 需要加上 "" ,否则无法与null匹配成功

     * 不知道什么原因

     * */ 

    for(int i=0; i<dataSources.size(); i++)

    {

       Element element = dataSources.get(i);

       if(id.equals("" + element.getAttributeValue(ATT_ID)))

       {

           dataSourceElement = element;

       }

    }

   

 

2.3 创建、设置、获取数据源实例

[创建数据源实例] 先获取数据源type属性值(声明数据源类完全限定名称),通过这个完全限定名称字符串创建 Class 类实例。

 

[设置数据源实例] 再获取当前数据源节点<datab-source>下所有属性设置节点<set-property>

然后获取每个属性设置节点<set-property>namevalue属性值,根据name值获取字段类型。

不同类型值转换过程通过getMatchValue()方法来实现,其用途是:

a. 利用反射机制获取对应的setter方法的方法getMethod(),需要setter方法参数类型

b. value属性值读出来的是String类型(可以看作是Object类型),需要转换成setter方法对应的类型值。

例如:org.apache.commons.dbcp.BasicDataSource.BasicDataSource setMinIdle()方法:

    public synchronized void setMinIdle(int minIdle)

 

[获取数据源实例] 最后,通过invoke()方法完成setter方法调用,将属性设置节点<set-property>属性值通过相应的setter方法设置到 Class 类实例相应的属性中。将 Class 类实例强制转换成 DataSource(所有数据源类的父类),返回。

 

关键代码:

    /**

     * 设置数据源数据

     * @param dataSourceElement Element - 选择的数据源节点

     * @throws Exception

     * */

    private DataSource newInstance(Element dataSourceElement) throws Exception

    {

       String type = dataSourceElement.getAttributeValue(ATT_TYPE);

       if(type == null)

       {

           throw new Exception("未定义数据源类型");

       }

 

       Class<?> clazz = Class.forName(type);

       Object obj = clazz.newInstance();

      

       List<Element> properties = dataSourceElement.getChildren(NODE_SET_PROPERTY);

      

       for(int i=0; i<properties.size(); i++)

       {

           Element property = properties.get(i);

           String name = property.getAttributeValue(ATT_PROPERTY);

           if(name == null)

           {

              throw new Exception("未定义属性名");

           }

          

           String value = property.getAttributeValue(ATT_VALUE);

           if(value == null)

           {

              throw new Exception("未定义属性值");

           }

          

           /** 为了获取参数类型 */

           Field field = clazz.getDeclaredField(name);

           Class<?> fieldType = field.getType();

          

           String methodName = getMethodName(PREFIX_SET, name);

           Method method = clazz.getMethod(methodName, fieldType);

           method.invoke(obj, getMatchValue(fieldType, value));

          

       }

       return (DataSource)obj;

    }

   

    /**

     * 获取与字段类型相匹配的值

     * */

    private Object getMatchValue(Class<?> fieldType, String value)

    {

      

       /**

        * 目前只有二类基本类型转换

        * */

       if(fieldType.getName().equals(TYPE_INT))

       {

           return Integer.parseInt(value);

       }

       else if(fieldType.getName().equals(TYPE_LONG))

       {

           return Long.parseLong(value);

       }

       else

           return value;

    }

   

 

2.4 建立数据库连接

通过上一步返回的数据源javax.sql.DataSource(这是一个接口)实例调用getConnection()方法建立数据库连接。

 

关键代码:

   

    /**

     * 获取指定数据源的数据库

     * @throws Exception

     * */

    protected static Connection getConnection(DataSourceConfig dataSourceConfig, String id)

           throws Exception

    {

       return dataSourceConfig.getDataSource(id).getConnection();

    }

   

 

2.5 提供获取数据库连接用户接口

封装具体的数据库内部实现,将相关方法权限设置为 protected

利用getConnection(String id)获取指定数据源的数据库连接。

 

关键代码:

 

    /**

     * 获取指定数据源的数据库连接

     * 不支持自定义用户名,密码方式

     * @param id String

     * */

    public Connection getConnection(String id)

    {

       Connection conn = null;

       try

       {

           conn = DataSourceConfig.getConnection(dataSourceConfig, id);

       }

       catch (Exception e)

       {

           e.printStackTrace();

       }

       return conn;

    }

   

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值