Browse Source
We still prefer Tomcat if it is available (that can change if the community asks loudly enough). Hikari is supported via the same spring.datasource.* properties as Tomcat (and DBCP), with some modifications: * The validation and timeout settings are not as fine-grained in Hikari, so many of them will simply be ignored. The most common options (url, username, password, driverClassName) all work as expected. * The Hikari team recommends using a vendor-specific DataSource via spring.datasource.dataSourceClassName and supplying it with Properties (spring.datasource.hikari.*). Hikari prefers the JDBC4 isValid() API (encapsulates vendor- specific queries) which is probably a good thing, but we haven't provided any explicit support or testing for that yet. Fixes gh-418pull/753/merge
6 changed files with 320 additions and 5 deletions
@ -0,0 +1,122 @@
@@ -0,0 +1,122 @@
|
||||
/* |
||||
* Copyright 2012-2014 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.autoconfigure.jdbc; |
||||
|
||||
import java.util.Properties; |
||||
|
||||
import javax.annotation.PreDestroy; |
||||
import javax.sql.DataSource; |
||||
|
||||
import org.springframework.context.annotation.Bean; |
||||
import org.springframework.context.annotation.Configuration; |
||||
import org.springframework.util.StringUtils; |
||||
|
||||
import com.zaxxer.hikari.HikariDataSource; |
||||
|
||||
/** |
||||
* Configuration for a HikariCP database pool. The HikariCP pool is a popular data source |
||||
* implementation that provides high performance as well as some useful opinionated |
||||
* defaults. For compatibility with other DataSource implementations accepts configuration |
||||
* via properties in "spring.datasource.*", e.g. "url", "driverClassName", "username", |
||||
* "password" (and some others but the full list supported by the Tomcat pool is not |
||||
* applicable). Note that the Hikari team recommends using a "dataSourceClassName" and a |
||||
* Properties instance (specified here as "spring.datasource.hikari.*"). This makes the |
||||
* binding potentially vendor specific, but gives you full control of all the native |
||||
* features in the vendor's DataSource. |
||||
* |
||||
* @author Dave Syer |
||||
* @see DataSourceAutoConfiguration |
||||
*/ |
||||
@Configuration |
||||
public class HikariDataSourceConfiguration extends AbstractDataSourceConfiguration { |
||||
|
||||
private String dataSourceClassName; |
||||
private String username; |
||||
|
||||
private HikariDataSource pool; |
||||
private Properties hikari = new Properties(); |
||||
|
||||
@Bean(destroyMethod = "shutdown") |
||||
public DataSource dataSource() { |
||||
this.pool = new HikariDataSource(); |
||||
if (this.dataSourceClassName == null) { |
||||
this.pool.setDriverClassName(getDriverClassName()); |
||||
} |
||||
else { |
||||
this.pool.setDataSourceClassName(this.dataSourceClassName); |
||||
this.pool.setDataSourceProperties(this.hikari); |
||||
} |
||||
this.pool.setJdbcUrl(getUrl()); |
||||
if (getUsername() != null) { |
||||
this.pool.setUsername(getUsername()); |
||||
} |
||||
if (getPassword() != null) { |
||||
this.pool.setPassword(getPassword()); |
||||
} |
||||
this.pool.setMaximumPoolSize(getMaxActive()); |
||||
this.pool.setMinimumIdle(getMinIdle()); |
||||
if (isTestOnBorrow()) { |
||||
this.pool.setConnectionInitSql(getValidationQuery()); |
||||
} |
||||
else { |
||||
this.pool.setConnectionTestQuery(getValidationQuery()); |
||||
} |
||||
if (getMaxWaitMillis() != null) { |
||||
this.pool.setMaxLifetime(getMaxWaitMillis()); |
||||
} |
||||
return this.pool; |
||||
} |
||||
|
||||
@PreDestroy |
||||
public void close() { |
||||
if (this.pool != null) { |
||||
this.pool.close(); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* @param dataSourceClassName the dataSourceClassName to set |
||||
*/ |
||||
public void setDataSourceClassName(String dataSourceClassName) { |
||||
this.dataSourceClassName = dataSourceClassName; |
||||
} |
||||
|
||||
@Override |
||||
public void setUsername(String username) { |
||||
this.username = username; |
||||
} |
||||
|
||||
/** |
||||
* @return the hikari data source properties |
||||
*/ |
||||
public Properties getHikari() { |
||||
return this.hikari; |
||||
} |
||||
|
||||
@Override |
||||
protected String getUsername() { |
||||
if (StringUtils.hasText(this.username)) { |
||||
return this.username; |
||||
} |
||||
if (this.dataSourceClassName == null |
||||
&& EmbeddedDatabaseConnection.isEmbedded(getDriverClassName())) { |
||||
return "sa"; |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,116 @@
@@ -0,0 +1,116 @@
|
||||
/* |
||||
* Copyright 2012-2014 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.boot.autoconfigure.jdbc; |
||||
|
||||
import java.lang.reflect.Field; |
||||
|
||||
import javax.sql.DataSource; |
||||
|
||||
import org.junit.After; |
||||
import org.junit.Test; |
||||
import org.springframework.beans.factory.BeanCreationException; |
||||
import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration; |
||||
import org.springframework.boot.test.EnvironmentTestUtils; |
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext; |
||||
import org.springframework.util.ReflectionUtils; |
||||
|
||||
import com.zaxxer.hikari.HikariDataSource; |
||||
|
||||
import static org.junit.Assert.assertEquals; |
||||
import static org.junit.Assert.assertNotNull; |
||||
|
||||
/** |
||||
* Tests for {@link HikariDataSourceConfiguration}. |
||||
* |
||||
* @author Dave Syer |
||||
*/ |
||||
public class HikariDataSourceConfigurationTests { |
||||
|
||||
private static final String PREFIX = "spring.datasource."; |
||||
|
||||
private final AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); |
||||
|
||||
@After |
||||
public void restore() { |
||||
EmbeddedDatabaseConnection.override = null; |
||||
} |
||||
|
||||
@Test |
||||
public void testDataSourceExists() throws Exception { |
||||
this.context.register(HikariDataSourceConfiguration.class); |
||||
this.context.refresh(); |
||||
assertNotNull(this.context.getBean(DataSource.class)); |
||||
assertNotNull(this.context.getBean(HikariDataSource.class)); |
||||
} |
||||
|
||||
@Test |
||||
public void testDataSourcePropertiesOverridden() throws Exception { |
||||
this.context.register(HikariDataSourceConfiguration.class); |
||||
EnvironmentTestUtils.addEnvironment(this.context, PREFIX |
||||
+ "url:jdbc:foo//bar/spam"); |
||||
EnvironmentTestUtils.addEnvironment(this.context, PREFIX + "maxWait:1234"); |
||||
this.context.refresh(); |
||||
HikariDataSource ds = this.context.getBean(HikariDataSource.class); |
||||
assertEquals("jdbc:foo//bar/spam", ds.getJdbcUrl()); |
||||
assertEquals(1234, ds.getMaxLifetime()); |
||||
// TODO: test JDBC4 isValid()
|
||||
} |
||||
|
||||
@Test |
||||
public void testDataSourceGenericPropertiesOverridden() throws Exception { |
||||
this.context.register(HikariDataSourceConfiguration.class); |
||||
EnvironmentTestUtils.addEnvironment(this.context, PREFIX |
||||
+ "hikari.databaseName:foo", PREFIX |
||||
+ "dataSourceClassName:org.h2.JDBCDataSource"); |
||||
this.context.refresh(); |
||||
HikariDataSource ds = this.context.getBean(HikariDataSource.class); |
||||
assertEquals("foo", ds.getDataSourceProperties().getProperty("databaseName")); |
||||
} |
||||
|
||||
@Test |
||||
public void testDataSourceDefaultsPreserved() throws Exception { |
||||
this.context.register(HikariDataSourceConfiguration.class); |
||||
this.context.refresh(); |
||||
HikariDataSource ds = this.context.getBean(HikariDataSource.class); |
||||
assertEquals(1800000, ds.getMaxLifetime()); |
||||
} |
||||
|
||||
@Test(expected = BeanCreationException.class) |
||||
public void testBadUrl() throws Exception { |
||||
EmbeddedDatabaseConnection.override = EmbeddedDatabaseConnection.NONE; |
||||
this.context.register(HikariDataSourceConfiguration.class, |
||||
PropertyPlaceholderAutoConfiguration.class); |
||||
this.context.refresh(); |
||||
assertNotNull(this.context.getBean(DataSource.class)); |
||||
} |
||||
|
||||
@Test(expected = BeanCreationException.class) |
||||
public void testBadDriverClass() throws Exception { |
||||
EmbeddedDatabaseConnection.override = EmbeddedDatabaseConnection.NONE; |
||||
this.context.register(HikariDataSourceConfiguration.class, |
||||
PropertyPlaceholderAutoConfiguration.class); |
||||
this.context.refresh(); |
||||
assertNotNull(this.context.getBean(DataSource.class)); |
||||
} |
||||
|
||||
@SuppressWarnings("unchecked") |
||||
public static <T> T getField(Class<?> target, String name) { |
||||
Field field = ReflectionUtils.findField(target, name, null); |
||||
ReflectionUtils.makeAccessible(field); |
||||
return (T) ReflectionUtils.getField(field, target); |
||||
} |
||||
} |
||||
Loading…
Reference in new issue