6、SpringBootWeb开发-整合数据

一、整合JDBC

1、新建web项目

  引入JDBC APImysql DriverSpring Web

Springboot会自动帮我们导入以下启动器

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency> 

2、编写yaml配置文件

spring:
  datasource:
    username: root
    password: zhixi158
    url: jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
    driver-class-name: com.mysql.cj.jdbc.Driver

测试数据库连接信息:

@SpringBootTest
class Springboot03DataApplicationTests {

    @Autowired
    DataSource dataSource;

    @Test
    void contextLoads() throws SQLException {
        // 查看默认数据源
        System.out.println(dataSource.getClass());

        // 检查数据库连接
        Connection conn = dataSource.getConnection();
        System.out.println(conn);

        // 关闭
        conn.close();
    }
}

 可以看出 Spring Boot 2.2.5 默认使用HikariDataSource 数据源,而以前版本,如 Spring Boot 1.5 默认使用 org.apache.tomcat.jdbc.pool.DataSource 作为数据源;

 3、测试查询数据库

编写controller类,使用一个类:JdbcTemplate 实现CRUD等操作

package com.zhixi.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.List;

/**
 * @author zhangzhixi
 */
@Controller
public class JDBCController {
    @Autowired
    JdbcTemplate jdbcTemplate;

    /**
     * 添加用户
     */
    @ResponseBody
    @RequestMapping("/userAdd")
    public String addUser() {
        String sql = "insert into mybatis.user(id,name,pwd) values(6,'小六','666')";
        jdbcTemplate.update(sql);
        return "update ok!";
    }

    /**
     * 删除用户
     */
    @ResponseBody
    @RequestMapping("/delUser/{userId}")
    public String delUser(@PathVariable("userId") int userId) {
        String sql = "delete from mybatis.user where id = " + userId + "";
        jdbcTemplate.update(sql);
        return "delete ok!";
    }

    /**
     * 修改用户
     */
    @ResponseBody
    @RequestMapping("/updateUser/{userId}")
    public String updateUser(@PathVariable("userId") int userId) {
        String sql = "update mybatis.user set name='赵六',pwd='123456' where id=" + userId + "";
        jdbcTemplate.update(sql);
        return "update Ok";
    }

    /**
     * 查询所有用户
     */
    @ResponseBody
    @RequestMapping("/userList")

    public List getUserAll() {
        String sql = "select * from user";
        List maps = jdbcTemplate.queryForList(sql);
        return maps;
    }
}

