使用有限多例模式管理数据库结构信息

多例模式是一个类可以有多个实例的模式,它是单例模式的自然推广。它的特点是:

   1、该类可以有多个实例;

   2、由类本身创建管理它的实例;

   3、由类本身向外界提供它的实例。

JadePool使用有限多例模式管理数据库结构信息

在JadePool开源工具中,具体的说就是由DbCenter负责管理事务型数据库的数据库结构信息,由DbAccess负责管理非事务型数据库的数据库结构信息,它们共同遵守Db规划的方法。它们管理各自的Table、Field对象。本篇将以DbCenter为例,简要介绍数据库结构信息管理的实现过程。

为什么选择有限多例模式管理数据库结构信息?

这可能是最合理的选择,数据库结构信息作为整个软件系统中的共享资源,可能需要大量使用,因此这种资源应当驻留在内存中。DbCenter目前提供了四个实例,每个实例负责管理一个数据库的结构信息,软件系统在理论上可以同时最多管理四个数据库,通常情况下用户只需要一个实例,如果需要实现两个不同数据库的交互操作,用户可以实例化两个实例。使用有限多例模式保证了用户对几个不同数据库同时操作。

DbCenter做了哪些工作?

   1、根据用户请求如:ProcessVO构造方法的请求,创建对应的DbCenter实例,并初始化该实例,并将该实例提交给用户;

   2、实例化的类的属性,包括:数据库驱动器名称、数据库框架名称、数据库名称、数据库表的名称的集合。

   3、为数据库的每一个表创建Table对象;

   4、为每个表的字段创建Field对象。

通过以上四步工作,把整个数据库的结构信息完整地提取出来,供应用程序下一步调用。


