Add support for HikariDataSource
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
parent
f46d281b22
commit
50190a4de7
@ -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 @@
|
||||
/*
|
||||
* 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