简单ORM论道

作者: _chubby | 来源:发表于2019-03-24 20:23 被阅读0次

ORM定义:对象关系映射(英语:(Object Relational Mapping,简称ORM,或O/RM,或O/R mapping),是一种程序技术,用于实现面向对象编程语言里不同类型系统的数据之间的转换

万物同源,始于JDBC
  • 加载JDBC驱动程序

  • 根据参数建立数据库连接

  • 执行查询

  • 处理结果集

  • 处理异常,关闭连接

其他所有框架对关系数据库的操作都是基于以上步骤的封装。

基于JdbcTemplate实现简单的ORM,ORM的关注点是表跟实体的映射。那么关注点就在于
  • 获取实体的定义

  • 获取表的定义

  • 实体跟表字段的一一映射

  • 将对实体的操作转化为sql进而转化为对表的操作

简单InsertBean实现

方法定义

/**
 * 往数据库指定的表插入一个BEAN的数据
 * @param tableName
 * @param keyId
 * @param dataBean
 * @return
 */
public Long insertBean(String tableName,String keyColumn,Object dataBean) throws DbException;

实现的模板方法

Map<String,Field> fieldMap = getClassFieldMap(bean.getClass());
List<String> fieldList = getTableFieldList(tableName);
Map<String,Object> dataMap = constructInsertMap(fieldMap, fieldList, keyColumn);
insertMap(dataMap,tableName)

insertMap(Map<String, ?> dataMap, String tableName) 具体实现

wrapInsertMap(dataMap); //对dataMap进行包装、再一次赋空值、日期处理等等
SimpleJdbcInsert insert = getSimpleJdbcInsert();    
insert.withTableName(tableName).usingColumns(getCols(dataMap));
insert.execute(insertMap);

更直白的描述

InsertBeanActivity.png

获取实体属性具体实现 getClassFieldMap(bean.getClass())

    /**
     * 取得类的属性名
     * @param clazz
     * @return
     */
    public static Map<String,Field> getClassFieldMap(Class<?> clazz){
        Map<String,Field> rtMap = null;
        if(clazz != null){
            if(null == rtMap){
                rtMap = new HashMap<String,Field>();
                Field[] fields = clazz.getDeclaredFields();
                if(fields != null && fields.length>0){
                    Field.setAccessible(fields,true);
                    Field f = null;
                    for (int j = 0; j < fields.length; j++) {
                        f = fields[j];
                        rtMap.put(f.getName().toLowerCase(), f);
                    }
                }
            }           
        }
        return rtMap;       
    }

获取表的字段具体实现(基于Oracle实现) getTableFieldList(tableName)

    public List<String> getTableFieldList(String tableName){       
        List<String> list = null;
        DbTable dbTable = getDbTableDefine(tableName);
        if(dbTable != null && dbTable.getFieldMap() != null && !dbTable.getFieldMap().isEmpty()){
            list = Arrays.asList(dbTable.getFieldMap().keySet().toArray(new String[dbTable.getFieldMap().keySet().size()]));
        }
        return list;
    }   
    
    public DbTable getDbTableDefine(String tableName){
        try {           
            return DbUtil.getDbTableDefine(tableName, this.getJdbcTemplate());
        } catch (Exception e) {         
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 获取Table的配置
     * @param tableName
     * @param conn
     * @return
     */
    public static DbTable getDbTableDefine(String tableName, JdbcTemplate jdbcTemplate){
        
        if(StringUtils.isEmpty(tableName)){
            return null;
        }
        WebRmInforesCacheService cacheService = SpringContextUtil.getBean("com.gpdi.infores.services.bs.WebRmInforesCacheServiceImpl");
        tableName = tableName.toUpperCase();
        DbTable dbTable = cacheService.getData("DbTable_"+tableName);
        if(dbTable == null){            
            Connection conn = null;//DataSourceUtils.getConnection(jdbcTemplate.getDataSource());
            
            ResultSet rsTable = null;//取表
            ResultSet rsField = null;//取字段
            
            ResultSet rsKey = null;//取主键
            
            ResultSet indexInfo = null;//索引
            
            Statement stmt = null;//字段注释
            ResultSet rsRemark = null;//字段注释
            try {               
                conn = jdbcTemplate.getDataSource().getConnection();
                DatabaseMetaData dm = conn.getMetaData();
                String catalog = conn.getCatalog();
                rsTable = dm.getTables(catalog, dm.getUserName(), tableName.toUpperCase(), new String[]{"TABLE", "SYNONYM"});
                if(rsTable.next()) {
                    tableName = rsTable.getString("TABLE_NAME");
                    if(StringUtils.isNotEmpty(tableName)){
                        tableName = tableName.toUpperCase();
                        dbTable = new DbTable();
                        dbTable.setUserName(dm.getUserName());
                        dbTable.setTableName(tableName);
                        LinkedHashMap<String,DbTableField> fields = new LinkedHashMap<String,DbTableField>();
                        //取主键
                        rsKey = dm.getPrimaryKeys(catalog, null, tableName);
                        String keyField = null;
                        while (rsKey.next()) {
                            keyField = rsKey.getString("COLUMN_NAME");//组合组键时、只要最后一个主键
                        }
                        //取字段
                        rsField = dm.getColumns(catalog, null, tableName, null);
                        while (rsField.next()) {
                            DbTableField f = new DbTableField();
                            
                            String fieldName = rsField.getString("COLUMN_NAME").toUpperCase();
                            f.setName(fieldName);//参数值可参考dm.getColumns(catalog, null, tableName, null)的帮助文档
                            f.setSqlType(new Integer(rsField.getString("DATA_TYPE")).intValue());//如:java.sql.Types.INTEGER
                            f.setTypeName(rsField.getString("TYPE_NAME"));//如:BIGINT
                            f.setSize(rsField.getString("COLUMN_SIZE"));//用户定义的字段长度
                            f.setBufferLength(rsField.getString("BUFFER_LENGTH"));//字段缓冲区大小
                            f.setDecimal(rsField.getString("DECIMAL_DIGITS"));//精度
                            f.setNullable("1".equals(rsField.getString("NULLABLE")));//取值0||1,1允许空值,0不允许空值
                            f.setDefaultValue(rsField.getString("COLUMN_DEF"));//默认值
                            f.setPosition(rsField.getString("ORDINAL_POSITION"));//在表中的位置
                            f.setRemark(rsField.getString("REMARKS"));//注释为空的、下面会有专门取注释
                            
                            if(StringUtils.isNotEmpty(keyField) && keyField.equalsIgnoreCase(fieldName)){
                                f.setPrimarykey(true);
                                dbTable.setKeyField(f);//主键字段
                            }
                            fields.put(fieldName.toLowerCase(),f);
                        }
                        //字段注释
                        String remarkSQL = "select c.* from all_col_comments c where lower(c.table_name) = lower('"+tableName+"')";
                        if(StringUtils.isNotEmpty(dm.getUserName()))
                            remarkSQL += " and c.owner='"+dm.getUserName()+"'";
                        stmt = conn.createStatement();
                        rsRemark = stmt.executeQuery(remarkSQL);
                        while(rsRemark.next()){
                            String fieldNmae = rsRemark.getString("COLUMN_NAME");
                            fields.get(fieldNmae.toLowerCase()).setRemark(rsRemark.getString("COMMENTS"));
                        }
                        //表名注释
                        remarkSQL = "select c.* from all_tab_comments c where lower(c.table_name) = lower('"+tableName+"')";
                        if(StringUtils.isNotEmpty(dm.getUserName())) remarkSQL += " and c.owner='"+dm.getUserName()+"'";
                        ResultSet rstRemark = stmt.executeQuery(remarkSQL);
                        if(rstRemark.next()){
                            dbTable.setTableCnName(rstRemark.getString("COMMENTS"));
                        }
                        dbTable.setFieldMap(fields);        
                        cacheService.cacheData("DbTable_"+tableName, dbTable);
                       
                    }
                }else{
                    dbTable = null;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }finally{
                close(rsRemark);
                close(stmt);
                close(indexInfo);
                close(rsKey);
                close(rsField);
                close(rsTable);
                close(conn);
            }
        }
            
        return dbTable;
    }
updateBean、deleteBean类似不再详述
selectBean

jdbcTemplate中实现了一个BeanPropertyRowMapper或者可以自己实现一个更加通用的RowMapper

好奇心害死猫--------------JdbcTemplate后面做了什么

几个重要的类与接口
  • SimpleJdbcInsertOperations 定义了一些简单插入基本操作的接口
  • AbstractJdbcInsert 抽象插入类、实现了几乎所有的插入相关操作
  • SimpleJdbcInsert 插入操作的具体实现、简单将请求委托给父类
  • TableMetaDataContext 保存与表相关的信息、提供一些插入构造参数的方法
关系------------只画出重要操作与属性
Relation.png
流程

insert.withTableName(tableName).usingColumns(getCols(dataMap))

注册需要插入的表至TableMeteDataContext、插入字段则保存至AbstractJdbcInsert的declaredColumns中

insert.execute(insertMap)

插入具体流程


InsertFlow.png

重要方法

  • createInsertString

    /**
       * Build the insert string based on configuration and metadata information
       * @return the insert string to be used
       */
      public String createInsertString(String... generatedKeyNames) {
          Set<String> keys = new LinkedHashSet<String>(generatedKeyNames.length);
          for (String key : generatedKeyNames) {
              keys.add(key.toUpperCase());
          }
          StringBuilder insertStatement = new StringBuilder();
          insertStatement.append("INSERT INTO ");
          if (getSchemaName() != null) {
              insertStatement.append(getSchemaName());
              insertStatement.append(".");
          }
          insertStatement.append(getTableName());
          insertStatement.append(" (");
          int columnCount = 0;
          for (String columnName : getTableColumns()) {
              if (!keys.contains(columnName.toUpperCase())) {
                  columnCount++;
                  if (columnCount > 1) {
                      insertStatement.append(", ");
                  }
                  insertStatement.append(columnName);
              }
          }
          insertStatement.append(") VALUES(");
          if (columnCount < 1) {
              if (this.generatedKeyColumnsUsed) {
                  logger.info("Unable to locate non-key columns for table '" +
                          getTableName() + "' so an empty insert statement is generated");
              }
              else {
                  throw new InvalidDataAccessApiUsageException("Unable to locate columns for table '" +
                          getTableName() + "' so an insert statement can't be generated");
              }
          }
          for (int i = 0; i < columnCount; i++) {
              if (i > 0) {
                  insertStatement.append(", ");
              }
              insertStatement.append("?");
          }
          insertStatement.append(")");
          return insertStatement.toString();
      }
    
  • createInsertTypes 不再贴出源码

  • executeInsertInternal

    if (logger.isDebugEnabled()) {
              logger.debug("The following parameters are used for insert " + getInsertString() + " with: " + values);
          }
          return getJdbcTemplate().update(getInsertString(), values.toArray(), getInsertTypes());
    

    update回到JdbcTemplate类一路封装最后执行public <T> T execute(PreparedStatementCreator psc, PreparedStatementCallback<T> action)方法

其他一些有意思的东西

  • 更大的自由度---定义表与实体类、表字段与实体类属性对应关系。Hibernate的Implicit Naming StrategyPhysical Naming Strategy
  • 将sql转移到别的地方。MybatisxmlHibernate的基于注解的NamedQueries
  • 基于方法名生成简单的sql。类似JPA 或者像我们系统根据自定义表单、表格生成稍微复杂的自定义sql
  • 将表定义、执行相关信息等进行缓存。一级二级缓存。

其他

相关文章

  • 简单ORM论道

    ORM定义:对象关系映射(英语:(Object Relational Mapping,简称ORM,或O/RM,或O...

  • SQLAlchemy 教程

    ORM( Object Relational Mapping) ORM,又称对象关系映射。简单来说,ORM将数据库...

  • flask-sqlalchemy介绍与安装

    简单来说flask-sqlalchemy是一套ORM框架。 至于什么是ORM框架呢? ORM是关系对象模型,能将数...

  • Java | 实现一个 ORM 真的很简单

    实现一个 ORM 到底多简单 Table of Contents 实现一个 ORM 到底多简单[#%E5%AE%9...

  • 简单理解ORM

    ORM的理解: ORM是“对象-关系-映射”的简称, 1.根据对象的类型生成表结构 2.将对象、列表的操作,转换为...

  • 自定义ORM框架

    一、ORM简单科普 所谓ORM,即对象-关系映射(Object/Relation Mapping),方便我们以操作...

  • Spring同时集成JPA与Mybatis

    ORM ORM的出现,使得关系型数据库映射成了对象;简单来说,有了ORM之后,JAVA程序员从面向JDBC编程转化...

  • .NET 常用中间件

    什么是中间件 简单讲,中间件就是非业务的技术类组件。 ORM ORM,Object Relational Mapp...

  • 3.7 Beego事务处理

    事务处理ORM 可以简单的进行事务操作

  • 轻量高效ORM之Dapper篇

    大家好,我是大成子。今天学习ORM框架之dapper 介绍 dapper是一个简单的对象关系映射框架(ORM---...

网友评论

    本文标题:简单ORM论道

    本文链接:https://www.haomeiwen.com/subject/pbkpvqtx.html