java - 如何正确覆盖 Spring 和 Hibernate 的 BasicDataSource

标签 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>

这可能看起来令人困惑。

有没有更好的解决方案?

最佳答案

无需扩展 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 .

关于java - 如何正确覆盖 Spring 和 Hibernate 的 BasicDataSource,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4493743/

相关文章:

java - JAXB:从不同继承级别获取标签

java - 如何在 Thymeleaf 中静态初始化 Map?

java - hibernate OneToMany java.lang.StackOverflowError

java - 处理 spring 数据中的并发事务

spring - 无法在Spring Data项目中加载资源

java - 如何从java中的组合框中获取整数值?

hibernate - 在 hibernate 中调用 AES_ENCRYPT 函数

java - 在 hibernate 中延迟加载一个 clob

hibernate - Spring/Hibernate 延迟加载需要帮助

java - 结合格式化和字符串附加的惯用方法?