源码:

  数据库中所有能够配置的东西:DataSourceProperties

  1 /*
  2  * Copyright 2012-2020 the original author or authors.
  3  *
  4  * Licensed under the Apache License, Version 2.0 (the "License");
  5  * you may not use this file except in compliance with the License.
  6  * You may obtain a copy of the License at
  7  *
  8  *      https://www.apache.org/licenses/LICENSE-2.0
  9  *
 10  * Unless required by applicable law or agreed to in writing, software
 11  * distributed under the License is distributed on an "AS IS" BASIS,
 12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  * See the License for the specific language governing permissions and
 14  * limitations under the License.
 15  */
 16 
 17 package org.springframework.boot.autoconfigure.jdbc;
 18 
 19 import java.nio.charset.Charset;
 20 import java.util.LinkedHashMap;
 21 import java.util.List;
 22 import java.util.Map;
 23 import java.util.UUID;
 24 
 25 import javax.sql.DataSource;
 26 
 27 import org.springframework.beans.factory.BeanClassLoaderAware;
 28 import org.springframework.beans.factory.BeanCreationException;
 29 import org.springframework.beans.factory.InitializingBean;
 30 import org.springframework.boot.context.properties.ConfigurationProperties;
 31 import org.springframework.boot.jdbc.DataSourceBuilder;
 32 import org.springframework.boot.jdbc.DataSourceInitializationMode;
 33 import org.springframework.boot.jdbc.DatabaseDriver;
 34 import org.springframework.boot.jdbc.EmbeddedDatabaseConnection;
 35 import org.springframework.util.Assert;
 36 import org.springframework.util.ClassUtils;
 37 import org.springframework.util.StringUtils;
 38 
 39 /**
 40  * Base class for configuration of a data source.
 41  *
 42  * @author Dave Syer
 43  * @author Maciej Walkowiak
 44  * @author Stephane Nicoll
 45  * @author Benedikt Ritter
 46  * @author Eddú Meléndez
 47  * @author Scott Frederick
 48  * @since 1.1.0
 49  */
 50 @ConfigurationProperties(prefix = "spring.datasource")
 51 public class DataSourceProperties implements BeanClassLoaderAware, InitializingBean {
 52 
 53     private ClassLoader classLoader;
 54 
 55     /**
 56      * Name of the datasource. Default to "testdb" when using an embedded database.
 57      */
 58     private String name;
 59 
 60     /**
 61      * Whether to generate a random datasource name.
 62      */
 63     private boolean generateUniqueName = true;
 64 
 65     /**
 66      * Fully qualified name of the connection pool implementation to use. By default, it
 67      * is auto-detected from the classpath.
 68      */
 69     private Class<? extends DataSource> type;
 70 
 71     /**
 72      * Fully qualified name of the JDBC driver. Auto-detected based on the URL by default.
 73      */
 74     private String driverClassName;
 75 
 76     /**
 77      * JDBC URL of the database.
 78      */
 79     private String url;
 80 
 81     /**
 82      * Login username of the database.
 83      */
 84     private String username;
 85 
 86     /**
 87      * Login password of the database.
 88      */
 89     private String password;
 90 
 91     /**
 92      * JNDI location of the datasource. Class, url, username and password are ignored when
 93      * set.
 94      */
 95     private String jndiName;
 96 
 97     /**
 98      * Mode to apply when determining if DataSource initialization should be performed
 99      * using the available DDL and DML scripts.
100      */
101     private DataSourceInitializationMode initializationMode = DataSourceInitializationMode.EMBEDDED;
102 
103     /**
104      * Platform to use in the DDL or DML scripts (such as schema-${platform}.sql or
105      * data-${platform}.sql).
106      */
107     private String platform = "all";
108 
109     /**
110      * Schema (DDL) script resource references.
111      */
112     private List<String> schema;
113 
114     /**
115      * Username of the database to execute DDL scripts (if different).
116      */
117     private String schemaUsername;
118 
119     /**
120      * Password of the database to execute DDL scripts (if different).
121      */
122     private String schemaPassword;
123 
124     /**
125      * Data (DML) script resource references.
126      */
127     private List<String> data;
128 
129     /**
130      * Username of the database to execute DML scripts (if different).
131      */
132     private String dataUsername;
133 
134     /**
135      * Password of the database to execute DML scripts (if different).
136      */
137     private String dataPassword;
138 
139     /**
140      * Whether to stop if an error occurs while initializing the database.
141      */
142     private boolean continueOnError = false;
143 
144     /**
145      * Statement separator in SQL initialization scripts.
146      */
147     private String separator = ";";
148 
149     /**
150      * SQL scripts encoding.
151      */
152     private Charset sqlScriptEncoding;
153 
154     private EmbeddedDatabaseConnection embeddedDatabaseConnection = EmbeddedDatabaseConnection.NONE;
155 
156     private Xa xa = new Xa();
157 
158     private String uniqueName;
159 
160     @Override
161     public void setBeanClassLoader(ClassLoader classLoader) {
162         this.classLoader = classLoader;
163     }
164 
165     @Override
166     public void afterPropertiesSet() throws Exception {
167         this.embeddedDatabaseConnection = EmbeddedDatabaseConnection.get(this.classLoader);
168     }
169 
170     /**
171      * Initialize a {@link DataSourceBuilder} with the state of this instance.
172      * @return a {@link DataSourceBuilder} initialized with the customizations defined on
173      * this instance
174      */
175     public DataSourceBuilder<?> initializeDataSourceBuilder() {
176         return DataSourceBuilder.create(getClassLoader()).type(getType()).driverClassName(determineDriverClassName())
177                 .url(determineUrl()).username(determineUsername()).password(determinePassword());
178     }
179 
180     public String getName() {
181         return this.name;
182     }
183 
184     public void setName(String name) {
185         this.name = name;
186     }
187 
188     public boolean isGenerateUniqueName() {
189         return this.generateUniqueName;
190     }
191 
192     public void setGenerateUniqueName(boolean generateUniqueName) {
193         this.generateUniqueName = generateUniqueName;
194     }
195 
196     public Class<? extends DataSource> getType() {
197         return this.type;
198     }
199 
200     public void setType(Class<? extends DataSource> type) {
201         this.type = type;
202     }
203 
204     /**
205      * Return the configured driver or {@code null} if none was configured.
206      * @return the configured driver
207      * @see #determineDriverClassName()
208      */
209     public String getDriverClassName() {
210         return this.driverClassName;
211     }
212 
213     public void setDriverClassName(String driverClassName) {
214         this.driverClassName = driverClassName;
215     }
216 
217     /**
218      * Determine the driver to use based on this configuration and the environment.
219      * @return the driver to use
220      * @since 1.4.0
221      */
222     public String determineDriverClassName() {
223         if (StringUtils.hasText(this.driverClassName)) {
224             Assert.state(driverClassIsLoadable(), () -> "Cannot load driver class: " + this.driverClassName);
225             return this.driverClassName;
226         }
227         String driverClassName = null;
228         if (StringUtils.hasText(this.url)) {
229             driverClassName = DatabaseDriver.fromJdbcUrl(this.url).getDriverClassName();
230         }
231         if (!StringUtils.hasText(driverClassName)) {
232             driverClassName = this.embeddedDatabaseConnection.getDriverClassName();
233         }
234         if (!StringUtils.hasText(driverClassName)) {
235             throw new DataSourceBeanCreationException("Failed to determine a suitable driver class", this,
236                     this.embeddedDatabaseConnection);
237         }
238         return driverClassName;
239     }
240 
241     private boolean driverClassIsLoadable() {
242         try {
243             ClassUtils.forName(this.driverClassName, null);
244             return true;
245         }
246         catch (UnsupportedClassVersionError ex) {
247             // Driver library has been compiled with a later JDK, propagate error
248             throw ex;
249         }
250         catch (Throwable ex) {
251             return false;
252         }
253     }
254 
255     /**
256      * Return the configured url or {@code null} if none was configured.
257      * @return the configured url
258      * @see #determineUrl()
259      */
260     public String getUrl() {
261         return this.url;
262     }
263 
264     public void setUrl(String url) {
265         this.url = url;
266     }
267 
268     /**
269      * Determine the url to use based on this configuration and the environment.
270      * @return the url to use
271      * @since 1.4.0
272      */
273     public String determineUrl() {
274         if (StringUtils.hasText(this.url)) {
275             return this.url;
276         }
277         String databaseName = determineDatabaseName();
278         String url = (databaseName != null) ? this.embeddedDatabaseConnection.getUrl(databaseName) : null;
279         if (!StringUtils.hasText(url)) {
280             throw new DataSourceBeanCreationException("Failed to determine suitable jdbc url", this,
281                     this.embeddedDatabaseConnection);
282         }
283         return url;
284     }
285 
286     /**
287      * Determine the name to used based on this configuration.
288      * @return the database name to use or {@code null}
289      * @since 2.0.0
290      */
291     public String determineDatabaseName() {
292         if (this.generateUniqueName) {
293             if (this.uniqueName == null) {
294                 this.uniqueName = UUID.randomUUID().toString();
295             }
296             return this.uniqueName;
297         }
298         if (StringUtils.hasLength(this.name)) {
299             return this.name;
300         }
301         if (this.embeddedDatabaseConnection != EmbeddedDatabaseConnection.NONE) {
302             return "testdb";
303         }
304         return null;
305     }
306 
307     /**
308      * Return the configured username or {@code null} if none was configured.
309      * @return the configured username
310      * @see #determineUsername()
311      */
312     public String getUsername() {
313         return this.username;
314     }
315 
316     public void setUsername(String username) {
317         this.username = username;
318     }
319 
320     /**
321      * Determine the username to use based on this configuration and the environment.
322      * @return the username to use
323      * @since 1.4.0
324      */
325     public String determineUsername() {
326         if (StringUtils.hasText(this.username)) {
327             return this.username;
328         }
329         if (EmbeddedDatabaseConnection.isEmbedded(determineDriverClassName(), determineUrl())) {
330             return "sa";
331         }
332         return null;
333     }
334 
335     /**
336      * Return the configured password or {@code null} if none was configured.
337      * @return the configured password
338      * @see #determinePassword()
339      */
340     public String getPassword() {
341         return this.password;
342     }
343 
344     public void setPassword(String password) {
345         this.password = password;
346     }
347 
348     /**
349      * Determine the password to use based on this configuration and the environment.
350      * @return the password to use
351      * @since 1.4.0
352      */
353     public String determinePassword() {
354         if (StringUtils.hasText(this.password)) {
355             return this.password;
356         }
357         if (EmbeddedDatabaseConnection.isEmbedded(determineDriverClassName(), determineUrl())) {
358             return "";
359         }
360         return null;
361     }
362 
363     public String getJndiName() {
364         return this.jndiName;
365     }
366 
367     /**
368      * Allows the DataSource to be managed by the container and obtained via JNDI. The
369      * {@code URL}, {@code driverClassName}, {@code username} and {@code password} fields
370      * will be ignored when using JNDI lookups.
371      * @param jndiName the JNDI name
372      */
373     public void setJndiName(String jndiName) {
374         this.jndiName = jndiName;
375     }
376 
377     public DataSourceInitializationMode getInitializationMode() {
378         return this.initializationMode;
379     }
380 
381     public void setInitializationMode(DataSourceInitializationMode initializationMode) {
382         this.initializationMode = initializationMode;
383     }
384 
385     public String getPlatform() {
386         return this.platform;
387     }
388 
389     public void setPlatform(String platform) {
390         this.platform = platform;
391     }
392 
393     public List<String> getSchema() {
394         return this.schema;
395     }
396 
397     public void setSchema(List<String> schema) {
398         this.schema = schema;
399     }
400 
401     public String getSchemaUsername() {
402         return this.schemaUsername;
403     }
404 
405     public void setSchemaUsername(String schemaUsername) {
406         this.schemaUsername = schemaUsername;
407     }
408 
409     public String getSchemaPassword() {
410         return this.schemaPassword;
411     }
412 
413     public void setSchemaPassword(String schemaPassword) {
414         this.schemaPassword = schemaPassword;
415     }
416 
417     public List<String> getData() {
418         return this.data;
419     }
420 
421     public void setData(List<String> data) {
422         this.data = data;
423     }
424 
425     public String getDataUsername() {
426         return this.dataUsername;
427     }
428 
429     public void setDataUsername(String dataUsername) {
430         this.dataUsername = dataUsername;
431     }
432 
433     public String getDataPassword() {
434         return this.dataPassword;
435     }
436 
437     public void setDataPassword(String dataPassword) {
438         this.dataPassword = dataPassword;
439     }
440 
441     public boolean isContinueOnError() {
442         return this.continueOnError;
443     }
444 
445     public void setContinueOnError(boolean continueOnError) {
446         this.continueOnError = continueOnError;
447     }
448 
449     public String getSeparator() {
450         return this.separator;
451     }
452 
453     public void setSeparator(String separator) {
454         this.separator = separator;
455     }
456 
457     public Charset getSqlScriptEncoding() {
458         return this.sqlScriptEncoding;
459     }
460 
461     public void setSqlScriptEncoding(Charset sqlScriptEncoding) {
462         this.sqlScriptEncoding = sqlScriptEncoding;
463     }
464 
465     public ClassLoader getClassLoader() {
466         return this.classLoader;
467     }
468 
469     public Xa getXa() {
470         return this.xa;
471     }
472 
473     public void setXa(Xa xa) {
474         this.xa = xa;
475     }
476 
477     /**
478      * XA Specific datasource settings.
479      */
480     public static class Xa {
481 
482         /**
483          * XA datasource fully qualified name.
484          */
485         private String dataSourceClassName;
486 
487         /**
488          * Properties to pass to the XA data source.
489          */
490         private Map<String, String> properties = new LinkedHashMap<>();
491 
492         public String getDataSourceClassName() {
493             return this.dataSourceClassName;
494         }
495 
496         public void setDataSourceClassName(String dataSourceClassName) {
497             this.dataSourceClassName = dataSourceClassName;
498         }
499 
500         public Map<String, String> getProperties() {
501             return this.properties;
502         }
503 
504         public void setProperties(Map<String, String> properties) {
505             this.properties = properties;
506         }
507 
508     }
509 
510     static class DataSourceBeanCreationException extends BeanCreationException {
511 
512         private final DataSourceProperties properties;
513 
514         private final EmbeddedDatabaseConnection connection;
515 
516         DataSourceBeanCreationException(String message, DataSourceProperties properties,
517                 EmbeddedDatabaseConnection connection) {
518             super(message);
519             this.properties = properties;
520             this.connection = connection;
521         }
522 
523         DataSourceProperties getProperties() {
524             return this.properties;
525         }
526 
527         EmbeddedDatabaseConnection getConnection() {
528             return this.connection;
529         }
530 
531     }
532 
533 }
View Code

  在SpringBoot中有:xxx.properties,肯定存在xxxAutoConfiguration自动配置的文件

