目录摘要
- DataSource配置
- jdbcTemplate配置方式
- 自定义DataSource配置
配置DataSource
Java类库的 javax.sql.DataSource 接口 定义了一个数据库连接的规范,传统的方法是一个’DataSource’使用url及一些用户名、密码凭证信息建立一个连接,springboot集成了标准的datasource,你只需要配置相应的url等连接信息就可以建立一个DataSource,在application.properties中配置如下信息
spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
配置完这些信息,一个默认的dataSource就配置好了,数据库datasource相关配置 属性来源于类org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; springboot中application.properties中的属性大都来源于*Properties.java文件中,如果JdbcTemplate的配置在JdbcProperties类中,这些配置类都有一个共同的注解如下 @ConfigurationProperties(prefix = "spring.datasource")
,也可以自定义类似的属性配置类,后面自定义DataSource时会有介绍到
/*
* Copyright 2012-2018 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.nio.charset.Charset;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import javax.sql.DataSource;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.boot.jdbc.DataSourceInitializationMode;
import org.springframework.boot.jdbc.DatabaseDriver;
import org.springframework.boot.jdbc.EmbeddedDatabaseConnection;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
/**
* Base class for configuration of a data source.
*
* @author Dave Syer
* @author Maciej Walkowiak
* @author Stephane Nicoll
* @author Benedikt Ritter
* @author Eddú Meléndez
* @since 1.1.0
*/
@ConfigurationProperties(prefix = "spring.datasource")
public class DataSourceProperties implements BeanClassLoaderAware, InitializingBean {
private ClassLoader classLoader;
/**
* Name of the datasource. Default to "testdb" when using an embedded database.
*/
private String name;
/**
* Whether to generate a random datasource name.
*/
private boolean generateUniqueName;
/**
* Fully qualified name of the connection pool implementation to use. By default, it
* is auto-detected from the classpath.
*/
private Class<? extends DataSource> type;
/**
* Fully qualified name of the JDBC driver. Auto-detected based on the URL by default.
*/
private String driverClassName;
/**
* JDBC URL of the database.
*/
private String url;
/**
* Login username of the database.
*/
private String username;
/**
* Login password of the database.
*/
private String password;
/**
* JNDI location of the datasource. Class, url, username & password are ignored when
* set.
*/
private String jndiName;
/**
* Initialize the datasource with available DDL and DML scripts.
*/
private DataSourceInitializationMode initializationMode = DataSourceInitializationMode.EMBEDDED;
/**
* Platform to use in the DDL or DML scripts (such as schema-${platform}.sql or
* data-${platform}.sql).
*/
private String platform = "all";
/**
* Schema (DDL) script resource references.
*/
private List<String> schema;
/**
* Username of the database to execute DDL scripts (if different).
*/
private String schemaUsername;
/**
* Password of the database to execute DDL scripts (if different).
*/
private String schemaPassword;
/**
* Data (DML) script resource references.
*/
private List<String> data;
/**
* Username of the database to execute DML scripts (if different).
*/
private String dataUsername;
/**
* Password of the database to execute DML scripts (if different).
*/
private String dataPassword;
/**
* Whether to stop if an error occurs while initializing the database.
*/
private boolean continueOnError = false;
/**
* Statement separator in SQL initialization scripts.
*/
private String separator = ";";
/**
* SQL scripts encoding.
*/
private Charset sqlScriptEncoding;
private EmbeddedDatabaseConnection embeddedDatabaseConnection = EmbeddedDatabaseConnection.NONE;
private Xa xa = new Xa();
private String uniqueName;
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
}
@Override
public void afterPropertiesSet() throws Exception {
this.embeddedDatabaseConnection = EmbeddedDatabaseConnection
.get(this.classLoader);
}
/**
* Initialize a {@link DataSourceBuilder} with the state of this instance.
* @return a {@link DataSourceBuilder} initialized with the customizations defined on
* this instance
*/
public DataSourceBuilder<?> initializeDataSourceBuilder() {
return DataSourceBuilder.create(getClassLoader()).type(getType())
.driverClassName(determineDriverClassName()).url(determineUrl())
.username(determineUsername()).password(determinePassword());
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public boolean isGenerateUniqueName() {
return this.generateUniqueName;
}
public void setGenerateUniqueName(boolean generateUniqueName) {
this.generateUniqueName = generateUniqueName;
}
public Class<? extends DataSource> getType() {
return this.type;
}
public void setType(Class<? extends DataSource> type) {
this.type = type;
}
/**
* Return the configured driver or {@code null} if none was configured.
* @return the configured driver
* @see #determineDriverClassName()
*/
public String getDriverClassName() {
return this.driverClassName;
}
public void setDriverClassName(String driverClassName) {
this.driverClassName = driverClassName;
}
/**
* Determine the driver to use based on this configuration and the environment.
* @return the driver to use
* @since 1.4.0
*/
public String determineDriverClassName() {
if (StringUtils.hasText(this.driverClassName)) {
Assert.state(driverClassIsLoadable(),
() -> "Cannot load driver class: " + this.driverClassName);
return this.driverClassName;
}
String driverClassName = null;
if (StringUtils.hasText(this.url)) {
driverClassName = DatabaseDriver.fromJdbcUrl(this.url).getDriverClassName();
}
if (!StringUtils.hasText(driverClassName)) {
driverClassName = this.embeddedDatabaseConnection.getDriverClassName();
}
if (!StringUtils.hasText(driverClassName)) {
throw new DataSourceBeanCreationException(
"Failed to determine a suitable driver class", this,
this.embeddedDatabaseConnection);
}
return driverClassName;
}
private boolean driverClassIsLoadable() {
try {
ClassUtils.forName(this.driverClassName, null);
return true;
}
catch (UnsupportedClassVersionError ex) {
// Driver library has been compiled with a later JDK, propagate error
throw ex;
}
catch (Throwable ex) {
return false;
}
}
/**
* Return the configured url or {@code null} if none was configured.
* @return the configured url
* @see #determineUrl()
*/
public String getUrl() {
return this.url;
}
public void setUrl(String url) {
this.url = url;
}
/**
* Determine the url to use based on this configuration and the environment.
* @return the url to use
* @since 1.4.0
*/
public String determineUrl() {
if (StringUtils.hasText(this.url)) {
return this.url;
}
String databaseName = determineDatabaseName();
String url = (databaseName != null
? this.embeddedDatabaseConnection.getUrl(databaseName) : null);
if (!StringUtils.hasText(url)) {
throw new DataSourceBeanCreationException(
"Failed to determine suitable jdbc url", this,
this.embeddedDatabaseConnection);
}
return url;
}
/**
* Determine the name to used based on this configuration.
* @return the database name to use or {@code null}
* @since 2.0.0
*/
public String determineDatabaseName() {
if (this.generateUniqueName) {
if (this.uniqueName == null) {
this.uniqueName = UUID.randomUUID().toString();
}
return this.uniqueName;
}
if (StringUtils.hasLength(this.name)) {
return this.name;
}
if (this.embeddedDatabaseConnection != EmbeddedDatabaseConnection.NONE) {
return "testdb";
}
return null;
}
/**
* Return the configured username or {@code null} if none was configured.
* @return the configured username
* @see #determineUsername()
*/
public String getUsername() {
return this.username;
}
public void setUsername(String username) {
this.username = username;
}
/**
* Determine the username to use based on this configuration and the environment.
* @return the username to use
* @since 1.4.0
*/
public String determineUsername() {
if (StringUtils.hasText(this.username)) {
return this.username;
}
if (EmbeddedDatabaseConnection.isEmbedded(determineDriverClassName())) {
return "sa";
}
return null;
}
/**
* Return the configured password or {@code null} if none was configured.
* @return the configured password
* @see #determinePassword()
*/
public String getPassword() {
return this.password;
}
public void setPassword(String password) {
this.password = password;
}
/**
* Determine the password to use based on this configuration and the environment.
* @return the password to use
* @since 1.4.0
*/
public String determinePassword() {
if (StringUtils.hasText(this.password)) {
return this.password;
}
if (EmbeddedDatabaseConnection.isEmbedded(determineDriverClassName())) {
return "";
}
return null;
}
public String getJndiName() {
return this.jndiName;
}
/**
* Allows the DataSource to be managed by the container and obtained via JNDI. The
* {@code URL}, {@code driverClassName}, {@code username} and {@code password} fields
* will be ignored when using JNDI lookups.
* @param jndiName the JNDI name
*/
public void setJndiName(String jndiName) {
this.jndiName = jndiName;
}
public DataSourceInitializationMode getInitializationMode() {
return this.initializationMode;
}
public void setInitializationMode(DataSourceInitializationMode initializationMode) {
this.initializationMode = initializationMode;
}
public String getPlatform() {
return this.platform;
}
public void setPlatform(String platform) {
this.platform = platform;
}
public List<String> getSchema() {
return this.schema;
}
public void setSchema(List<String> schema) {
this.schema = schema;
}
public String getSchemaUsername() {
return this.schemaUsername;
}
public void setSchemaUsername(String schemaUsername) {
this.schemaUsername = schemaUsername;
}
public String getSchemaPassword() {
return this.schemaPassword;
}
public void setSchemaPassword(String schemaPassword) {
this.schemaPassword = schemaPassword;
}
public List<String> getData() {
return this.data;
}
public void setData(List<String> data) {
this.data = data;
}
public String getDataUsername() {
return this.dataUsername;
}
public void setDataUsername(String dataUsername) {
this.dataUsername = dataUsername;
}
public String getDataPassword() {
return this.dataPassword;
}
public void setDataPassword(String dataPassword) {
this.dataPassword = dataPassword;
}
public boolean isContinueOnError() {
return this.continueOnError;
}
public void setContinueOnError(boolean continueOnError) {
this.continueOnError = continueOnError;
}
public String getSeparator() {
return this.separator;
}
public void setSeparator(String separator) {
this.separator = separator;
}
public Charset getSqlScriptEncoding() {
return this.sqlScriptEncoding;
}
public void setSqlScriptEncoding(Charset sqlScriptEncoding) {
this.sqlScriptEncoding = sqlScriptEncoding;
}
public ClassLoader getClassLoader() {
return this.classLoader;
}
public Xa getXa() {
return this.xa;
}
public void setXa(Xa xa) {
this.xa = xa;
}
/**
* XA Specific datasource settings.
*/
public static class Xa {
/**
* XA datasource fully qualified name.
*/
private String dataSourceClassName;
/**
* Properties to pass to the XA data source.
*/
private Map<String, String> properties = new LinkedHashMap<>();
public String getDataSourceClassName() {
return this.dataSourceClassName;
}
public void setDataSourceClassName(String dataSourceClassName) {
this.dataSourceClassName = dataSourceClassName;
}
public Map<String, String> getProperties() {
return this.properties;
}
public void setProperties(Map<String, String> properties) {
this.properties = properties;
}
}
static class DataSourceBeanCreationException extends BeanCreationException {
private final DataSourceProperties properties;
private final EmbeddedDatabaseConnection connection;
DataSourceBeanCreationException(String message, DataSourceProperties properties,
EmbeddedDatabaseConnection connection) {
super(message);
this.properties = properties;
this.connection = connection;
}
public DataSourceProperties getProperties() {
return this.properties;
}
public EmbeddedDatabaseConnection getConnection() {
return this.connection;
}
}
}
jdbcTemplate配置方式
springboot 关于jdbTemplate单数据源的配置非常简单,只需要配置JdbcTemplate的属性,无需再xml方式注入DataSource,JdbcTemplate的属性配置对应JdbcProperties.java类,部分属性如下:
spring.jdbc.template.max-rows=1
spring.jdbc.template.fetch-size=
spring.jdbc.template.query-timeout=
JdbcProperties.java
/*
* Copyright 2012-2018 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.time.Duration;
import java.time.temporal.ChronoUnit;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.convert.DurationUnit;
/**
* Configuration properties for JDBC.
*
* @author Kazuki Shimizu
* @author Stephane Nicoll
* @since 2.0.0
*/
@ConfigurationProperties(prefix = "spring.jdbc")
public class JdbcProperties {
private final Template template = new Template();
public Template getTemplate() {
return this.template;
}
/**
* {@code JdbcTemplate} settings.
*/
public static class Template {
/**
* Number of rows that should be fetched from the database when more rows are
* needed. Use -1 to use the JDBC driver's default configuration.
*/
private int fetchSize = -1;
/**
* Maximum number of rows. Use -1 to use the JDBC driver's default configuration.
*/
private int maxRows = -1;
/**
* Query timeout. Default is to use the JDBC driver's default configuration. If a
* duration suffix is not specified, seconds will be used.
*/
@DurationUnit(ChronoUnit.SECONDS)
private Duration queryTimeout;
public int getFetchSize() {
return this.fetchSize;
}
public void setFetchSize(int fetchSize) {
this.fetchSize = fetchSize;
}
public int getMaxRows() {
return this.maxRows;
}
public void setMaxRows(int maxRows) {
this.maxRows = maxRows;
}
public Duration getQueryTimeout() {
return this.queryTimeout;
}
public void setQueryTimeout(Duration queryTimeout) {
this.queryTimeout = queryTimeout;
}
}
}
配置完成后就可以直接使用JdbcTemplate了,使用案例如下,也可以在github上下载我的源码 下载源码
package org.zhgs.demo.springboot.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
@RestController
public class JdbcHelloWordController {
@Autowired
private JdbcTemplate jdbcTemplate;
@RequestMapping("list")
public List<Map<String, Object>> index(){
List<Map<String, Object>> resultList = appJdbcTemplate.queryForList("select * from user");
return resultList;
}
}
本机启动后打开http://localhost:8080/list 测试结果如下:表示数据库查询成功
[{"id":1,"name":"zhgs_test2"},{"id":2,"name":"gs_test2"}]
自定义DataSource配置
前面介绍了如果配置springboot的DataSource和JdbcTemplate,本节介绍下自定义DataSource如何配置,了解如何自定义DataSource是掌握配置多数据源的一个前提。
目录
- 实例化自定义DataSource、JdbcTemplate
- 自定义DataSource属性配置
- 测试
- 注解@Configuration介绍
- 注解@Bean介绍
- 注解@Qualifier介绍
- 注解@ConfigurationProperties介绍
- 注解@Primary介绍
实例化自定义DataSource、JdbcTemplate
新建一个类DataSourceConfig用于实例化自定义DataSource、JdbcTemplate,自定义的属性命名空间为app.datasource,配置属性时以这个为前缀
package org.zhgs.demo.springboot.datasource;
import com.zaxxer.hikari.HikariDataSource;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.core.JdbcTemplate;
import javax.sql.DataSource;
@Configuration
public class DataSourceConfig {
/*
@Bean(name = "appDataSource")
@ConfigurationProperties(prefix = "app.datasource")
@Primary // 主加载顺序优先,默认
@Qualifier("appDataSource") // bean别名
public HikariDataSource dataSource() {
return DataSourceBuilder.create().type(HikariDataSource.class).build();
}
*/
@Bean(name = "appJdbcTemplate")
@Primary
public JdbcTemplate jdbcTemplate(@Qualifier("appDataSource") DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
@Bean(name = "appDataSource")
@ConfigurationProperties(prefix = "app.datasource")
@Primary // 主加载顺序优先,默认
@Qualifier("appDataSource") // bean别名
public HikariDataSource dataSource(DataSourceProperties properties) {
return properties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
}
@Bean
@Primary
@ConfigurationProperties("app.datasource")
public DataSourceProperties dataSourceProperties() {
return new DataSourceProperties();
}
}
自定义DataSource属性配置
app.datasource.url=jdbc:mysql://localhost:3306/test2
app.datasource.username=root
app.datasource.password=
app.datasource.driver-class-name=com.mysql.jdbc.Driver
需要注意以上配置中的app.datasource.url是使用自定义Properties属性规则时才能使用,如果不配置自定义Properties时,使用app.datasource.jdbc-url,因为HikariDataSource中没有url,只有jdbcUrl;
app.datasource.jdbc-url=jdbc:mysql://localhost:3306/test2
app.datasource.username=root
app.datasource.password=
app.datasource.driver-class-name=com.mysql.jdbc.Driver
测试案例
package org.zhgs.demo.springboot.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
@RestController
public class JdbcHelloWordController {
@Autowired
@Qualifier("appJdbcTemplate")
private JdbcTemplate appJdbcTemplate;
/**
* 自定义DataSource
* @return
*/
@RequestMapping("self-datasource-list")
public List<Map<String, Object>> selfDataSourceList(){
List<Map<String, Object>> resultList = appJdbcTemplate.queryForList("select * from user");
return resultList;
}
}
注解@Configuration介绍
从Spring3.0,@Configuration用于定义配置类,可替换xml配置文件,被注解的类内部包含有一个或多个被@Bean注解的方法,这些方法将会被AnnotationConfigApplicationContext或AnnotationConfigWebApplicationContext类进行扫描,并用于构建bean定义,初始化Spring容器。
注意:@Configuration注解的配置类有如下要求:
@Configuration不可以是final类型;
@Configuration不可以是匿名类;
嵌套的configuration必须是静态类。
更多介绍 点我
注解@Bean介绍
@Bean标注在方法上(返回某个实例的方法),等价于spring的xml配置文件中的
注:
- @Bean注解在返回实例的方法上,如果未通过@Bean指定bean的名称,则默认与标注的方法名相同;
- @Bean注解默认作用域为单例singleton作用域,可通过@Scope(“prototype”)设置为原型作用域;
- 既然@Bean的作用是注册bean对象,那么完全可以使用@Component、@Controller、@Service、@Ripository等注解注册bean,当然需要配置@ComponentScan注解进行自动扫描。
@Bean下管理bean的生命周期
可以使用基于 Java 的配置来管理 bean 的生命周期。@Bean 支持两种属性,即 initMethod 和destroyMethod,这些属性可用于定义生命周期方法。在实例化 bean 或即将销毁它时,容器便可调用生命周期方法。生命周期方法也称为回调方法,因为它将由容器调用。使用 @Bean 注释注册的 bean 也支持 JSR-250 规定的标准 @PostConstruct 和 @PreDestroy 注释。如果您正在使用 XML 方法来定义 bean,那么就应该使用 bean 元素来定义生命周期回调方法。
@Configuation总结
- @Configuation等价于
- @Bean等价于
- @ComponentScan等价于<context:component-scan base-package=”com.dxz.demo”/>
注解@Qualifier介绍
qualifier的意思是合格者,意思是在实现同一个接口的多个service,我们引入实现时需要指定其中一个,比如:
@Qualifier("appJdbcTemplate")
private JdbcTemplate appJdbcTemplate;
“appJdbcTemplate”是我们在配置bean时定义的名称,除了我们定义的还有springboot内部自带的jdbcTemplate,我们使用时需要指定我们自定义的,这个时候qualifier就发挥了这个作用。
注解@ConfigurationProperties介绍
@ConfigurationProperties主要作用:就是绑定application.properties中的属性
DataSourceConfig类中多次使用到了此注解,无论是dataSource还是dataSourceProperties都使用此注解与属性名前缀关联。
注解@Primary介绍
同qualifier场景类似,多个实现类的时候如果按类型加载类实现不指定qualifier名称时,会默认指定Primary注解,也就是优先加载。