/**
* Copyright 2009-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.mapping;
import java.sql.SQLException;
import java.util.Properties;
import javax.sql.DataSource;
/**
* Should return an id to identify the type of this database.
* That id can be used later on to build different queries for each database type
* This mechanism enables supporting multiple vendors or versions
* 应该返回一个id来识别这个数据库的类型。
* 以后可以使用该id为每个数据库类型建立不同的查询,
* 这个机制使得能够支持多个供应商或者版本
* @author Eduardo Macarron
*/
public interface DatabaseIdProvider {
void setProperties(Properties p);
String getDatabaseId(DataSource dataSource) throws SQLException;
}
VendorDatabaseIdProvider
/**
* Copyright 2009-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.mapping;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.util.Map;
import java.util.Properties;
import javax.sql.DataSource;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;
/**
* Vendor DatabaseId provider.
* 供应商的数据库Id提供者
* <br/>
* It returns database product name as a databaseId.
* If the user provides a properties it uses it to translate database product name
* key="Microsoft SQL Server", value="ms" will return "ms".
* It can return null, if no database product name or
* a properties was specified and no translation was found.
* <p>
* 它会返回数据库产品名称作为数据库ID.
* 如果使用用户提供一个属性列表,它会使用这个属性列表去转化数据库产品名
* 如:key='Microsoft SQL Server',value='ms',就会返回'ms'.
* 如果没有数据库产品名或者指定了属性列表当但是没有找到译文,就会返回null.
* </p>
* @author Eduardo Macarron
*/
public class VendorDatabaseIdProvider implements DatabaseIdProvider {
private Properties properties;
/**
* 获取数据库ID
* <p>
* 如果 {@link @dataSoure} 为null会抛出异常。实际直接调用 {@link VendorDatabaseIdProvider#getDatabaseName(DataSource)}
* </p>
*/
@Override
public String getDatabaseId(DataSource dataSource) {
if (dataSource == null) {
throw new NullPointerException("dataSource cannot be null");
}
try {
return getDatabaseName(dataSource);
} catch (Exception e) {
LogHolder.log.error("Could not get a databaseId from dataSource", e);
}
return null;
}
@Override
public void setProperties(Properties p) {
this.properties = p;
}
/**
* 获取数据库名
* <p>
* 调用 {@link VendorDatabaseIdProvider#getDatabaseName(DataSource)} 获取到数据库产品名赋值给{@link @productName},
* 然后遍历 {@link VendorDatabaseIdProvider#properties} 找出第一个能与 {@link @productName} 匹配的 key ,
* 最后返回其 value ( p:主要是第一个匹配的key,而不是最匹配的key ).如果{@link VendorDatabaseIdProvider#properties}
* 没有找到,就直接返回{@link @productName}
* </p>
*/
private String getDatabaseName(DataSource dataSource) throws SQLException {
String productName = getDatabaseProductName(dataSource);
if (this.properties != null) {
for (Map.Entry<Object, Object> property : properties.entrySet()) {
/**
* 用contains能够校验的容错率
* 这种方式对应出现 productName=’Oracle11’,property的key有'oracle'和'oracle11' 的情况时,
* 会返回key='oracle'的值,而不是key='oracle11'的值,(取第一个能匹配的key)
*/
if (productName.contains((String) property.getKey())) {
return (String) property.getValue();
}
}
// no match, return null
return null;
}
return productName;
}
/**
* 获取数据库产品名
* <p>
* 通过获取数据库来连接得到数据库的描述信息类,从中获取数据库产品名称
* </p>
*/
private String getDatabaseProductName(DataSource dataSource) throws SQLException {
Connection con = null;
try {
con = dataSource.getConnection();
//DatabaseMetaData 其实就是数据库的描述信息。其实'Meta'的翻译过来是'元',当其实在英文中是表示描述信息的意思。
DatabaseMetaData metaData = con.getMetaData();
return metaData.getDatabaseProductName();
} finally {
if (con != null) {
try {
con.close();
} catch (SQLException e) {
// ignored
}
}
}
}
/**
* 只是封装了一个 {@link Log} 并赋值给 {@link LogHolder#log}
*/
private static class LogHolder {
private static final Log log = LogFactory.getLog(VendorDatabaseIdProvider.class);
}
}