/*
 * Copyright 2012-2020 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
 *
 *      https://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 javax.sql.DataSource;
import javax.sql.XADataSource;

import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
import org.springframework.boot.autoconfigure.condition.ConditionMessage;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
import org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadataProvidersConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.boot.jdbc.EmbeddedDatabaseConnection;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
import org.springframework.util.StringUtils;

/**
 * {@link EnableAutoConfiguration Auto-configuration} for {@link DataSource}.
 *
 * @author Dave Syer
 * @author Phillip Webb
 * @author Stephane Nicoll
 * @author Kazuki Shimizu
 * @since 1.0.0
 */
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
@ConditionalOnMissingBean(type = "io.r2dbc.spi.ConnectionFactory")
@EnableConfigurationProperties(DataSourceProperties.class)
@Import({ DataSourcePoolMetadataProvidersConfiguration.class, DataSourceInitializationConfiguration.class })
public class DataSourceAutoConfiguration {

    @Configuration(proxyBeanMethods = false)
    @Conditional(EmbeddedDatabaseCondition.class)
    @ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
    @Import(EmbeddedDataSourceConfiguration.class)
    protected static class EmbeddedDatabaseConfiguration {

    }

    @Configuration(proxyBeanMethods = false)
    @Conditional(PooledDataSourceCondition.class)
    @ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
    @Import({ DataSourceConfiguration.Hikari.class, DataSourceConfiguration.Tomcat.class,
            DataSourceConfiguration.Dbcp2.class, DataSourceConfiguration.OracleUcp.class,
            DataSourceConfiguration.Generic.class, DataSourceJmxConfiguration.class })
    protected static class PooledDataSourceConfiguration {

    }

