番外篇-1:databaseIdProvider的使用

本文介绍了MyBatis中databaseIdProvider的功能,解释了如何根据不同的数据库厂商执行不同的SQL语句。通过数据库厂商ID,MyBatis实现数据隔离,允许在mapper.xml中针对特定数据库配置SQL。当数据库标识与mapper配置匹配时,SQL语句才会被使用。虽然此功能在实际业务中使用较少,但了解其工作原理有助于更好地理解和定制MyBatis配置。
摘要由CSDN通过智能技术生成

其实刚开始的时候我也不知道是啥意思,一般也不会去用这个功能,不过今天在看源码的时候理解了这个问题,不过这个东西不会讲全部,主要是讲下怎么使用,官网的文档为

mybatis – MyBatis 3 | 配置

刚开始看的时候其实不知道啥意思,直到看到了源码才明白了

首先先解析下这个功能是在干啥,就是MyBatis 可以根据不同的数据库厂商执行不同的语句,这是官网原话,看起来可能有点懵,举个例子你就知道了

以下是xml里面关于数据库厂商的配置

<databaseIdProvider type="DB_VENDOR">
     <property name="MySQL" value="mysql"/>
      <property name="Oracle" value="oracle" />
</databaseIdProvider>

其中name是数据库的名字,而值是一个标识来的,而数据库名字是每个厂商自己返回的,获取代码在

org.apache.ibatis.mapping.VendorDatabaseIdProvider#getDatabaseProductName

看到代码你就明白了

private String getDatabaseProductName(DataSource dataSource) throws SQLException {
    Connection con = null;
    try {
      con = dataSource.getConnection();
      DatabaseMetaData metaData = con.getMetaData();
      return metaData.getDatabaseProductName();
    } finally {
      if (con != null) {
        try {
          con.close();
        } catch (SQLException e) {
          // ignored
        }
      }
    }
  }

其实就是jdbc的一些规范进行获取的

而value是用来干啥的呢,就是用来配置在你mapper.xml里面的标识

看个例子

   <select id="selectList" resultType="com.zxc.study.test.bean.User" databaseId="mysql">
        select * from user
    </select>

上面这个databaseId就代表这个sql语句只有当你当前的数据库返回的是MySQL的时候才能使用到,否则你是拿不到这个语句的,这么说应该还好理解吧,这个功能就等于mybatis进行了数据隔离,但是我们一般不用这个功能,所以也就比较少使用了

  <select id="selectUser" resultType="com.zxc.study.test.bean.User" flushCache="true">
    select * from user where id = #{param1}
  </select>

对于这种没有指定databaseId的语句就是每个数据源都是可以获取到的

这篇就是随便讲讲databaseIdProvider的使用,估计业务中也不这么去用,不过mybatis确实提供了这个支持

源码

源码的话就稍微看下,不过这里只是一部分,要看全部的还要去其他地方看,这里不细说了

org.apache.ibatis.builder.xml.XMLConfigBuilder#databaseIdProviderElement


  private void databaseIdProviderElement(XNode context) throws Exception {
    DatabaseIdProvider databaseIdProvider = null;
    //配置了数据库厂商
    if (context != null) {
      String type = context.getStringAttribute("type");
      // awful patch to keep backward compatibility
      if ("VENDOR".equals(type)) {
          type = "DB_VENDOR";
      }
      //获取设置的熟悉以及设置给DatabaseIdProvider
      Properties properties = context.getChildrenAsProperties();
      databaseIdProvider = (DatabaseIdProvider) resolveClass(type).newInstance();
      databaseIdProvider.setProperties(properties);
    }
    Environment environment = configuration.getEnvironment();
    //环境不为空和数据库厂商不为空时会进入这个判断
    if (environment != null && databaseIdProvider != null) {
      //通过数据源获取对应的数据标识,就是上面说的意思
      String databaseId = databaseIdProvider.getDatabaseId(environment.getDataSource());
      //标记当前应用使用的数据库厂商标识
      configuration.setDatabaseId(databaseId);
    }
  }



再稍微看下是如何获取数据库厂商id的,估计你点开就明白了


org.apache.ibatis.mapping.VendorDatabaseIdProvider#getDatabaseId


@Override
  public String getDatabaseId(DataSource dataSource) {
    if (dataSource == null) {
      throw new NullPointerException("dataSource cannot be null");
    }
    try {
      return getDatabaseName(dataSource);
    } catch (Exception e) {
      log.error("Could not get a databaseId from dataSource", e);
    }
    return null;
  }

  @Override
  public void setProperties(Properties p) {
    this.properties = p;
  }

  private String getDatabaseName(DataSource dataSource) throws SQLException {
    String productName = getDatabaseProductName(dataSource);
    if (this.properties != null) {
      for (Map.Entry<Object, Object> property : properties.entrySet()) {
        if (productName.contains((String) property.getKey())) {
          return (String) property.getValue();
        }
      }
      // no match, return null
      return null;
    }
    return productName;
  }

  private String getDatabaseProductName(DataSource dataSource) throws SQLException {
    Connection con = null;
    try {
      con = dataSource.getConnection();
      DatabaseMetaData metaData = con.getMetaData();
      return metaData.getDatabaseProductName();
    } finally {
      if (con != null) {
        try {
          con.close();
        } catch (SQLException e) {
          // ignored
        }
      }
    }
  }

至于他是怎么把数据标识id存起来的对应关系,这里就不说了,大概就是解析的时候把它做了一个标识吧,有兴趣的可以参考一下

