开发者

How to correctly override BasicDataSource for Spring and Hibernate

开发者 https://www.devze.com 2023-02-01 00:47 出处:网络
Currently I have following basic data source in Spring: <bean id=\"dbcpDataSource\" class=\"org.apache.commons.dbcp.BasicDataSource\" destroy-method=\"close\">

Currently I have following basic data source in 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>

Now I need to provide custom data source based on server environment (not config), for which I need to calculate driverClassName and url fields based on some condition.

I tried overriding createDataSource() method:

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();
    }
}

Which works, but I noticed that createDataSource() is getting called each time a query is executed(?), so I would rather move my condition testing elsewhere.

I tried overriding setDriverClassName() and setUrl() which also works and is getting called only once from what I can tell, but then I need to provide some values in Spring configuration in order to trigger those sette开发者_开发技巧rs as they are not called otherwise:

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

which might look confusing.

Are there any better solutions?


There is no need to extend BasicDataSource. Inheritance is the strongest form of coupling and should be avoided unless you have a real reason to use it.

You have two options:

  • Create a wrapper (use composition instead of inheritance)

    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 ...
    }
    
  • Create a factory (since you need to customize only the creation phase of the object, you don't need to control the whole lifetime of it).

     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>
    

EDIT: Note that you still can configure the object obtained from the factory as a regular Spring bean.

Also if your condition is simple enough, you can avoid writing your own code at all by using declarative approaches provided by Spring, such as Spring Expression language.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号