    /**
     * {@link AnyNestedCondition} that checks that either {@code spring.datasource.type}
     * is set or {@link PooledDataSourceAvailableCondition} applies.
     */
    static class PooledDataSourceCondition extends AnyNestedCondition {

        PooledDataSourceCondition() {
            super(ConfigurationPhase.PARSE_CONFIGURATION);
        }

        @ConditionalOnProperty(prefix = "spring.datasource", name = "type")
        static class ExplicitType {

        }

        @Conditional(PooledDataSourceAvailableCondition.class)
        static class PooledDataSourceAvailable {

        }

    }

    /**
     * {@link Condition} to test if a supported connection pool is available.
     */
    static class PooledDataSourceAvailableCondition extends SpringBootCondition {

        @Override
        public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
            ConditionMessage.Builder message = ConditionMessage.forCondition("PooledDataSource");
            if (DataSourceBuilder.findType(context.getClassLoader()) != null) {
                return ConditionOutcome.match(message.foundExactly("supported DataSource"));
            }
            return ConditionOutcome.noMatch(message.didNotFind("supported DataSource").atAll());
        }

    }

    /**
     * {@link Condition} to detect when an embedded {@link DataSource} type can be used.
     * If a pooled {@link DataSource} is available, it will always be preferred to an
     * {@code EmbeddedDatabase}.
     */
    static class EmbeddedDatabaseCondition extends SpringBootCondition {

        private static final String DATASOURCE_URL_PROPERTY = "spring.datasource.url";

        private final SpringBootCondition pooledCondition = new PooledDataSourceCondition();

        @Override
        public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
            ConditionMessage.Builder message = ConditionMessage.forCondition("EmbeddedDataSource");
            if (hasDataSourceUrlProperty(context)) {
                return ConditionOutcome.noMatch(message.because(DATASOURCE_URL_PROPERTY + " is set"));
            }
            if (anyMatches(context, metadata, this.pooledCondition)) {
                return ConditionOutcome.noMatch(message.foundExactly("supported pooled data source"));
            }
            EmbeddedDatabaseType type = EmbeddedDatabaseConnection.get(context.getClassLoader()).getType();
            if (type == null) {
                return ConditionOutcome.noMatch(message.didNotFind("embedded database").atAll());
            }
            return ConditionOutcome.match(message.found("embedded database").items(type));
        }

        private boolean hasDataSourceUrlProperty(ConditionContext context) {
            Environment environment = context.getEnvironment();
            if (environment.containsProperty(DATASOURCE_URL_PROPERTY)) {
                try {
                    return StringUtils.hasText(environment.getProperty(DATASOURCE_URL_PROPERTY));
                }
                catch (IllegalArgumentException ex) {
                    // Ignore unresolvable placeholder errors
                }
            }
            return false;
        }

    }

}
View Code

