简体   繁体   中英

How to correctly override BasicDataSource for Spring and Hibernate

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 setters 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 .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM