對於數據訪問層,不管是 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=? */ } }