二、整合Druid


1、Druid简介

  Java程序很大一部分要操作数据库,为了提高性能操作数据库的时候,又不得不使用数据库连接池。

  Druid 是阿里巴巴开源平台上一个数据库连接池实现,结合了 C3P0、DBCP 等 DB 池的优点,同时加入了日志监控。

  Druid 可以很好的监控 DB 池连接和 SQL 的执行情况,天生就是针对监控而生的 DB 连接池。

  Druid已经在阿里巴巴部署了超过600个应用,经过一年多生产环境大规模部署的严苛考验。

  Spring Boot 2.0 以上默认使用 Hikari 数据源,可以说 Hikari 与 Driud 都是当前 Java Web 上最优秀的数据源,我们来重点介绍 Spring Boot 如何集成 Druid 数据源,如何实现数据库监控。

   Github地址:https://github.com/alibaba/druid/

基本配置

配置缺省值说明
name   配置这个属性的意义在于,如果存在多个数据源,监控的时候可以通过名字来区分开来。 如果没有配置,将会生成一个名字,格式是:“DataSource-” + System.identityHashCode(this)
jdbcUrl   连接数据库的url,不同数据库不一样。例如: mysql : jdbc:mysql://10.20.153.104:3306/druid2 oracle : jdbc:oracle:thin:@10.20.149.85:1521:ocnauto
username   连接数据库的用户名
password   连接数据库的密码。如果你不希望密码直接写在配置文件中,可以使用ConfigFilter。详细看这里:https://github.com/alibaba/druid/wiki/%E4%BD%BF%E7%94%A8ConfigFilter
driverClassName 根据url自动识别 这一项可配可不配,如果不配置druid会根据url自动识别dbType,然后选择相应的driverClassName(建议配置下)
initialSize 0 初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时
maxActive 8 最大连接池数量
maxIdle 8 已经不再使用,配置了也没效果
minIdle   最小连接池数量
maxWait   获取连接时最大等待时间,单位毫秒。配置了maxWait之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置useUnfairLock属性为true使用非公平锁。
poolPreparedStatements false 是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle。在mysql下建议关闭。
maxOpenPreparedStatements -1 要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true。在Druid中,不会存在Oracle下PSCache占用内存过多的问题,可以把这个数值配置大一些,比如说100
validationQuery   用来检测连接是否有效的sql,要求是一个查询语句。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会其作用。
testOnBorrow true 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。
testOnReturn false 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能
testWhileIdle false 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。
timeBetweenEvictionRunsMillis   有两个含义: 1) Destroy线程会检测连接的间隔时间2) testWhileIdle的判断依据,详细看testWhileIdle属性的说明
numTestsPerEvictionRun   不再使用,一个DruidDataSource只支持一个EvictionRun
minEvictableIdleTimeMillis    
connectionInitSqls   物理连接初始化的时候执行的sql
exceptionSorter 根据dbType自动识别 当数据库抛出一些不可恢复的异常时,抛弃连接
filters   属性类型是字符串,通过别名的方式配置扩展插件,常用的插件有: 监控统计用的filter:stat日志用的filter:log4j防御sql注入的filter:wall
proxyFilters   类型是List<com.alibaba.druid.filter.Filter>,如果同时配置了filters和proxyFilters,是组合关系,并非替换关系

 2、Druid的使用

  • 添加druid数据源 pom.xml
