我正在開發一個適合客戶端 - 服務器模型的Eclipse插件。它是一個商業項目,所以我們不能爲我們用插件支持的各種數據庫重新分配JDBC驅動程序。如何動態替換Eclipse插件的類加載器?
因此,我開發了一個首選項頁面,允許用戶找到瓶子並具有一個簡單的發現機制,它遍歷jar文件中的類,加載每個類以驗證它是否實現了java.sql.Driver接口。這一切都很好。
但問題在於我正在使用Hibernate。而Hibernate使用Class.forName()來實例化JDBC驅動程序。
如果我嘗試使用以下我得到ClassNotFoundException
。
public Object execute(final IRepositoryCallback callback)
{
final DatabaseDriverClassLoader loader = new DatabaseDriverClassLoader(
Activator.getDefault().getDatabaseDriverRegistry());
final ClassLoader oldLoader = Thread.currentThread()
.getContextClassLoader();
try
{
Thread.currentThread().setContextClassLoader(loader);
try
{
final SessionFactory sessionFactory = this.configuration
.buildSessionFactory();
if (sessionFactory != null)
{
final Session session = sessionFactory
.openSession();
if (session != null)
{
// CHECKSTYLE:OFF
try
// CHECKSTYLE:ON
{
return callback.doExecute(session);
}
finally
{
session.close();
}
}
}
connection.close();
}
finally
{
}
}
// CHECKSTYLE:OFF
catch (Exception e)
// CHECKSTYLE:ON
{
RepositoryTemplate.LOG.error(e.getMessage(), e);
}
finally
{
Thread.currentThread().setContextClassLoader(oldLoader);
}
return null;
}
如果我嘗試自己創建驅動程序,如下所示,我得到一個SecurityException。
public Object execute(final IRepositoryCallback callback)
{
final DatabaseDriverClassLoader loader = new DatabaseDriverClassLoader(
Activator.getDefault().getDatabaseDriverRegistry());
final ClassLoader oldLoader = Thread.currentThread()
.getContextClassLoader();
try
{
Thread.currentThread().setContextClassLoader(loader);
final Class driverClass = loader.loadClass(this.connectionDriverClassName);
final Driver driver = (Driver)driverClass.newInstance();
DriverManager.registerDriver(driver);
try
{
final Connection connection = DriverManager.getConnection(
this.connectionUrl, this.connectionUsername,
this.connectionPassword);
final SessionFactory sessionFactory = this.configuration
.buildSessionFactory();
if (sessionFactory != null)
{
final Session session = sessionFactory
.openSession(connection);
if (session != null)
{
// CHECKSTYLE:OFF
try
// CHECKSTYLE:ON
{
return callback.doExecute(session);
}
finally
{
session.close();
}
}
}
connection.close();
}
finally
{
DriverManager.deregisterDriver(driver);
}
}
// CHECKSTYLE:OFF
catch (Exception e)
// CHECKSTYLE:ON
{
RepositoryTemplate.LOG.error(e.getMessage(), e);
}
finally
{
Thread.currentThread().setContextClassLoader(oldLoader);
}
return null;
}
編輯:我不知道這是最好的選擇,但我實現了我自己的ConnectionProvider
這讓我使用Class.forName()
實例化的驅動程序,然後我打開使用Driver.connect()
代替DriverManager.getConnection()
連接的方法。它非常基本,但我不需要連接池在我的具體用例。
的configure()
方法如下:
public void configure(final Properties props)
{
this.url = props.getProperty(Environment.URL);
this.connectionProperties = ConnectionProviderFactory
.getConnectionProperties(props);
final DatabaseDriverClassLoader classLoader = new DatabaseDriverClassLoader(
Activator.getDefault().getDatabaseDriverRegistry());
final String driverClassName = props.getProperty(Environment.DRIVER);
try
{
final Class driverClass = Class.forName(driverClassName, true,
classLoader);
this.driver = (Driver)driverClass.newInstance();
}
catch (ClassNotFoundException e)
{
throw new HibernateException(e);
}
catch (IllegalAccessException e)
{
throw new HibernateException(e);
}
catch (InstantiationException e)
{
throw new HibernateException(e);
}
}
而且getConnection()
方法如下:
public Connection getConnection()
throws SQLException
{
return this.driver.connect(this.url, this.connectionProperties);
}
我會爲其他項目記住這個建議。不幸的是,這些方法相當於我用我的工具重新包裝和分發供應商的驅動程序。我必須避免由於許可問題。 – 2008-12-18 22:22:07