以下是部分源代码,详细的原代码,可以下载JadePool-1.0-GBK的资源文件获取。

    synchronized private void init(Connection _con) {
        this.con = _con;
        try {
            //schema=con.getSchema();
            catalog = con.getCatalog();//数据库名
            dm = con.getMetaData();
            driverName = dm.getDriverName();
            Set<String> tableNameSet = new java.util.LinkedHashSet();//表名集合
            String[] types = {"TABLE"};

            ResultSet rs = dm.getTables(null, null, null, types);//获取数据库中所有表的名称
            while (rs.next()) {
                String tableName = rs.getString("TABLE_NAME");
                tableNameSet.add(tableName);
            }
            Object[] o = tableNameSet.toArray();
            tableNames_tmp = new String[o.length];
            tableNames = new String[o.length];
            for (int i = 0; i < o.length; i++) {
                tableNames_tmp[i] = (String) o[i].toString();
                tableNames[i] = (String) o[i].toString().toLowerCase();
            }

            if (tableNames_tmp != null) {
                for (int i = 0; i < tableNames_tmp.length; i++) {
                    initTableMap(tableNames_tmp[i]);//大写
                }
            }

            instance_times++;

        } catch (SQLException ex) {
            Logger.getLogger(DbCenter.class.getName()).log(Level.SEVERE, ex.getMessage(), ex);
        } finally {
            //con.close();共享con,不能关闭,由调用 Db实例.getCon()的用户关闭
        }

    }

    /**
     * 应当在此,还要初始化字段Field及各表的键值信息 注意事项: 使用ResultSet rs = dm.getColumns(catalog,
     * null, tableName, null);/使用derby数据库时,大小写是敏感的, 因此
     *
     * @param tableName的值,只能通过dm原型获取的值,这样造成了大小写问题,在运算过程中,不能转换大小写,只能在最后统一转换成小写,
     */
    synchronized private void initTableMap(String tableName) throws SQLException {
        Table table = getTable(tableName.toLowerCase());
        if (table == null) {
            table = new Table();
        }
        table.setName(tableName.toLowerCase());
        Set fieldSet = new java.util.LinkedHashSet();
        Set keySet = new java.util.LinkedHashSet();
        if (con != null) {
            ResultSet rs = dm.getColumns(catalog, null, tableName, null);//获取表中所有字段 //参考ResultSet rs = dm.getProcedureColumns(catalog, catalog, driverName, tableName);//?
            Map<String, Field> field_map = new LinkedHashMap();
            while (rs.next()) {
                String name = rs.getString("COLUMN_NAME");//参数值可参考dm.getColumns(catalog, null, tableName, null)的帮助文档
                fieldSet.add(lowerCase(name));
                Field f = new Field();
                f.setName(lowerCase(name));

                //DATA_TYPE int => SQL type from java.sql.Types

                /*
                 Each column description has the following columns:
                 TABLE_CAT String => table catalog (may be null)
                 TABLE_SCHEM String => table schema (may be null)
                 TABLE_NAME String => table name
                 COLUMN_NAME String => column name
                 DATA_TYPE int => SQL type from java.sql.Types
                 TYPE_NAME String => Data source dependent type name, for a UDT the type name is fully qualified
                 COLUMN_SIZE int => column size.
                 BUFFER_LENGTH is not used.
                 DECIMAL_DIGITS int => the number of fractional digits. Null is returned for data types where DECIMAL_DIGITS is not applicable.
                 NUM_PREC_RADIX int => Radix (typically either 10 or 2)
                 NULLABLE int => is NULL allowed.
                 columnNoNulls - might not allow NULL values
                 columnNullable - definitely allows NULL values
                 columnNullableUnknown - nullability unknown
                 REMARKS String => comment describing column (may be null)
                 COLUMN_DEF String => default value for the column, which should be interpreted as a string when the value is enclosed in single quotes (may be null)
                 SQL_DATA_TYPE int => unused
                 SQL_DATETIME_SUB int => unused
                 CHAR_OCTET_LENGTH int => for char types the maximum number of bytes in the column
                 ORDINAL_POSITION int => index of column in table (starting at 1)
                 IS_NULLABLE String => ISO rules are used to determine the nullability for a column.
                 YES --- if the column can include NULLs
                 NO --- if the column cannot include NULLs
                 empty string --- if the nullability for the column is unknown
                 SCOPE_CATALOG String => catalog of table that is the scope of a reference attribute (null if DATA_TYPE isn't REF)
                 SCOPE_SCHEMA String => schema of table that is the scope of a reference attribute (null if the DATA_TYPE isn't REF)
                 SCOPE_TABLE String => table name that this the scope of a reference attribute (null if the DATA_TYPE isn't REF)
                 SOURCE_DATA_TYPE short => source type of a distinct type or user-generated Ref type, SQL type from java.sql.Types (null if DATA_TYPE isn't DISTINCT or user-generated REF)
                 IS_AUTOINCREMENT String => Indicates whether this column is auto incremented
                 YES --- if the column is auto incremented
                 NO --- if the column is not auto incremented
                 empty string --- if it cannot be determined whether the column is auto incremented
                 IS_GENERATEDCOLUMN String => Indicates whether this is a generated column
                 YES --- if this a generated column
                 NO --- if this not a generated column
                 empty string --- if it cannot be determined whether this is a generated column

                 */


                String dataType = rs.getString("DATA_TYPE");
                f.setSqlType(new Integer(dataType).intValue());//如:java.sql.Types.INTEGER

                String type = rs.getString("TYPE_NAME");//如:BIGINT
                f.setTypeName(lowerCase(type));
                String position = rs.getString("ORDINAL_POSITION");//在表中的位置
                f.setPosition(position);


                String size = rs.getString("COLUMN_SIZE");//用户定义的字段长度
                f.setSize(size);

                String bufferLength = rs.getString("BUFFER_LENGTH");//字段缓冲区大小
                f.setBufferLength(bufferLength);


                String decimal = rs.getString("DECIMAL_DIGITS");//精度
                f.setDecimal(decimal);
                String defaultValue = rs.getString("COLUMN_DEF");
                f.setDefaultValue(defaultValue);
                String remark = rs.getString("REMARKS");
                f.setRemark(remark);
                String nullable = rs.getString("NULLABLE");//取值0||1,1允许空值,0不允许空值
                if ("0".equals(nullable)) {
                    f.setNullable(false);
                }
                if ("1".equals(nullable)) {
                    f.setNullable(true);
                }
                field_map.put(name.toLowerCase(), f);
            }

            table.setFieldMap(field_map);//字段名:Field对象的映射表;

            //获取字段名数组
            Object[] o = fieldSet.toArray();
            String[] fields = new String[o.length];
            for (int i = 0; i < o.length; i++) {
                fields[i] = ((String) o[i]).toLowerCase();

            }
            table.setFields(fields);

            //主键部分,开始
            ResultSet rsk = dm.getPrimaryKeys(catalog, null, tableName); //均通过新版SQL Server和MySQL的jdbc驱动的测试,返回所有主键  //ResultSet rsk = dm.getPrimaryKeys(catalog, "%", tableName);//早期版本的MySQL jdbc驱动程序中通过测试,返回所有主键  //
            while (rsk.next()) {
                String name = rsk.getString("COLUMN_NAME");//主键名
                keySet.add(lowerCase(name.toLowerCase()));//
            }
            Object[] k = keySet.toArray();
            String[] keys = new String[k.length];
            for (int i = 0; i < k.length; i++) {
                keys[i] = (String) k[i];
                field_map.get(keys[i]).setPrimarykey(true);//通过mssql、mysql、derby
            }
            table.setKeys(keys);
            //主键部分,结束

            ///给Field属性typeClassName赋值
            String squeryFieldTypeClassName = "select * from " + tableName.toLowerCase() + " where " + table.getFields()[0] + " is null";
            if (table.getKeys().length > 0) {
                squeryFieldTypeClassName = "select * from " + tableName.toLowerCase() + " where " + table.getKeys()[0] + " is null";
            }
            Statement stmt0 = con.createStatement();
            ResultSet rscname = stmt0.executeQuery(squeryFieldTypeClassName);
            ResultSetMetaData rsmd = rscname.getMetaData();
            for (int i = 1; i <= rsmd.getColumnCount(); i++) {
                String fieldNmae = rsmd.getColumnName(i);
                field_map.get(fieldNmae.toLowerCase()).setTypeClassName(rsmd.getColumnClassName(i));//通过mssql、mysql、derby
            }
            stmt0.close();
        }
        tableMap.put(tableName.toLowerCase(), table);//初始化Table
    }