<!--阿里druid数据源-->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.21</version>
</dependency>
<!--druid数据源依赖-->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.1.17</version>
</dependency>
<!--log4j日志监控-->
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>
  • 切换数据源 application.yaml
spring:
  datasource:
    username: root
    password: zhixi158
    url: jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
    driver-class-name: com.mysql.cj.jdbc.Driver

    # 更改数据源
    type: com.alibaba.druid.pool.DruidDataSource

    #druid 数据源专有配置
    druid:
      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
      
      #配置监控统计拦截的filters,stat:监控统计、log4j:日志记录、wall:防御sql注入
      #如果允许时报错  java.lang.ClassNotFoundException: org.apache.log4j.Priority
      #则导入 log4j 依赖即可,Maven 地址:https://mvnrepository.com/artifact/log4j/log4j
      filters: stat,wall,log4j
      maxPoolPreparedStatementPerConnectionSize: 20
      useGlobalDataSourceStat: true
      connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
  • 测试druid的日志监控 config/DruidConfigUraction.java
package com.zhixi.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 DruidConfiguration {

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource druidDataSource() throws SQLException {
        DruidDataSource dataSource = new DruidDataSource();
        /*开启druid的SQL监控功能*/
        dataSource.setFilters("stat");
    return dataSource;
}

    @Bean
    //后台监控功能
    //因为SpringBoot内置了Servlet容器,所以没有Web.xml,替代方法:ServletRegistrationBean
    public ServletRegistrationBean statViewServlet(){

        ServletRegistrationBean bean = new ServletRegistrationBean<>(new StatViewServlet(),"/druid/*");
        //后台需要有人登陆,账号密码配置
        HashMap<String,String> map = new HashMap<>();
        //增加配置
        map.put("loginUsername","admin");//登陆的key,是固定的,不能自己定义成其他的
        map.put("loginPassword","123456");

        //允许谁可以访问
        map.put("allow","localhost");

        bean.setInitParameters(map);//初始化参数
        return bean;
    }

    //配置 Druid 监控 之  web 监控的 filter
    //WebStatFilter:用于配置Web和Druid数据源之间的管理关联监控统计
    @Bean
    public FilterRegistrationBean webStatFilter() {
        FilterRegistrationBean bean = new FilterRegistrationBean();
        bean.setFilter(new WebStatFilter());

        //exclusions:设置哪些请求进行过滤排除掉,从而不进行统计
        Map<String, String> initParams = new HashMap<>();
        initParams.put("exclusions", "*.js,*.css,/druid/*");
        bean.setInitParameters(initParams);

        //"/*" 表示过滤所有请求
        bean.setUrlPatterns(Arrays.asList("/*"));
        return bean;
    }
}

