[问题的由来]
一般来说,通用的商业产品都可以访问多数据库。TopLink也不例外,其中的JPA号称支持上十种数据库平台,而且还可以动态增加。那么具体的技术细节是怎么样呢?怎么实现的呢?
我们来看一看TopLink的源代码是如何实现的。
[源代码分析]
第一步:在登陆访问数据库时,首先执行下面的函数
public void loginAndDetectDatasource() throws DatabaseException {
preConnectDatasource();
Connection conn = null;
try{
conn = (Connection)getReadLogin().connectToDatasource(null);
getLogin().setPlatformClassName(DBPlatformHelper.getDBPlatform(conn.getMetaData().getDatabaseProductName(), getSessionLog()));
}catch (SQLException ex){
DatabaseException dbEx = DatabaseException.errorRetrieveDbMetadataThroughJDBCConnection();
// Typically exception would occur if user did not provide correct connection
// parameters. The root cause of exception should be propogated up
dbEx.initCause(ex);
throw dbEx;
}finally{
if (conn != null){
try{
conn.close();
}catch (SQLException ex){
DatabaseException dbEx = DatabaseException.errorRetrieveDbMetadataThroughJDBCConnection();
// Typically exception would occur if user did not provide correct connection
// parameters. The root cause of exception should be propogated up
dbEx.initCause(ex);
throw dbEx;
}
}
}
connect();
postConnectDatasource();
}
第二步:在上面的函数中,有一个特别重要的函数,大家注意看,叫做DBPlatformHelper.getDBPlatform()
下面我们来看看这个函数到底做了什么?
public static String getDBPlatform(String vendorName, SessionLog logger) { initializeNameToVendorPlatform(logger); String detectedDbPlatform = null; if(vendorName != null) { detectedDbPlatform = matchVendorNameInProperties(vendorName, _nameToVendorPlatform, logger); } if (logger.shouldLog(SessionLog.FINE) ) { logger.log(SessionLog.FINE, "dbPlaformHelper_detectedVendorPlatform", detectedDbPlatform ); // NOI18N } if (detectedDbPlatform == null) { if(logger.shouldLog(SessionLog.INFO)) { logger.log(SessionLog.INFO, "dbPlaformHelper_defaultingPlatform", vendorName, DEFAULTPLATFORM); // NOI18N } detectedDbPlatform = DEFAULTPLATFORM; } return detectedDbPlatform; } |
大家注意看,上面这个函数的第一行是:
initializeNameToVendorPlatform(logger);
这个函数是干什么的?是不是就是访问多数据库强大功能的秘密呢?我们再往下面看。
第三步:秘密原来在这里
private static Properties initializeNameToVendorPlatform(SessionLog logger) { synchronized(DBPlatformHelper.class) { if(_nameToVendorPlatform == null) { _nameToVendorPlatform = new Properties(); try { loadFromResource(_nameToVendorPlatform, VENDOR_NAME_TO_PLATFORM_RESOURCE_NAME, DBPlatformHelper.class.getClassLoader() ); } catch (IOException e) { logger.log(SessionLog.WARNING, "dbPlaformHelper_noMappingFound", VENDOR_NAME_TO_PLATFORM_RESOURCE_NAME); } } } return _nameToVendorPlatform; } |
大家注意看,上面这个函数中,有一个很关键的一行代码
loadFromResource(_nameToVendorPlatform,VENDOR_NAME_TO_PLATFORM_RESOURCE_NAME, DBPlatformHelper.class.getClassLoader() );
这行代码干的事情就是动态的对应到不同的数据库平台。那么到底是怎么做到的呢?
这行代码中有一个变量,名字叫做[VENDOR_NAME_TO_PLATFORM_RESOURCE_NAME]
它的真实面貌如下:
private final static String VENDOR_NAME_TO_PLATFORM_RESOURCE_NAME =PROPERTY_PATH + "VendorNameToPlatformMapping.properties"; //NOI18N |
关键问题出现了,原来它是最后找到了一个配置文件,通过它解决不同数据库的访问难题,那么最后我们来看看这个配置文件里面写着什么。
第四步:配置文件
#Property file containing mapping between vendor name and vendor platform #The key of the property is in form of java regular expression. #At runtime, vendor name obtained from DatabaseMetaData#getDatabaseProductName() #is matched against the regular expresion to derive the DatabasePlaform we are #running against. SQL\ Anywhere=oracle.toplink.essentials.platform.database.SQLAnyWherePlatform (?i)oracle.*=oracle.toplink.essentials.platform.database.oracle.OraclePlatform (?i)(sybase.*)|(adaptive\ server\ enterprise.*)|(SQL\ Server)=oracle.toplink.essentials.platform.database.SybasePlatform (?i)microsoft.*=oracle.toplink.essentials.platform.database.SQLServerPlatform #Use JavaDBPlatform as the platform for Derby (?i).*derby=oracle.toplink.essentials.platform.database.JavaDBPlatform (?i).*db2.*=oracle.toplink.essentials.platform.database.DB2Platform (?i)pointbase.*=oracle.toplink.essentials.platform.database.PointBasePlatform (?i)mysql.*=oracle.toplink.essentials.platform.database.MySQL4Platform (?i)informix.*=oracle.toplink.essentials.platform.database.InformixPlatform (?i)postgresql.*=oracle.toplink.essentials.platform.database.PostgreSQLPlatform (?i).*symfoware.*=oracle.toplink.essentials.platform.database.SymfowarePlatform
|