第六篇:Configuration之mappers_zxc_user的博客-CSDN博客

怎么设置的

这里还是稍微说下Mybatis是怎么设置这个关系的,但是也只是简单说下,其实也并不难

还是复习下原来的方法

org.apache.ibatis.builder.xml.XMLMapperBuilder#configurationElement

  private void configurationElement(XNode context) {
    try {
      String namespace = context.getStringAttribute("namespace");
      if (namespace == null || namespace.equals("")) {
        throw new BuilderException("Mapper's namespace cannot be empty");
      }
      builderAssistant.setCurrentNamespace(namespace);
      //解析二级缓存相关的
      cacheRefElement(context.evalNode("cache-ref"));
      //解析缓存
      cacheElement(context.evalNode("cache"));
      //解析parameterMap,现在已经基本不用了
      parameterMapElement(context.evalNodes("/mapper/parameterMap"));
      //解析resultMap
      resultMapElements(context.evalNodes("/mapper/resultMap"));
      //解析sql片段,应该是要放到某个map中,其他地方引用的时候再从map拿出来
      sqlElement(context.evalNodes("/mapper/sql"));
      //核心的sql语句解析逻辑,增删改查
      buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
    }
  }


最核心的方法为



org.apache.ibatis.builder.xml.XMLMapperBuilder#buildStatementFromContext(java.util.List<org.apache.ibatis.parsing.XNode>)


  private void buildStatementFromContext(List<XNode> list) {
    //如果数据库标识不为空,则设置一下,这个标识是在前面设置的
    if (configuration.getDatabaseId() != null) {
      buildStatementFromContext(list, configuration.getDatabaseId());
    }
    buildStatementFromContext(list, null);
  }

  private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
    for (XNode context : list) {
      final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
      try {
        statementParser.parseStatementNode();
      } catch (IncompleteElementException e) {
        configuration.addIncompleteStatement(statementParser);
      }
    }
  }



那么最终就是看 statementParser.parseStatementNode()方法是怎么处理的




org.apache.ibatis.builder.xml.XMLStatementBuilder#parseStatementNode




  public void parseStatementNode() {
    String id = context.getStringAttribute("id");
    String databaseId = context.getStringAttribute("databaseId");

    //判断是否与厂商一致
    if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
      return;
    }
}

后面的去掉了,因为之前已经说过了,最重要是看看下面这个方法




  private boolean databaseIdMatchesCurrent(String id, String databaseId, String requiredDatabaseId) {
    //数据库厂商设置了
    if (requiredDatabaseId != null) {
      //核心在这里,但是mapper里面配置的跟当前的数据库厂商不一致,直接返回,也就是不把当前
      //的mapper加到配置中,那么你在获取的时候自然就找不到了
      if (!requiredDatabaseId.equals(databaseId)) {
        return false;
      }
    } else {
      //如果本地环境没有设置数据库厂商,但是mapper里面设置了,那也是不添加到配置文件中的,因为根本就不知道属于哪个数据库厂商
      if (databaseId != null) {
        return false;
      }
      // skip this statement if there is a previous one with a not null databaseId
      id = builderAssistant.applyCurrentNamespace(id, false);
      if (this.configuration.hasStatement(id, false)) {
        MappedStatement previous = this.configuration.getMappedStatement(id, false); // issue #2
        if (previous.getDatabaseId() != null) {
          return false;
        }
      }
    }
    return true;
  }



这样应该就比较清晰了

好的,您想了解有关自定义几何体的内容,我很乐意为您解答。首先,让我们了解一下什么是几何体。 在Cesium中,几何体是由一些点、线和三角形组成的图形。几何体可以在地球上显示各种形状的物体,如建筑、飞机、汽车等。Cesium提供了一些内置的几何体,如BoxGeometry、CylinderGeometry、SphereGeometry等,但是有时候我们需要展示一些特殊形状的物体,这时候就需要自定义几何体了。 下面是一个简单的自定义几何体的例子: ```javascript var geometry = new Cesium.Geometry({ attributes: { position: new Cesium.GeometryAttribute({ componentDatatype: Cesium.ComponentDatatype.DOUBLE, componentsPerAttribute: 3, values: new Float64Array([ 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0 ]) }) }, indices: new Uint16Array([ 0, 1, 2, 0, 2, 3 ]), primitiveType: Cesium.PrimitiveType.TRIANGLES }); ``` 这个例子中,我们创建了一个由四个点组成的矩形,并用这些点的索引定义了两个三角形。这个几何体可以用来在地球上显示一个矩形。 接下来,让我们逐步了解这个例子中的代码。首先是Cesium.GeometryAttribute。 Cesium.GeometryAttribute是几何体属性的容器。在这个例子中,我们定义了一个名为position的属性,它有三个分量:x、y和z。这个属性使用的数据类型是Cesium.ComponentDatatype.DOUBLE,表示每个分量有一个双精度浮点数。componentsPerAttribute表示每个属性有几个分量。在这个例子中,每个属性都有三个分量。最后,我们用一个Float64Array数组来定义这个属性的值。 接下来是indices,它定义了几何体使用哪些点来组成三角形。在这个例子中,我们定义了两个三角形,每个三角形使用三个顶点。在indices数组中,我们用顶点在attributes数组中的索引来定义每个三角形。 最后,我们定义了几何体的primitiveType,它表示几何体的类型。在这个例子中,我们使用的是三角形类型,所以primitiveType为Cesium.PrimitiveType.TRIANGLES。 希望这个例子可以帮助您更好地理解自定义几何体的实现。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值