如何正确覆盖Spring和Hibernate的BasicDataSource

时间:2010-12-20 20:41:37

标签: java hibernate spring jdbc

目前我在Spring中有以下基本数据源:

<bean id="dbcpDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    <property name="driverClassName" value="com.mysql.jdbc.Driver" />
    <property name="url" value="jdbc:mysql://localhost/test?relaxAutoCommit=true" />
    ...
</bean>

现在我需要提供基于服务器环境(不是配置)的自定义数据源,我需要根据某些条件计算driverClassNameurl字段。

我尝试覆盖createDataSource()方法:

public class MyDataSource extends BasicDataSource {

    @Override
    protected synchronized DataSource createDataSource() throws SQLException {
        if(condition) {
            super.setDriverClassName("com.mysql.jdbc.Driver");
            super.setUrl("jdbc:mysql://localhost/test?relaxAutoCommit=true");
        } else {
            //...
        }
        return super.createDataSource();
    }
}

哪个有效,但我注意到每次执行查询时都会调用createDataSource()(?),所以我宁愿在其他地方移动我的条件测试。

我尝试覆盖setDriverClassName()setUrl()这也是有效的,并且只能从我能告诉的内容中调用一次,但是我需要在Spring配置中提供一些值,以便触发这些setter否则不会被称为:

<bean id="dbcpDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    <property name="driverClassName" value="whatever" />
    <property name="url" value="whatever" />
    ...
</bean>

可能看起来令人困惑。

有没有更好的解决方案?

1 个答案:

答案 0 :(得分:4)

无需延长BasicDataSource。继承是最强的耦合形式,应该避免,除非你有真正的理由使用它。

您有两种选择:

  • 创建一个包装器(使用组合而不是继承)

    public class MyDataSource implements DataSource {
        private BasicDataSource target = new BasicDataSource();
        public MyDataSource() {
            if (condition) {
                target.setDriverClassName("com.mysql.jdbc.Driver");                
                target.setUrl("jdbc:mysql://localhost/test?relaxAutoCommit=true"); 
            } else { ... }
        }
        public Connection getConnection() {
            return target.getConnection();
        }
        ... etc ...
    }
    
  • 创建工厂(因为您只需要自定义对象的创建阶段,您不需要控制它的整个生命周期。)

     public class MyDataSourceFactory {
        public DataSource createDataSource() {
            BasicDataSource target = new BasicDataSource();
            if (condition) {
                target.setDriverClassName("com.mysql.jdbc.Driver");                
                target.setUrl("jdbc:mysql://localhost/test?relaxAutoCommit=true"); 
            } else { ... }
            return target;
        }
    }
    

    <bean id = "factory" class = "MyDataSourceFactory" />
    <bean id = "dbcpDataSource" 
         factory-bean = "factory" factory-method = "createDataSource">
         <property ... />
    </bean>
    

编辑:请注意,您仍然可以将从工厂获得的对象配置为常规Spring bean。

此外,如果您的条件足够简单,您可以使用Spring提供的声明方法(例如Spring Expression language)来避免编写自己的代码。