在基于HashMap关系数据映射技术的JadePool的实现中,Db族类通过有限多例模式动态提取和管理数据库结构信息,是JadePool的大管家。这是HRM产品与ORM产品最根本的区别所在。是JadePool之所以做到高效、简洁、智能化的重要前提。

顺便说一下DbAccess负责提取管理非事务型数据库的数据库结构信息。

使用DbCenter和DbAccess的重要区别在于数据库是否支持事务,在事务型的数据库中,可以通过ResultSet rsk = dm.getPrimaryKeys(catalog, null, tableName);语句提取主键信息,而非事务的数据库目前不支持这种操作。目前JadePool将非事务型数据库的表的第一个字段当做主键对待。


如何实例化DbCenter?

ProcessVO是JadePool的核心类,主要用于实现数据库的CRUD或DML操作。目前,该类有三个构造函数

    1、ProcessVO();它将实例化DbCenter中的defaultDb实例
            2、ProcessVO(Connection con);它将实例化DbCenter中的userDb实例
            3、ProcessVO(Connection con, int connectionType);它将根据connectionType的值,如:根据cn.jadepool.sql.DbConnectionType.USING_DB_01实例化db_01,根据cn.jadepool.sql.DbConnectionType.USING_DB_02实例化db_02。

例如:

ProcessVO pvo=new ProcessVO();

Db db=pvo.getDb();

......

此时,用户可以通过db来访问数据库的结构信息、表的结构信息以及字段的结构信息。

如果您对JadePool感兴趣,可以通过以下网址下载jadepool-1.0-GBK

http://download.csdn.net/detail/wj800/5109413

http://www.jadepool.cn/down?id=1000







  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值