对于数据访问层,不管是 SQL 仍是 NOSQL,SpringBoot 默认采用整合 SpringData 的方式进行统一处理,添加了大量的自动配置,引入了各类 Template、Repository 来简化咱们对数据访问层的操做,咱们使用时只需进行简单的配置便可。css
一、使用 maven 构建 SpringBoot 项目,引入以下场景启动器:java
二、配置数据库链接相关信息:mysql
spring: datasource: username: root password: root url: jdbc:mysql://192.168.202.135:3306/springboot_jdbc driver-class-name: com.mysql.jdbc.Driver
三、作完上述两个操做咱们就能够直接测试获取数据源了:web
package com.springboot.data_jdbc; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import javax.sql.DataSource; import java.sql.Connection; import java.sql.SQLException; @RunWith(SpringRunner.class) @SpringBootTest public class DataJdbcApplicationTests { @Autowired public DataSource dataSource; @Test public void testDataSource() throws SQLException { System.out.println(dataSource); System.out.println(dataSource.getClass()); /* org.apache.tomcat.jdbc.pool.DataSource@6107165{ConnectionPool[defaultAutoCommit=null; defaultReadOnly=null; defaultTransactionIsolation=-1; defaultCatalog=null; driverClassName=com.mysql.jdbc.Driver; maxActive=100;...} class org.apache.tomcat.jdbc.pool.DataSource */ } }
SpringBoot 经过 org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration 自动配置类为咱们自动配置了 JdbcTemplate,因此能够直接从容器中获取使用它。spring
package com.springboot.data_jdbc; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.test.context.junit4.SpringRunner; import javax.sql.DataSource; import java.sql.Connection; import java.sql.SQLException; import java.util.List; import java.util.Map; @RunWith(SpringRunner.class) @SpringBootTest public class DataJdbcApplicationTests { @Autowired public JdbcTemplate jdbcTemplate; @Test public void testJdbcTemplate() throws SQLException { List<Map<String, Object>> maps = jdbcTemplate.queryForList("select * from user;"); System.out.println(maps); /* [{id=1, name=张三}] */ } }
上述使用中咱们只是在配置文件中配置了数据库链接信息而后咱们就能够直接获取到数据源,缘由也是由于 SpringBoot 给咱们作了大量的自动配置,对应的相关自动配置类在 org.springframework.boot.autoconfigure.jdbc 包下:sql
查看 DataSourceConfiguration 类:数据库
1 abstract class DataSourceConfiguration { 2 3 @SuppressWarnings("unchecked") 4 protected static <T> T createDataSource(DataSourceProperties properties, 5 Class<? extends DataSource> type) { 6 return (T) properties.initializeDataSourceBuilder().type(type).build(); 7 } 8 9 @ConditionalOnClass(org.apache.tomcat.jdbc.pool.DataSource.class) 10 @ConditionalOnMissingBean(DataSource.class) 11 @ConditionalOnProperty(name = "spring.datasource.type", havingValue = "org.apache.tomcat.jdbc.pool.DataSource", matchIfMissing = true) 12 static class Tomcat { 13 14 @Bean 15 @ConfigurationProperties(prefix = "spring.datasource.tomcat") 16 public org.apache.tomcat.jdbc.pool.DataSource dataSource( 17 DataSourceProperties properties) { 18 org.apache.tomcat.jdbc.pool.DataSource dataSource = createDataSource( 19 properties, org.apache.tomcat.jdbc.pool.DataSource.class); 20 DatabaseDriver databaseDriver = DatabaseDriver 21 .fromJdbcUrl(properties.determineUrl()); 22 String validationQuery = databaseDriver.getValidationQuery(); 23 if (validationQuery != null) { 24 dataSource.setTestOnBorrow(true); 25 dataSource.setValidationQuery(validationQuery); 26 } 27 return dataSource; 28 } 29 30 } 31 32 @ConditionalOnClass(HikariDataSource.class) 33 @ConditionalOnMissingBean(DataSource.class) 34 @ConditionalOnProperty(name = "spring.datasource.type", havingValue = "com.zaxxer.hikari.HikariDataSource", matchIfMissing = true) 35 static class Hikari { 36 37 @Bean 38 @ConfigurationProperties(prefix = "spring.datasource.hikari") 39 public HikariDataSource dataSource(DataSourceProperties properties) { 40 return createDataSource(properties, HikariDataSource.class); 41 } 42 43 } 44 45 @ConditionalOnClass(org.apache.commons.dbcp.BasicDataSource.class) 46 @ConditionalOnMissingBean(DataSource.class) 47 @ConditionalOnProperty(name = "spring.datasource.type", havingValue = "org.apache.commons.dbcp.BasicDataSource", matchIfMissing = true) 48 @Deprecated 49 static class Dbcp { 50 51 @Bean 52 @ConfigurationProperties(prefix = "spring.datasource.dbcp") 53 public org.apache.commons.dbcp.BasicDataSource dataSource( 54 DataSourceProperties properties) { 55 org.apache.commons.dbcp.BasicDataSource dataSource = createDataSource( 56 properties, org.apache.commons.dbcp.BasicDataSource.class); 57 DatabaseDriver databaseDriver = DatabaseDriver 58 .fromJdbcUrl(properties.determineUrl()); 59 String validationQuery = databaseDriver.getValidationQuery(); 60 if (validationQuery != null) { 61 dataSource.setTestOnBorrow(true); 62 dataSource.setValidationQuery(validationQuery); 63 } 64 return dataSource; 65 } 66 67 } 68 69 @ConditionalOnClass(org.apache.commons.dbcp2.BasicDataSource.class) 70 @ConditionalOnMissingBean(DataSource.class) 71 @ConditionalOnProperty(name = "spring.datasource.type", havingValue = "org.apache.commons.dbcp2.BasicDataSource", matchIfMissing = true) 72 static class Dbcp2 { 73 74 @Bean 75 @ConfigurationProperties(prefix = "spring.datasource.dbcp2") 76 public org.apache.commons.dbcp2.BasicDataSource dataSource( 77 DataSourceProperties properties) { 78 return createDataSource(properties, 79 org.apache.commons.dbcp2.BasicDataSource.class); 80 } 81 82 } 83 84 @ConditionalOnMissingBean(DataSource.class) 85 @ConditionalOnProperty(name = "spring.datasource.type") 86 static class Generic { 87 88 @Bean 89 public DataSource dataSource(DataSourceProperties properties) { 90 return properties.initializeDataSourceBuilder().build(); 91 } 92 93 } 94 }
能够看到在当前工程引入不一样数据源依赖时 SpringBoot 会给咱们自动注册不一样类型的数据源 bean,默认提供以下几个数据源的自动配置:apache
org.apache.tomcat.jdbc.pool.DataSource # 因 web 场景启动器默认引入了 tomcat 依赖,因此默认使用该数据源 com.zaxxer.hikari.HikariDataSource org.apache.commons.dbcp.BasicDataSource org.apache.commons.dbcp2.BasicDataSource
除了上面几个可自动配置的数据源,在第 86-93 行还有一个 Generic 内部类,该内部类的做用是为咱们提供定制其它数据源功能的支持。它是如何让咱们实现自定义数据源的呢?tomcat
首先该内部类起做用的前提是咱们在 IoC 容器中没有注册数据源,而且还在配置中经过 spring.datasource.type 指定了数据源类型。知足这两个条件后才会作以下操做:springboot
dataSource 方法是用来想容器中注册一个数据源 bean,而这个 bean 的是由第 90 行经过 properties.initializeDataSourceBuilder() 初始化的一个数据源构建器的 build() 生成的,查看该方法:
1 public DataSourceBuilder initializeDataSourceBuilder() { 2 return DataSourceBuilder.create(getClassLoader()).type(getType()) 3 .driverClassName(determineDriverClassName()).url(determineUrl()) 4 .username(determineUsername()).password(determinePassword()); 5 }
该方法建立了一个数据源构建器,接着将数据库链接信息绑定到该构建器,而这些数据库链接信息的值正是咱们在配置文件中配置的 spring.datasource 节下的属性值,最后返回该构建器的实例,接着调用该构建器的 build() 方法:
1 public DataSource build() { 2 Class<? extends DataSource> type = getType(); 3 DataSource result = BeanUtils.instantiate(type); 4 maybeGetDriverClassName(); 5 bind(result); 6 return result; 7 }
最终利用反射建立对应类型数据源的实例,绑定数据库链接信息,返回了数据源。
再查看 DataSourceAutoConfiguration 类:
1 @Configuration 2 @ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class }) 3 @EnableConfigurationProperties(DataSourceProperties.class) 4 @Import({ Registrar.class, DataSourcePoolMetadataProvidersConfiguration.class }) 5 public class DataSourceAutoConfiguration { 6 7 private static final Log logger = LogFactory 8 .getLog(DataSourceAutoConfiguration.class); 9 10 @Bean 11 @ConditionalOnMissingBean 12 public DataSourceInitializer dataSourceInitializer(DataSourceProperties properties, 13 ApplicationContext applicationContext) { 14 return new DataSourceInitializer(properties, applicationContext); 15 } 16 17 public static boolean containsAutoConfiguredDataSource( 18 ConfigurableListableBeanFactory beanFactory) { 19 try { 20 BeanDefinition beanDefinition = beanFactory.getBeanDefinition("dataSource"); 21 return EmbeddedDataSourceConfiguration.class.getName() 22 .equals(beanDefinition.getFactoryBeanName()); 23 } 24 catch (NoSuchBeanDefinitionException ex) { 25 return false; 26 } 27 } 28 29 @Configuration 30 @Conditional(EmbeddedDatabaseCondition.class) 31 @ConditionalOnMissingBean({ DataSource.class, XADataSource.class }) 32 @Import(EmbeddedDataSourceConfiguration.class) 33 protected static class EmbeddedDatabaseConfiguration { 34 35 } 36 37 @Configuration 38 @Conditional(PooledDataSourceCondition.class) 39 @ConditionalOnMissingBean({ DataSource.class, XADataSource.class }) 40 @Import({ DataSourceConfiguration.Tomcat.class, DataSourceConfiguration.Hikari.class, 41 DataSourceConfiguration.Dbcp.class, DataSourceConfiguration.Dbcp2.class, 42 DataSourceConfiguration.Generic.class }) 43 @SuppressWarnings("deprecation") 44 protected static class PooledDataSourceConfiguration { 45 46 } 47 48 @Configuration 49 @ConditionalOnProperty(prefix = "spring.datasource", name = "jmx-enabled") 50 @ConditionalOnClass(name = "org.apache.tomcat.jdbc.pool.DataSourceProxy") 51 @Conditional(DataSourceAutoConfiguration.DataSourceAvailableCondition.class) 52 @ConditionalOnMissingBean(name = "dataSourceMBean") 53 protected static class TomcatDataSourceJmxConfiguration { 54 55 @Bean 56 public Object dataSourceMBean(DataSource dataSource) { 57 if (dataSource instanceof DataSourceProxy) { 58 try { 59 return ((DataSourceProxy) dataSource).createPool().getJmxPool(); 60 } 61 catch (SQLException ex) { 62 logger.warn("Cannot expose DataSource to JMX (could not connect)"); 63 } 64 } 65 return null; 66 } 67 68 } 69 70 static class PooledDataSourceCondition extends AnyNestedCondition { 71 72 PooledDataSourceCondition() { 73 super(ConfigurationPhase.PARSE_CONFIGURATION); 74 } 75 76 @ConditionalOnProperty(prefix = "spring.datasource", name = "type") 77 static class ExplicitType { 78 79 } 80 81 @Conditional(PooledDataSourceAvailableCondition.class) 82 static class PooledDataSourceAvailable { 83 84 } 85 86 } 87 88 static class PooledDataSourceAvailableCondition extends SpringBootCondition { 89 90 @Override 91 public ConditionOutcome getMatchOutcome(ConditionContext context, 92 AnnotatedTypeMetadata metadata) { 93 ConditionMessage.Builder message = ConditionMessage 94 .forCondition("PooledDataSource"); 95 if (getDataSourceClassLoader(context) != null) { 96 return ConditionOutcome 97 .match(message.foundExactly("supported DataSource")); 98 } 99 return ConditionOutcome 100 .noMatch(message.didNotFind("supported DataSource").atAll()); 101 } 102 103 private ClassLoader getDataSourceClassLoader(ConditionContext context) { 104 Class<?> dataSourceClass = new DataSourceBuilder(context.getClassLoader()) 105 .findType(); 106 return (dataSourceClass != null) ? dataSourceClass.getClassLoader() : null; 107 } 108 109 } 110 111 static class EmbeddedDatabaseCondition extends SpringBootCondition { 112 113 private final SpringBootCondition pooledCondition = new PooledDataSourceCondition(); 114 115 @Override 116 public ConditionOutcome getMatchOutcome(ConditionContext context, 117 AnnotatedTypeMetadata metadata) { 118 ConditionMessage.Builder message = ConditionMessage 119 .forCondition("EmbeddedDataSource"); 120 if (anyMatches(context, metadata, this.pooledCondition)) { 121 return ConditionOutcome 122 .noMatch(message.foundExactly("supported pooled data source")); 123 } 124 EmbeddedDatabaseType type = EmbeddedDatabaseConnection 125 .get(context.getClassLoader()).getType(); 126 if (type == null) { 127 return ConditionOutcome 128 .noMatch(message.didNotFind("embedded database").atAll()); 129 } 130 return ConditionOutcome.match(message.found("embedded database").items(type)); 131 } 132 133 } 134 135 @Order(Ordered.LOWEST_PRECEDENCE - 10) 136 static class DataSourceAvailableCondition extends SpringBootCondition { 137 138 private final SpringBootCondition pooledCondition = new PooledDataSourceCondition(); 139 140 private final SpringBootCondition embeddedCondition = new EmbeddedDatabaseCondition(); 141 142 @Override 143 public ConditionOutcome getMatchOutcome(ConditionContext context, 144 AnnotatedTypeMetadata metadata) { 145 ConditionMessage.Builder message = ConditionMessage 146 .forCondition("DataSourceAvailable"); 147 if (hasBean(context, DataSource.class) 148 || hasBean(context, XADataSource.class)) { 149 return ConditionOutcome 150 .match(message.foundExactly("existing data source bean")); 151 } 152 if (anyMatches(context, metadata, this.pooledCondition, 153 this.embeddedCondition)) { 154 return ConditionOutcome.match(message 155 .foundExactly("existing auto-configured data source bean")); 156 } 157 return ConditionOutcome 158 .noMatch(message.didNotFind("any existing data source bean").atAll()); 159 } 160 161 private boolean hasBean(ConditionContext context, Class<?> type) { 162 return BeanFactoryUtils.beanNamesForTypeIncludingAncestors( 163 context.getBeanFactory(), type, true, false).length > 0; 164 } 165 166 } 167 168 }
看第 12 行, dataSourceInitializer() 方法给容器中注册了一个数据源初始化器,查看初始化器类:
1 class DataSourceInitializer implements ApplicationListener<DataSourceInitializedEvent> { 2 3 private static final Log logger = LogFactory.getLog(DataSourceInitializer.class); 4 5 private final DataSourceProperties properties; 6 7 private final ApplicationContext applicationContext; 8 9 private DataSource dataSource; 10 11 private boolean initialized = false; 12 13 DataSourceInitializer(DataSourceProperties properties, 14 ApplicationContext applicationContext) { 15 this.properties = properties; 16 this.applicationContext = applicationContext; 17 } 18 19 @PostConstruct 20 public void init() { 21 if (!this.properties.isInitialize()) { 22 logger.debug("Initialization disabled (not running DDL scripts)"); 23 return; 24 } 25 if (this.applicationContext.getBeanNamesForType(DataSource.class, false, 26 false).length > 0) { 27 this.dataSource = this.applicationContext.getBean(DataSource.class); 28 } 29 if (this.dataSource == null) { 30 logger.debug("No DataSource found so not initializing"); 31 return; 32 } 33 runSchemaScripts(); 34 } 35 36 private void runSchemaScripts() { 37 List<Resource> scripts = getScripts("spring.datasource.schema", 38 this.properties.getSchema(), "schema"); 39 if (!scripts.isEmpty()) { 40 String username = this.properties.getSchemaUsername(); 41 String password = this.properties.getSchemaPassword(); 42 runScripts(scripts, username, password); 43 try { 44 this.applicationContext 45 .publishEvent(new DataSourceInitializedEvent(this.dataSource)); 46 if (!this.initialized) { 47 runDataScripts(); 48 this.initialized = true; 49 } 50 } 51 catch (IllegalStateException ex) { 52 logger.warn("Could not send event to complete DataSource initialization (" 53 + ex.getMessage() + ")"); 54 } 55 } 56 } 57 58 @Override 59 public void onApplicationEvent(DataSourceInitializedEvent event) { 60 if (!this.properties.isInitialize()) { 61 logger.debug("Initialization disabled (not running data scripts)"); 62 return; 63 } 64 if (!this.initialized) { 65 runDataScripts(); 66 this.initialized = true; 67 } 68 } 69 70 private void runDataScripts() { 71 List<Resource> scripts = getScripts("spring.datasource.data", 72 this.properties.getData(), "data"); 73 String username = this.properties.getDataUsername(); 74 String password = this.properties.getDataPassword(); 75 runScripts(scripts, username, password); 76 } 77 78 private List<Resource> getScripts(String propertyName, List<String> resources, 79 String fallback) { 80 if (resources != null) { 81 return getResources(propertyName, resources, true); 82 } 83 String platform = this.properties.getPlatform(); 84 List<String> fallbackResources = new ArrayList<String>(); 85 fallbackResources.add("classpath*:" + fallback + "-" + platform + ".sql"); 86 fallbackResources.add("classpath*:" + fallback + ".sql"); 87 return getResources(propertyName, fallbackResources, false); 88 } 89 90 private List<Resource> getResources(String propertyName, List<String> locations, 91 boolean validate) { 92 List<Resource> resources = new ArrayList<Resource>(); 93 for (String location : locations) { 94 for (Resource resource : doGetResources(location)) { 95 if (resource.exists()) { 96 resources.add(resource); 97 } 98 else if (validate) { 99 throw new ResourceNotFoundException(propertyName, resource); 100 } 101 } 102 } 103 return resources; 104 } 105 106 private Resource[] doGetResources(String location) { 107 try { 108 SortedResourcesFactoryBean factory = new SortedResourcesFactoryBean( 109 this.applicationContext, Collections.singletonList(location)); 110 factory.afterPropertiesSet(); 111 return factory.getObject(); 112 } 113 catch (Exception ex) { 114 throw new IllegalStateException("Unable to load resources from " + location, 115 ex); 116 } 117 } 118 119 private void runScripts(List<Resource> resources, String username, String password) { 120 if (resources.isEmpty()) { 121 return; 122 } 123 ResourceDatabasePopulator populator = new ResourceDatabasePopulator(); 124 populator.setContinueOnError(this.properties.isContinueOnError()); 125 populator.setSeparator(this.properties.getSeparator()); 126 if (this.properties.getSqlScriptEncoding() != null) { 127 populator.setSqlScriptEncoding(this.properties.getSqlScriptEncoding().name()); 128 } 129 for (Resource resource : resources) { 130 populator.addScript(resource); 131 } 132 DataSource dataSource = this.dataSource; 133 if (StringUtils.hasText(username) && StringUtils.hasText(password)) { 134 dataSource = DataSourceBuilder.create(this.properties.getClassLoader()) 135 .driverClassName(this.properties.determineDriverClassName()) 136 .url(this.properties.determineUrl()).username(username) 137 .password(password).build(); 138 } 139 DatabasePopulatorUtils.execute(populator, dataSource); 140 } 141 142 }
在第 20 行有一个初始化方法,该方法会在当前类实例建立完成以后执行,在第 33 行执行了 runSchemaScripts() 方法,这里直接说明该方法的做用,该方法是使用来执行指定位置存放的 sql 文件中的 DDL 语句。
接着在第 37 行经过 getScripts("spring.datasource.schema", this.properties.getSchema(), "schema") 方法获取一个 DDL 脚本资源列表。接着来到第 78 行的 getScripts 方法,若是咱们没有在配置文件中经过 spring.datasource.schema 属性指定 DDL sql 文件路径列表,那么将默认使用 classpath*:schema-all.sql 和 classpath*:schema.sql 位置的资源,即会执行该 sql 资源文件中的 DDL 语句。也能够经过配置 spring.datasource.schema 属性来指定一个存放有 DDL 语句的 sql 文件资源路径列表。
能够看到该类还实现了 ApplicationListener 监听器接口,即应用程序启动完成后会调用该类实例的 onApplicationEvent 方法,在该方法中执行了 runDataScripts() 方法,而该方法的做用是用来执行指定位置存放的 sql 文件中的 DML 语句。
接着在第 70 行的 runDataScripts() 方法中执行了 getScripts("spring.datasource.data", this.properties.getData(), "data") 来获取 DML 脚本资源列表,而后在第 71 行执行 getScripts("spring.datasource.data", this.properties.getData(), "data") 方法,与以前的 runSchemaScripts() 相似,若是咱们没有在配置文件中经过 spring.datasource.data 属性指定 DML sql 文件的路径列表,那么将默认使用 classpath*:data-all.sql 和 classpath*:data.sql 位置的资源,即会执行该 sql 资源文件中的 DML 语句。也能够经过配置 spring.datasource.data 属性指定 DML sql 文件路径列表。
spring: datasource: username: root password: root url: jdbc:mysql://192.168.202.135:3306/springboot_jdbc driver-class-name: com.mysql.jdbc.Driver schema: - classpath:myschema.sql data: - classpath:mydata.sql
DROP TABLE IF EXISTS `user`; CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;
insert into user(name) values('张三');
项目启动时将会执行 myschema 建立 user 表,并会执行 mydata.sql 往 user 表中添加一条数据。
要引入 Druid 依赖:
<dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.15</version> </dependency>
经过上面的源码分析咱们已经知道,若是咱们要切换数据源,只须要配置 spring.datasource.type 为指定的数据源类型便可,以下:
spring: datasource: username: root password: root url: jdbc:mysql://192.168.202.135:3306/springboot_jdbc driver-class-name: com.mysql.jdbc.Driver schema: - classpath:myschema.sql data: - classpath:mydata.sql type: com.alibaba.druid.pool.DruidDataSource
测试:
package com.springboot.data_jdbc; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.test.context.junit4.SpringRunner; import javax.sql.DataSource; import java.sql.Connection; import java.sql.SQLException; import java.util.List; import java.util.Map; @RunWith(SpringRunner.class) @SpringBootTest public class DataJdbcApplicationTests { @Autowired public DataSource dataSource; @Test public void testDataSource() throws SQLException { System.out.println(dataSource.getClass()); /* class com.alibaba.druid.pool.DruidDataSource */ } }
方式一其实有一个弊端,若是咱们要使用 Druid 独有的配置,例如要配置 Druid 监控,仅仅在配置文件中是完成不了这个需求的,此时咱们就须要手动注册数据源,而且手动将配置的属性的绑定到数据源实例。
配置:
spring: datasource: username: root password: root url: jdbc:mysql://192.168.202.135:3306/springboot_jdbc driver-class-name: com.mysql.jdbc.Driver # Durid 独有的属性 initialSize: 5 minIdle: 5 maxActive: 20 maxWait: 60000 timeBetweenEvictionRunsMillis: 60000 minEvictableIdleTimeMillis: 300000 validationQuery: select 1 from DUAL testWhileIdle: true testOnBorrow: false testOnReturn: false poolPreparedStatements: true # 配置监控统计拦截的 filter ,去掉后监控界面没法统计,wall 用于防火墙 filters: stat,wall,log4j maxPoolPreparedStatementPerConnectionSize: 20 useGlobalDataSourceStat: true connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
手动注册数据源 bean 并配置监控:
package com.springboot.data_jdbc.config; import com.alibaba.druid.pool.DruidDataSource; import com.alibaba.druid.support.http.StatViewServlet; import com.alibaba.druid.support.http.WebStatFilter; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.boot.web.servlet.ServletRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import javax.sql.DataSource; import java.util.Arrays; import java.util.HashMap; import java.util.Map; @Configuration public class DuridConfig { @ConfigurationProperties("spring.datasource") // 手动绑定配置属性 @Bean public DataSource duridDataSource(){ return new DruidDataSource(); } // 配置 Druid 监控 // 一、配置一个管理后台的 Servlet @Bean public ServletRegistrationBean statViewServlet(){ ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*"); Map<String, String> initParams = new HashMap<>(); initParams.put("loginUsername", "admin"); // 登陆后台的用户名 initParams.put("loginPassword", "123456"); initParams.put("allow",""); // 默认容许全部域名及 IP 访问 initParams.put("deny","127.0.0.1"); // 拒绝 127.0.0.1 访问 servletRegistrationBean.setInitParameters(initParams); return servletRegistrationBean; } // 二、配置一个监控的 filter @Bean public FilterRegistrationBean webStatFilter(){ FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(); WebStatFilter webStatFilter = new WebStatFilter(); filterRegistrationBean.setFilter(webStatFilter); Map<String, String> initParams = new HashMap<>(); initParams.put("exclusions", "*.js,*.css,/druid/*"); filterRegistrationBean.setInitParameters(initParams); filterRegistrationBean.setUrlPatterns(Arrays.asList("/*")); return filterRegistrationBean; } }
测试:
package com.springboot.data_jdbc; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.test.context.junit4.SpringRunner; import javax.sql.DataSource; import java.sql.Connection; import java.sql.SQLException; import java.util.List; import java.util.Map; @RunWith(SpringRunner.class) @SpringBootTest public class DataJdbcApplicationTests { @Autowired public DataSource dataSource; @Test public void testDataSource() throws SQLException { System.out.println(dataSource.getClass()); /* class com.alibaba.druid.pool.DruidDataSource */ } }
启动项目,可经过 localhost:8080/druid 访问到监控平台:
一、使用 maven 构建 SpringBoot 项目,引入以下场景启动器:
二、引入 Druid 依赖,配置 Druid 数据源,初始化测试表:
spring: datasource: username: root password: root url: jdbc:mysql://192.168.202.135:3306/springboot_mybatis driver-class-name: com.mysql.jdbc.Driver # Durid 独有的属性 initialSize: 5 minIdle: 5 maxActive: 20 maxWait: 60000 timeBetweenEvictionRunsMillis: 60000 minEvictableIdleTimeMillis: 300000 validationQuery: select 1 from DUAL testWhileIdle: true testOnBorrow: false testOnReturn: false poolPreparedStatements: true # 配置监控统计拦截的 filter ,去掉后监控界面没法统计,wall 用于防火墙 filters: stat,wall,log4j maxPoolPreparedStatementPerConnectionSize: 20 useGlobalDataSourceStat: true connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500 schema: - classpath:sql/user-schema.sql data: - classpath:sql/user-data.sql
SET FOREIGN_KEY_CHECKS=0; -- ---------------------------- -- Table structure for user -- ---------------------------- DROP TABLE IF EXISTS `user`; CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(40) DEFAULT NULL, `gender` int(11) DEFAULT NULL COMMENT '0:女 1:男', `birthday` date DEFAULT NULL, `address` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4;
INSERT INTO `user` VALUES ('1', '张三', '1', '1997-02-23', '北京'); INSERT INTO `user` VALUES ('2', '李四', '0', '1998-02-03', '武汉'); INSERT INTO `user` VALUES ('3', '王五', '1', '1996-06-04', '上海');
package com.springboot.data_mybatis.config; import com.alibaba.druid.pool.DruidDataSource; import com.alibaba.druid.support.http.StatViewServlet; import com.alibaba.druid.support.http.WebStatFilter; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.boot.web.servlet.ServletRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import javax.sql.DataSource; import java.util.Arrays; import java.util.HashMap; import java.util.Map; @Configuration public class DuridConfig { @ConfigurationProperties("spring.datasource") // 手动绑定配置属性 @Bean public DataSource duridDataSource(){ return new DruidDataSource(); } // 配置 Druid 监控 // 一、配置一个管理后台的 Servlet @Bean public ServletRegistrationBean statViewServlet(){ ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*"); Map<String, String> initParams = new HashMap<>(); initParams.put("loginUsername", "admin"); // 登陆后台的用户名 initParams.put("loginPassword", "123456"); initParams.put("allow",""); // 默认容许全部域名及 IP 访问 initParams.put("deny","127.0.0.1"); // 拒绝 127.0.0.1 访问 servletRegistrationBean.setInitParameters(initParams); return servletRegistrationBean; } // 二、配置一个监控的 filter @Bean public FilterRegistrationBean webStatFilter(){ FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(); WebStatFilter webStatFilter = new WebStatFilter(); filterRegistrationBean.setFilter(webStatFilter); Map<String, String> initParams = new HashMap<>(); initParams.put("exclusions", "*.js,*.css,/druid/*"); filterRegistrationBean.setInitParameters(initParams); filterRegistrationBean.setUrlPatterns(Arrays.asList("/*")); return filterRegistrationBean; } }
三、建立与测试表对应的 JavaBean:
package com.springboot.data_mybatis.bean; import java.util.Date; public class User { private Integer id; private String name; private Integer gender; private Date birthday; private String address; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getGender() { return gender; } public void setGender(Integer gender) { this.gender = gender; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } @Override public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + '}'; } }
编写 mapper 类,并在 mapper 类中经过注解绑定 sql:
package com.springboot.data_mybatis.mapper; import com.springboot.data_mybatis.bean.User; import org.apache.ibatis.annotations.*; import java.util.List; @Mapper public interface UserMappper { @Select("select * from user") public List<User> getAll(); @Select("select * from user where id=#{id}") public User getById(Integer id); @Delete("delete from user where id=#{id}") public Integer deleteById(Integer id); @Options(useGeneratedKeys = true,keyProperty = "id") @Insert("insert into user(name,gender,birthday,address) values(#{name},#{gender},#{birthday},#{address})") public Integer add(User user); @Update("update user set name=#{name},gender=#{gender},birthday=#{birthday},address=#{address} where id=#{id}") public Integer update(User user); }
测试:
package com.springboot.data_mybatis; import com.springboot.data_mybatis.bean.User; import com.springboot.data_mybatis.mapper.UserMappper; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import javax.sql.DataSource; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.List; @RunWith(SpringRunner.class) @SpringBootTest @SuppressWarnings("all") public class DataMybatisApplicationTests { @Autowired private DataSource dataSource; @Test public void test() { System.out.println(dataSource.getClass()); } @Autowired private UserMappper userMappper; @Test public void testGetAll(){ List<User> all = userMappper.getAll(); System.out.println(all); /* [User{id=1, name='张三'}, User{id=2, name='李四'}, User{id=3, name='王五'}] */ } @Test public void testGetById(){ User user = userMappper.getById(1); System.out.println(user); /* User{id=1, name='张三'} */ } @Test public void testAdd() throws ParseException { User user = new User(); user.setName("赵六"); user.setGender(1); user.setBirthday(new SimpleDateFormat("yyyy-MM-dd").parse("1998-2-2")); user.setAddress("南京"); Integer count = userMappper.add(user); System.out.println(count); /* 1 */ } @Test public void testUpdate(){ User user = userMappper.getById(4); user.setGender(0); Integer count = userMappper.update(user); System.out.println(count); /* 1 */ } @Test public void testDelete(){ Integer count = userMappper.deleteById(4); System.out.println(count); /* 1 */ } }
示例中是在 mapper 类上添加了 @Mapper 注解用来标识所标注的类是一个 Mapper 类,还能够经过 @MapperScan 注解配置 mapper 类的包扫描:
@MapperScan("com.springboot.data_mybatis.mapper")
能够看到上述注解方式没有 MyBatis 的核心配置文件,若是须要自定制 MyBatis 的部分配置,SpringBoot 给咱们提供了 MyBatis 配置自定制类,咱们只须要设置好该类实例相关属性将其放入 IoC 容器便可生效:
package com.springboot.data_mybatis.config; import org.apache.ibatis.session.Configuration; import org.mybatis.spring.boot.autoconfigure.ConfigurationCustomizer; import org.springframework.context.annotation.Bean; @org.springframework.context.annotation.Configuration public class MyBatisConfig { @Bean public ConfigurationCustomizer configurationCustomizer(){ return new ConfigurationCustomizer() { @Override public void customize(Configuration configuration) { // 启用驼峰命名,表字段 user_name 可映射到 userName 属性 configuration.setMapUnderscoreToCamelCase(true); } }; } }
添加 MyBatis 核心配置文件:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <settings> <!--启用驼峰命名,表字段 user_name 可映射到 userName --> <setting name="mapUnderscoreToCamelCase" value="true"/> </settings> </configuration>
在 SpringBoot 配置文件中指定 MyBatis 核心配置文件和映射文件位置:
spring: datasource: username: root password: root url: jdbc:mysql://192.168.202.135:3306/springboot_mybatis driver-class-name: com.mysql.jdbc.Driver # Durid 独有的属性 initialSize: 5 minIdle: 5 maxActive: 20 maxWait: 60000 timeBetweenEvictionRunsMillis: 60000 minEvictableIdleTimeMillis: 300000 validationQuery: select 1 from DUAL testWhileIdle: true testOnBorrow: false testOnReturn: false poolPreparedStatements: true # 配置监控统计拦截的 filter ,去掉后监控界面没法统计,wall 用于防火墙 filters: stat,wall,log4j maxPoolPreparedStatementPerConnectionSize: 20 useGlobalDataSourceStat: true connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500 # schema: # - classpath:sql/user-schema.sql # data: # - classpath:sql/user-data.sql mybatis: config-location: classpath:mybatis/mybatis-config.xml mapper-locations: classpath:mybatis/mapper/*.xml
编写 mapper 类:
package com.springboot.data_mybatis.mapper; import com.springboot.data_mybatis.bean.User; import java.util.List; public interface UserMappper { public List<User> getAll(); public User getById(Integer id); public Integer deleteById(Integer id); public Integer add(User user); public Integer update(User user); }
编写映射文件:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.springboot.data_mybatis.mapper.UserMappper"> <select id="getAll" resultType="com.springboot.data_mybatis.bean.User"> select * from user </select> <select id="getById" parameterType="int" resultType="com.springboot.data_mybatis.bean.User"> select * from user where id=#{id} </select> <delete id="deleteById" parameterType="int"> delete from user where id=#{id} </delete> <insert id="add" parameterType="com.springboot.data_mybatis.bean.User" useGeneratedKeys="true" keyProperty="id"> insert into user(name,gender,birthday,address) values(#{name},#{gender},#{birthday},#{address}) </insert> <update id="update" parameterType="com.springboot.data_mybatis.bean.User"> update user set name=#{name},gender=#{gender},birthday=#{birthday},address=#{address} where id=#{id} </update> </mapper>
使用注解配置 mapper 类的包扫描:
package com.springboot.data_mybatis; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication @MapperScan("com.springboot.data_mybatis.mapper") public class DataMybatisApplication { public static void main(String[] args) { SpringApplication.run(DataMybatisApplication.class, args); } }
测试:
package com.springboot.data_mybatis; import com.springboot.data_mybatis.bean.User; import com.springboot.data_mybatis.mapper.UserMappper; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import javax.sql.DataSource; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.List; @RunWith(SpringRunner.class) @SpringBootTest @SuppressWarnings("all") public class DataMybatisApplicationTests { @Autowired private DataSource dataSource; @Test public void test() { System.out.println(dataSource.getClass()); } @Autowired private UserMappper userMappper; @Test public void testGetAll(){ List<User> all = userMappper.getAll(); System.out.println(all); /* [User{id=1, name='张三'}, User{id=2, name='李四'}, User{id=3, name='王五'}] */ } @Test public void testGetById(){ User user = userMappper.getById(1); System.out.println(user); /* User{id=1, name='张三'} */ } @Test public void testAdd() throws ParseException { User user = new User(); user.setName("赵六"); user.setGender(1); user.setBirthday(new SimpleDateFormat("yyyy-MM-dd").parse("1998-2-2")); user.setAddress("南京"); Integer count = userMappper.add(user); System.out.println(count); /* 1 */ } @Test public void testUpdate(){ User user = userMappper.getById(5); user.setGender(0); Integer count = userMappper.update(user); System.out.println(count); /* 1 */ } @Test public void testDelete(){ Integer count = userMappper.deleteById(5); System.out.println(count); /* 1 */ } }
一、使用 maven 构建 SpringBoot 项目,引入如下依赖:
二、配置数据源及 JPA:
spring: datasource: username: root password: root url: jdbc:mysql://192.168.202.135:3306/springboot_jpa driver-class-name: com.mysql.jdbc.Driver jpa: # JPA 默认使用 Hibernate 做为实现,因此可使用 Hibernate 配置 hibernate: # 更新或建立数据表 ddl-auto: update # 输出执行的 sql show-sql: true
三、编写一个用于和数据库表映射的 JavaBean 即实体类,并配置好映射关系:
package com.springboot.data_jpa.bean; import javax.persistence.*; import java.util.Date; // 使用 JPA 注解配置映射关系 @Entity // 告诉 JPA 这是一个实体类 @Table(name = "user") // 指定与哪张数据表对应,若是省略默认表名为类名首字母小写 public class User { @Id // 标识该字段为主键 @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; @Column(name = "name",length = 40) // 指定与表中的哪一个字段对应 private String name; @Column // 若是省略默认列名为属性名 private Integer gender; @Column private Date birthday; @Column private String address; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getGender() { return gender; } public void setGender(Integer gender) { this.gender = gender; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } @Override public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + '}'; } }
四、编写 Repository 接口来操做实体类对应的数据表:
package com.springboot.data_jpa.repository; import com.springboot.data_jpa.bean.User; import org.springframework.data.jpa.repository.JpaRepository; /** * 第一个类型参数为要操做的实体类型 * 第二个类型参数为实体对应的主键类型 */ public interface UserRepository extends JpaRepository<User,Integer> { }
五、测试:
package com.springboot.data_jpa; import com.springboot.data_jpa.bean.User; import com.springboot.data_jpa.repository.UserRepository; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.List; @RunWith(SpringRunner.class) @SpringBootTest public class DataJpaApplicationTests { @Autowired private UserRepository userRepository; @Test public void test() { System.out.println(userRepository); /* org.springframework.data.jpa.repository.support.SimpleJpaRepository@b022551 */ } @Test public void testAdd() throws ParseException { User user = new User(); user.setName("赵六"); user.setGender(1); user.setBirthday(new SimpleDateFormat("yyyy-MM-dd").parse("1998-2-2")); user.setAddress("南京"); User u = userRepository.save(user); System.out.println(u); /* Hibernate: insert into user (address, birthday, gender, name) values (?, ?, ?, ?) User{id=4, name='赵六'} */ } @Test public void testGetAll(){ List<User> all = userRepository.findAll(); System.out.println(all); /* Hibernate: select user0_.id as id1_0_, user0_.address as address2_0_, user0_.birthday as birthday3_0_, user0_.gender as gender4_0_, user0_.name as name5_0_ from user user0_ [User{id=1, name='张三'}, User{id=2, name='李四'}, User{id=3, name='王五'}, User{id=4, name='赵六'}] */ } @Test public void testGetById(){ User user = userRepository.findOne(1); System.out.println(user); /* Hibernate: select user0_.id as id1_0_0_, user0_.address as address2_0_0_, user0_.birthday as birthday3_0_0_, user0_.gender as gender4_0_0_, user0_.name as name5_0_0_ from user user0_ where user0_.id=? User{id=1, name='张三'} */ } @Test public void testUpdate(){ User user = userRepository.findOne(4); user.setGender(1); // 有主键时修改 不然保存 userRepository.save(user); /* Hibernate: select user0_.id as id1_0_0_, user0_.address as address2_0_0_, user0_.birthday as birthday3_0_0_, user0_.gender as gender4_0_0_, user0_.name as name5_0_0_ from user user0_ where user0_.id=? Hibernate: update user set address=?, birthday=?, gender=?, name=? where id=? */ } @Test public void testDelete(){ userRepository.delete(4); /* Hibernate: select user0_.id as id1_0_0_, user0_.address as address2_0_0_, user0_.birthday as birthday3_0_0_, user0_.gender as gender4_0_0_, user0_.name as name5_0_0_ from user user0_ where user0_.id=? Hibernate: delete from user where id=? */ } }