访问:localhost:8080/druid,输入账号密码即可访问监控主页!

 三、整合mybatis


 1、整合包

  为了方便我这里就放了学习SpringBoot到现在所用的依赖,有注释方便查看

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!--lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
        </dependency>
        <!--阿里druid数据源-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.21</version>
        </dependency>
        <!--druid数据源依赖-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.17</version>
        </dependency>
        <!--log4j日志监控-->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
        <!--
            spring默认使用yml中的配置,解决
            @ConfigurationProperties(prefix = "person")爆红
        -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>

        <!--JSR303数据校验-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>

        <!--thymeleaf模板引擎-->
        <dependency>
            <groupId>org.thymeleaf</groupId>
            <artifactId>thymeleaf-spring5</artifactId>
        </dependency>
        <dependency>
            <groupId>org.thymeleaf.extras</groupId>
            <artifactId>thymeleaf-extras-java8time</artifactId>
        </dependency>
        <!--整合mybatis-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.1</version>
        </dependency>
    </dependencies>

2、实体类:popo/User.java

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private Integer id;
    private String name;
    private String pwd;
}

3、业务接口:mapper/UserMapper.java

@Component
@Mapper
public interface UserMapper {
    /**
     * 添加用户
     */
    int addUser(User user);

    /**
     * 修改用户
     */
    int updateUser(User user);

