專案原使用 Apache DBCP 資料源,後因引入第三方報表工具強制要求 JNDI 連線,需在 JBoss 4.2.2 GA + SQL Server 2000 環境下完成遷移。本文記錄完整設定步驟、Spring FactoryBean 實作與經典「首次連線失敗、重整即正常」問題的根因分析與解決方案。
在 server/default/deploy 目錄建立 xxx-ds.xml,JBoss 啟動時自動掃描部署:
<?xml version="1.0" encoding="UTF-8"?>
<datasources>
<local-tx-datasource>
<jndi-name>jdbc/DataSource</jndi-name>
<connection-url>jdbc:jtds:sqlserver://localhost:1433/fnx</connection-url>
<driver-class>net.sourceforge.jtds.jdbc.Driver</driver-class>
<user-name>sa</user-name>
<password>1234</password>
<metadata>
<type-mapping>MS SQLSERVER2000</type-mapping>
</metadata>
</local-tx-datasource>
</datasources>
public class DataSourceFactoryBean implements FactoryBean, InitializingBean {
private DataSource dataSource;
private boolean jndi;
private String jndiName;
private String driverClassName;
private String url;
private String username;
private String password;
@Override
public void afterPropertiesSet() throws Exception {
if (isJndi()) {
if (!StringUtils.hasText(this.jndiName)) {
throw new IllegalArgumentException("jndiName is required");
}
} else {
if (!StringUtils.hasText(this.driverClassName)) throw new IllegalArgumentException("driverClassName is required");
if (!StringUtils.hasText(this.url)) throw new IllegalArgumentException("url is required");
if (!StringUtils.hasText(this.username)) throw new IllegalArgumentException("username is required");
if (!StringUtils.hasText(this.password)) throw new IllegalArgumentException("password is required");
}
this.createDataSource();
}
@Override
public Object getObject() throws Exception {
return this.createDataSource();
}
@Override
public Class getObjectType() {
return javax.sql.DataSource.class;
}
@Override
public boolean isSingleton() {
return true;
}
protected DataSource createDataSource() {
if (this.dataSource == null) {
try {
if (this.isJndi()) {
this.dataSource = new JndiDataSourceSupport().lookupDataSource(this.jndiName);
} else {
this.dataSource = new DriverManagerDataSource(driverClassName, url, username, password);
}
} catch (Throwable err) {
throw new RuntimeException(err);
}
}
return this.dataSource;
}
private class JndiDataSourceSupport extends JndiLocatorSupport {
public DataSource lookupDataSource(String jndiName) throws NamingException {
return (DataSource) super.lookup(jndiName, getObjectType());
}
}
// getter/setter 省略
}
<bean id="dataSource" class="com.example.dao.DataSourceFactoryBean">
<property name="jndi" value="${jdbc.IS_JNDI}" />
<property name="jndiName" value="${jdbc.jndiName}" />
<property name="driverClassName" value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list><value>classpath:config/jdbc.properties</value></list>
</property>
</bean>
# false=開發環境用傳統 datasource, true=正式環境用 JNDI
jdbc.IS_JNDI=true
jdbc.jndiName=java:jdbc/DataSource
# SQL Server 設定 (開發環境備用)
jdbc.driverClassName=net.sourceforge.jtds.jdbc.Driver
jdbc.url=jdbc:jtds:sqlserver://localhost:1433/fnx
jdbc.username=sa
jdbc.password=1234
Throwable while attempting to get a new connection: null
org.jboss.resource.JBossResourceException: Could not create connection;
- nested throwable: (org.jboss.resource.JBossResourceException: Apparently wrong driver class specified for URL:
class: net.sourceforge.jtds.jdbc.Driver, url: jdbc:jtds:sqlserver://localhost:1433/fnx)
jTDS JDBC 驅動 JAR (jtds.jar) 僅放在專案 WEB-INF/lib,未放入 JBoss 容器共用類別庫 JBOSS_HOME/server/default/lib。
-ds.xml 建立資料源,需載入驅動類別WEB-INF/lib 類別載入容器 Classloader將 jtds.jar 複製至 JBOSS_HOME/server/default/lib 目錄,重啟 JBoss 即可徹底解決。