    /**
     * 删除用户
     */
    int delUser(int id);

    /**
     * 根据id查询用户
     */
    User queryUserById(@Param("userId") int id);

    /**
     * 查询全部用户
     */
    List<User> queryUserAll();

}

4、mybatis映射(CRUD):/resources/mybatis/mapper/UserMapper.xml

<?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.zhixi.mapper.UserMapper">
    <select id="queryUserAll" resultType="user">
        select *
        from mybatis.user;
    </select>

    <select id="queryUserById" resultType="user" parameterType="int">
        select *
        from mybatis.user
        where id = #{userId}
    </select>

    <delete id="delUser" parameterType="int">
        delete
        from mybatis.user
        where id = #{id}
    </delete>

    <update id="updateUser" parameterType="user">
        update mybatis.user
        set name=#{name},
            pwd=#{pwd}
        where id = #{id}
    </update>

    <insert id="addUser" parameterType="user">
        insert into mybatis.user(id, name, pwd)
        values (#{id}, #{name}, #{pwd});
    </insert>
</mapper>

5、配置文件:application.properties

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
spring.datasource.username=root
spring.datasource.password=zhixi158


# 整合mybatis
#-----------------------------
# 给实体类起别名
mybatis.type-aliases-package=com.zhixi.pojo
# mapper映射
mybatis.mapper-locations=classpath:mybatis/mapper/UserMapper.xml

6、业务接口的实现:controller/UserController

@Controller
public class UserController {

    @Autowired
    private UserMapper userMapper;

    /**
     * @return 查询全部用户
     */
    @ResponseBody
    @RequestMapping("/queryUser")
    public List<User> queryUserAll() {
        List<User> list = userMapper.queryUserAll();
        for (User user : list) {
            System.out.println(user);
        }
        return list;
    }

    /**
     * @param id
     * @return 根据id查询用户
     */
    @ResponseBody
    @RequestMapping("/queryUserById/{id}")
    public User queryUserById(@PathVariable("id") int id) {
        User user = userMapper.queryUserById(id);
        System.out.println(user);
        return user;
    }

    /**
     * @param id
     * @return 根据id删除用户
     */
    @ResponseBody
    @RequestMapping("/delUser/{id}")
    public String delUser(@PathVariable("id") int id) {
        int i = userMapper.delUser(id);
        return i >= 1 ? "删除成功" : "删除失败";
    }

    /**
     * @return 根据id修改用户
     */
    @ResponseBody
    @RequestMapping("/updateUser/{id}/{userName}/{userPwd}")
    public String updateUser(@PathVariable("id") int id,
                             @PathVariable("userName") String userName,
                             @PathVariable("userPwd") String userPwd) {
        int i = userMapper.updateUser(new User(id, userName, userPwd));
        return i >= 1 ? "修改成功" : "修改失败";
    }

    /**
     * @return 添加用户
     */
    @ResponseBody
    @RequestMapping("/addUser/{id}/{userName}/{userPwd}")
    public String addUser(@PathVariable("id") int id,
                          @PathVariable("userName") String userName,
                          @PathVariable("userPwd") String userPwd) {
        int i = userMapper.addUser(new User(id, userName, userPwd));
        return i >= 1 ? "添加用户成功" : "添加用户失败";
    }
}

 地址访问:localhost:8080/url地址即可实现对应操作

 

 

 

  

 

 
posted @ 2021-01-27 15:47  Java小白的搬砖路  阅读(172)  评论(0编辑  收藏  举报