Spring Boot数据库开发

通常SpringBoot数据库开发,会引入spring-boot-starter-jdbc,而如果引入了spring-boot-starter-jdbc,但没有可用的数据源或者没有配置,那么在运行Spring Boot时会出现异常,因为spring-boot-starter-jdbc内部会自动配置一个数据源,如果不能连接就会引发异常

假如系统用的数据源来自MySQL,但还没有配置,可以在POM里引入一个内存数据库(也可以配置成持久化存储在硬盘上),轻量级的关系型数据库管理系统,如下所示

<!-- 引入H2数据库的依赖 -->
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <scope>runtime</scope>
        </dependency>
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.

然后在启动SpringBoot,访问如下地址就能看到H2的访问页面,但实际上这个数据库并没有单独安装

互联网应用主流框架整合之Spring Boot开发_bc


在启动日志里也能看到这个数据库的启动

互联网应用主流框架整合之Spring Boot开发_java_02

也可以取消SpringBoot默认数据源的创建,如下代码所示

package com.sbdev;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.Map;

@RestController
//exclude = DataSourceAutoConfiguration.class 禁止自动配置数据源
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
public class SbDevApplication {
    @GetMapping("/test")
    public Map<String, String> test() {
        Map<String, String> map = new HashMap<>();
        map.put("success", "true");
        map.put("message", "我的第一个Spring Boot程序");
        return map;
    }
    public static void main(String[] args) {
        SpringApplication.run(SbDevApplication.class, args);
    }

}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.

@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)表示启动SpringBoot,不再使用DataSourceAutoConfiguration这个配置类,如果没有配置数据源,运行时就不再发生异常,这也变相的说明SpringBoot的配置理念,使用配置类来生成对应的Bean,而Bean的属性可以通过配置文件自定义

正常情况在开发应用的时候都会提前配置好,需要确认自己的环境,如下列情况不同的数据库版本配置对应的数据库驱动及参数

服务启动过程中经常会遇到的一个问题是无法创建连接,其中有一种原因是数据库的版本和连接驱动类的版本不匹配,如果使用的是MySQL8.0以下的版本,那在POM中添加的依赖应该是

<!-- 引入MySQL数据库连接驱动依赖 -->
	<dependency>
		<groupId>mysql</groupId>
		<artifactId>mysql-connector-java</artifactId>
		<version>5.1.29</version>
	</dependency>
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.

配置数据源的时候,driverClassName配置应该是props.setProperty("driverClassName", "com.mysql.jdbc.Driver");而如果MySQL用的是8.0以上的版本,那么POM中应该添加的依赖是

<dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>8.0.30</version> <!-- 使用实际的版本号 -->
    </dependency>
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

或者

<!-- 引入MySQL连接器,用于在运行时连接MySQL数据库 -->
    <dependency>
        <groupId>com.mysql</groupId>
        <artifactId>mysql-connector-j</artifactId>
    </dependency>
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

配置数据源的时候,driverClassName配置应该是props.setProperty("driverClassName", "com.mysql.cj.jdbc.Driver");

配置好这些之后,在SpringBoot的配置文件application.yml中,写入数据源配置,如下所示

spring.application.name:
  - SBDev

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/ssm?useUnicode=true&characterEncoding=utf-8&useSSL=false
    username: root
    password: Ms123!@#


server:
    servlet:
      context-path: /SBDev
    port: 8001
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.

这样POM中既不需要引入h2database也不需要在启动类上添加exclude = DataSourceAutoConfiguration.class,如果上述数据源配置无误且能正常链接,则启动SpringBoot的时候就可以正常启动

这里的配置最终会被读取到DataSourceAutoConfiguration中,然后再去创建数据源,看一下这个类的源码如下

/**
 * 自动配置数据源的类。根据应用程序的条件自动配置嵌入式数据库或连接池。
 * 
 * @AutoConfiguration before = {SqlInitializationAutoConfiguration.class} 指定此配置类在SqlInitializationAutoConfiguration之前加载
 * @ConditionalOnClass {DataSource.class, EmbeddedDatabaseType.class} 检查类路径中是否存在DataSource和EmbeddedDatabaseType类,以决定是否应该加载此配置
 * @ConditionalOnMissingBean type = {"io.r2dbc.spi.ConnectionFactory"} 如果应用程序中不存在ConnectionFactory bean,则加载此配置
 * @EnableConfigurationProperties {DataSourceProperties.class} 启用对DataSourceProperties属性的配置
 * @Import {DataSourcePoolMetadataProvidersConfiguration.class, DataSourceCheckpointRestoreConfiguration.class} 导入额外的配置类
 */
@AutoConfiguration(
    before = {SqlInitializationAutoConfiguration.class}
)
@ConditionalOnClass({DataSource.class, EmbeddedDatabaseType.class})
@ConditionalOnMissingBean(
    type = {"io.r2dbc.spi.ConnectionFactory"}
)
@EnableConfigurationProperties({DataSourceProperties.class})
@Import({DataSourcePoolMetadataProvidersConfiguration.class, DataSourceCheckpointRestoreConfiguration.class})
public class DataSourceAutoConfiguration {
    
    /**
     * 判断是否应该配置嵌入式数据库的条件类。
     * 它检查是否设置了spring.datasource.url属性,或者是否存在支持的连接池数据源。
     */
    static class EmbeddedDatabaseCondition extends SpringBootCondition {
        private static final String DATASOURCE_URL_PROPERTY = "spring.datasource.url";
        private final SpringBootCondition pooledCondition = new PooledDataSourceCondition();

        EmbeddedDatabaseCondition() {
        }

        /**
         * 根据当前环境和条件判断是否应该配置嵌入式数据库。
         * 
         * @param context 条件上下文,提供环境和类加载器等信息
         * @param metadata 注解类型元数据,用于获取注解参数等信息
         * @return 条件匹配的结果
         */
        public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
            ConditionMessage.Builder message = ConditionMessage.forCondition("EmbeddedDataSource", new Object[0]);
            if (this.hasDataSourceUrlProperty(context)) {
                return ConditionOutcome.noMatch(message.because("spring.datasource.url is set"));
            } else if (this.anyMatches(context, metadata, new Condition[]{this.pooledCondition})) {
                return ConditionOutcome.noMatch(message.foundExactly("supported pooled data source"));
            } else {
                EmbeddedDatabaseType type = EmbeddedDatabaseConnection.get(context.getClassLoader()).getType();
                return type == null ? ConditionOutcome.noMatch(message.didNotFind("embedded database").atAll()) : ConditionOutcome.match(message.found("embedded database").items(new Object[]{type}));
            }
        }

        /**
         * 检查是否设置了spring.datasource.url属性并且该属性有文本值。
         * 
         * @param context 条件上下文
         * @return 如果设置了有效的spring.datasource.url属性,则返回true;否则返回false
         */
        private boolean hasDataSourceUrlProperty(ConditionContext context) {
            Environment environment = context.getEnvironment();
            if (environment.containsProperty("spring.datasource.url")) {
                try {
                    return StringUtils.hasText(environment.getProperty("spring.datasource.url"));
                } catch (IllegalArgumentException var4) {
                }
            }

            return false;
        }
    }

    /**
     * 判断是否应该配置连接池数据源的条件类。
     */
    static class PooledDataSourceAvailableCondition extends SpringBootCondition {
        PooledDataSourceAvailableCondition() {
        }

        /**
         * 根据当前环境判断是否应该配置连接池数据源。
         * 
         * @param context 条件上下文
         * @param metadata 注解类型元数据
         * @return 条件匹配的结果
         */
        public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
            ConditionMessage.Builder message = ConditionMessage.forCondition("PooledDataSource", new Object[0]);
            return DataSourceBuilder.findType(context.getClassLoader()) != null ? ConditionOutcome.match(message.foundExactly("supported DataSource")) : ConditionOutcome.noMatch(message.didNotFind("supported DataSource").atAll());
        }
    }

    /**
     * 管理连接池数据源配置的条件类。
     * 根据是否存在特定的DataSource类型或属性来决定是否应用配置。
     */
    static class PooledDataSourceCondition extends AnyNestedCondition {
        PooledDataSourceCondition() {
            super(ConfigurationPhase.PARSE_CONFIGURATION);
        }

        /**
         * 当存在支持的连接池数据源类型时触发的条件。
         */
        @Conditional({PooledDataSourceAvailableCondition.class})
        static class PooledDataSourceAvailable {
            PooledDataSourceAvailable() {
            }
        }

        /**
         * 当spring.datasource.type属性被设置时触发的条件。
         */
        @ConditionalOnProperty(
            prefix = "spring.datasource",
            name = {"type"}
        )
        static class ExplicitType {
            ExplicitType() {
            }
        }
    }

    /**
     * 配置连接池数据源的配置类。
     * 在不存在DataSource和XADataSource bean且满足其他条件的情况下激活。
     */
    @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 {
        protected PooledDataSourceConfiguration() {
        }

        /**
         * 提供基于DataSourceProperties配置的JdbcConnectionDetails bean。
         * 
         * @param properties DataSource的属性
         * @return JdbcConnectionDetails的实例
         */
        @Bean
        @ConditionalOnMissingBean({JdbcConnectionDetails.class})
        PropertiesJdbcConnectionDetails jdbcConnectionDetails(DataSourceProperties properties) {
            return new PropertiesJdbcConnectionDetails(properties);
        }
    }

    /**
     * 配置嵌入式数据库的配置类。
     * 在不存在DataSource和XADataSource bean且满足其他条件的情况下激活。
     */
    @Configuration(
        proxyBeanMethods = false
    )
    @Conditional({EmbeddedDatabaseCondition.class})
    @ConditionalOnMissingBean({DataSource.class, XADataSource.class})
    @Import({EmbeddedDataSourceConfiguration.class})
    protected static class EmbeddedDatabaseConfiguration {
        protected EmbeddedDatabaseConfiguration() {
        }
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.
  • 96.
  • 97.
  • 98.
  • 99.
  • 100.
  • 101.
  • 102.
  • 103.
  • 104.
  • 105.
  • 106.
  • 107.
  • 108.
  • 109.
  • 110.
  • 111.
  • 112.
  • 113.
  • 114.
  • 115.
  • 116.
  • 117.
  • 118.
  • 119.
  • 120.
  • 121.
  • 122.
  • 123.
  • 124.
  • 125.
  • 126.
  • 127.
  • 128.
  • 129.
  • 130.
  • 131.
  • 132.
  • 133.
  • 134.
  • 135.
  • 136.
  • 137.
  • 138.
  • 139.
  • 140.
  • 141.
  • 142.
  • 143.
  • 144.
  • 145.
  • 146.
  • 147.
  • 148.
  • 149.
  • 150.
  • 151.
  • 152.
  • 153.
  • 154.
  • 155.
  • 156.
  • 157.
  • 158.
  • 159.
  • 160.
  • 161.
  • 162.

代码中不难看出加载了配置类@EnableConfigurationProperties({DataSourceProperties.class}), 这是Spring暴露的配置,在看一下这个配置类的源码如下

/**
 * 数据源属性配置类,用于封装数据源相关的配置信息。
 * 通过@ConfigurationProperties注解,绑定到配置文件中以spring.datasource为前缀的配置项。
 */
@ConfigurationProperties(prefix = "spring.datasource")
public class DataSourceProperties implements BeanClassLoaderAware, InitializingBean {
    private ClassLoader classLoader;
    private boolean generateUniqueName = true;
    private String name;
    private Class<? extends DataSource> type;
    private String driverClassName;
    private String url;
    private String username;
    private String password;
    private String jndiName;
    private EmbeddedDatabaseConnection embeddedDatabaseConnection;
    private Xa xa = new Xa();
    private String uniqueName;

    /**
     * 默认构造函数
     */
    public DataSourceProperties() {
    }

    /**
     * 实现BeanClassLoaderAware接口,设置类加载器
     * @param classLoader 类加载器
     */
    public void setBeanClassLoader(ClassLoader classLoader) {
        this.classLoader = classLoader;
    }

    /**
     * 实现InitializingBean接口,初始化数据源构建器
     * @throws Exception 初始化异常
     */
    public void afterPropertiesSet() throws Exception {
        if (this.embeddedDatabaseConnection == null) {
            this.embeddedDatabaseConnection = EmbeddedDatabaseConnection.get(this.classLoader);
        }
    }

    /**
     * 初始化DataSourceBuilder,用于构建DataSource实例。
     * @return DataSourceBuilder实例
     */
    public DataSourceBuilder<?> initializeDataSourceBuilder() {
        return DataSourceBuilder.create(this.getClassLoader()).type(this.getType()).driverClassName(this.determineDriverClassName()).url(this.determineUrl()).username(this.determineUsername()).password(this.determinePassword());
    }

    /**
     * 获取是否生成唯一名称的属性。
     * @return true表示生成唯一名称,false表示不生成
     */
    public boolean isGenerateUniqueName() {
        return this.generateUniqueName;
    }

    /**
     * 设置是否生成唯一名称的属性。
     * @param generateUniqueName true表示生成唯一名称,false表示不生成
     */
    public void setGenerateUniqueName(boolean generateUniqueName) {
        this.generateUniqueName = generateUniqueName;
    }

    /**
     * 获取数据源名称。
     * @return 数据源名称
     */
    public String getName() {
        return this.name;
    }

    /**
     * 设置数据源名称。
     * @param name 数据源名称
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取数据源类型。
     * @return 数据源类型
     */
    public Class<? extends DataSource> getType() {
        return this.type;
    }

    /**
     * 设置数据源类型。
     * @param type 数据源类型
     */
    public void setType(Class<? extends DataSource> type) {
        this.type = type;
    }

    /**
     * 获取驱动类名称。
     * @return 驱动类名称
     */
    public String getDriverClassName() {
        return this.driverClassName;
    }

    /**
     * 设置驱动类名称。
     * @param driverClassName 驱动类名称
     */
    public void setDriverClassName(String driverClassName) {
        this.driverClassName = driverClassName;
    }

    /**
     * 确定驱动类名称。
     * 如果已设置驱动类名称,则直接返回;否则根据URL确定驱动类名称。
     * @return 驱动类名称
     * @throws DataSourceBeanCreationException 如果无法确定驱动类名称,则抛出异常
     */
    public String determineDriverClassName() {
        if (StringUtils.hasText(this.driverClassName)) {
            Assert.state(this.driverClassIsLoadable(), () -> {
                return "Cannot load driver class: " + this.driverClassName;
            });
            return this.driverClassName;
        } else {
            String driverClassName = null;
            if (StringUtils.hasText(this.url)) {
                driverClassName = DatabaseDriver.fromJdbcUrl(this.url).getDriverClassName();
            }

            if (!StringUtils.hasText(driverClassName)) {
                driverClassName = this.embeddedDatabaseConnection.getDriverClassName();
            }

            if (!StringUtils.hasText(driverClassName)) {
                throw new DataSourceBeanCreationException("Failed to determine a suitable driver class", this, this.embeddedDatabaseConnection);
            } else {
                return driverClassName;
            }
        }
    }

    /**
     * 检查驱动类是否可加载。
     * @return true表示驱动类可加载,false表示不可加载
     */
    private boolean driverClassIsLoadable() {
        try {
            ClassUtils.forName(this.driverClassName, (ClassLoader)null);
            return true;
        } catch (UnsupportedClassVersionError var2) {
            throw var2;
        } catch (Throwable var3) {
            var3.printStackTrace();
            return false;
        }
    }

    /**
     * 获取URL。
     * @return JDBC URL
     */
    public String getUrl() {
        return this.url;
    }

    /**
     * 设置URL。
     * @param url JDBC URL
     */
    public void setUrl(String url) {
        this.url = url;
    }

    /**
     * 确定URL。
     * 如果已设置URL,则直接返回;否则根据数据库名称确定URL。
     * @return JDBC URL
     * @throws DataSourceBeanCreationException 如果无法确定URL,则抛出异常
     */
    public String determineUrl() {
        if (StringUtils.hasText(this.url)) {
            return this.url;
        } else {
            String databaseName = this.determineDatabaseName();
            String url = databaseName != null ? this.embeddedDatabaseConnection.getUrl(databaseName) : null;
            if (!StringUtils.hasText(url)) {
                throw new DataSourceBeanCreationException("Failed to determine suitable jdbc url", this, this.embeddedDatabaseConnection);
            } else {
                return url;
            }
        }
    }

    /**
     * 确定数据库名称。
     * 如果需要生成唯一名称,则生成并返回唯一名称;否则根据name属性返回名称。
     * @return 数据库名称
     */
    public String determineDatabaseName() {
        if (this.generateUniqueName) {
            if (this.uniqueName == null) {
                this.uniqueName = UUID.randomUUID().toString();
            }

            return this.uniqueName;
        } else if (StringUtils.hasLength(this.name)) {
            return this.name;
        } else {
            return this.embeddedDatabaseConnection != EmbeddedDatabaseConnection.NONE ? "testdb" : null;
        }
    }

    /**
     * 获取用户名。
     * @return 用户名
     */
    public String getUsername() {
        return this.username;
    }

    /**
     * 设置用户名。
     * @param username 用户名
     */
    public void setUsername(String username) {
        this.username = username;
    }

    /**
     * 确定用户名。
     * 如果已设置用户名,则直接返回;否则对于嵌入式数据库,返回"sa"。
     * @return 用户名
     */
    public String determineUsername() {
        if (StringUtils.hasText(this.username)) {
            return this.username;
        } else {
            return EmbeddedDatabaseConnection.isEmbedded(this.determineDriverClassName(), this.determineUrl()) ? "sa" : null;
        }
    }

    /**
     * 获取密码。
     * @return 密码
     */
    public String getPassword() {
        return this.password;
    }

    /**
     * 设置密码。
     * @param password 密码
     */
    public void setPassword(String password) {
        this.password = password;
    }

    /**
     * 确定密码。
     * 如果已设置密码,则直接返回;否则对于嵌入式数据库,返回空字符串。
     * @return 密码
     */
    public String determinePassword() {
        if (StringUtils.hasText(this.password)) {
            return this.password;
        } else {
            return EmbeddedDatabaseConnection.isEmbedded(this.determineDriverClassName(), this.determineUrl()) ? "" : null;
        }
    }

    /**
     * 获取JNDI名称。
     * @return JNDI名称
     */
    public String getJndiName() {
        return this.jndiName;
    }

    /**
     * 设置JNDI名称。
     * @param jndiName JNDI名称
     */
    public void setJndiName(String jndiName) {
        this.jndiName = jndiName;
    }

    /**
     * 获取嵌入式数据库连接类型。
     * @return 嵌入式数据库连接类型
     */
    public EmbeddedDatabaseConnection getEmbeddedDatabaseConnection() {
        return this.embeddedDatabaseConnection;
    }

    /**
     * 设置嵌入式数据库连接类型。
     * @param embeddedDatabaseConnection 嵌入式数据库连接类型
     */
    public void setEmbeddedDatabaseConnection(EmbeddedDatabaseConnection embeddedDatabaseConnection) {
        this.embeddedDatabaseConnection = embeddedDatabaseConnection;
    }

    /**
     * 获取类加载器。
     * @return 类加载器
     */
    public ClassLoader getClassLoader() {
        return this.classLoader;
    }

    /**
     * 获取Xa配置。
     * @return Xa配置
     */
    public Xa getXa() {
        return this.xa;
    }

    /**
     * 设置Xa配置。
     * @param xa Xa配置
     */
    public void setXa(Xa xa) {
        this.xa = xa;
    }

    /**
     * Xa配置类,用于存储XA数据源的相关配置。
     */
    public static class Xa {
        private String dataSourceClassName;
        private Map<String, String> properties = new LinkedHashMap();

        /**
         * 获取数据源类名称。
         * @return 数据源类名称
         */
        public String getDataSourceClassName() {
            return this.dataSourceClassName;
        }

        /**
         * 设置数据源类名称。
         * @param dataSourceClassName 数据源类名称
         */
        public void setDataSourceClassName(String dataSourceClassName) {
            this.dataSourceClassName = dataSourceClassName;
        }

        /**
         * 获取属性配置。
         * @return 属性配置
         */
        public Map<String, String> getProperties() {
            return this.properties;
        }

        /**
         * 设置属性配置。
         * @param properties 属性配置
         */
        public void setProperties(Map<String, String> properties) {
            this.properties = properties;
        }
    }

    /**
     * 数据源创建异常类,用于处理数据源创建过程中的异常。
     */
    static class DataSourceBeanCreationException extends BeanCreationException {
        private final DataSourceProperties properties;
        private final EmbeddedDatabaseConnection connection;

        /**
         * 构造函数。
         * @param message 异常信息
         * @param properties 数据源属性
         * @param connection 嵌入式数据库连接类型
         */
        DataSourceBeanCreationException(String message, DataSourceProperties properties, EmbeddedDatabaseConnection connection) {
            super(message);
            this.properties = properties;
            this.connection = connection;
        }

        /**
         * 获取数据源属性。
         * @return 数据源属性
         */
        DataSourceProperties getProperties() {
            return this.properties;
        }

        /**
         * 获取嵌入式数据库连接类型。
         * @return 嵌入式数据库连接类型
         */
        EmbeddedDatabaseConnection getConnection() {
            return this.connection;
        }
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.
  • 96.
  • 97.
  • 98.
  • 99.
  • 100.
  • 101.
  • 102.
  • 103.
  • 104.
  • 105.
  • 106.
  • 107.
  • 108.
  • 109.
  • 110.
  • 111.
  • 112.
  • 113.
  • 114.
  • 115.
  • 116.
  • 117.
  • 118.
  • 119.
  • 120.
  • 121.
  • 122.
  • 123.
  • 124.
  • 125.
  • 126.
  • 127.
  • 128.
  • 129.
  • 130.
  • 131.
  • 132.
  • 133.
  • 134.
  • 135.
  • 136.
  • 137.
  • 138.
  • 139.
  • 140.
  • 141.
  • 142.
  • 143.
  • 144.
  • 145.
  • 146.
  • 147.
  • 148.
  • 149.
  • 150.
  • 151.
  • 152.
  • 153.
  • 154.
  • 155.
  • 156.
  • 157.
  • 158.
  • 159.
  • 160.
  • 161.
  • 162.
  • 163.
  • 164.
  • 165.
  • 166.
  • 167.
  • 168.
  • 169.
  • 170.
  • 171.
  • 172.
  • 173.
  • 174.
  • 175.
  • 176.
  • 177.
  • 178.
  • 179.
  • 180.
  • 181.
  • 182.
  • 183.
  • 184.
  • 185.
  • 186.
  • 187.
  • 188.
  • 189.
  • 190.
  • 191.
  • 192.
  • 193.
  • 194.
  • 195.
  • 196.
  • 197.
  • 198.
  • 199.
  • 200.
  • 201.
  • 202.
  • 203.
  • 204.
  • 205.
  • 206.
  • 207.
  • 208.
  • 209.
  • 210.
  • 211.
  • 212.
  • 213.
  • 214.
  • 215.
  • 216.
  • 217.
  • 218.
  • 219.
  • 220.
  • 221.
  • 222.
  • 223.
  • 224.
  • 225.
  • 226.
  • 227.
  • 228.
  • 229.
  • 230.
  • 231.
  • 232.
  • 233.
  • 234.
  • 235.
  • 236.
  • 237.
  • 238.
  • 239.
  • 240.
  • 241.
  • 242.
  • 243.
  • 244.
  • 245.
  • 246.
  • 247.
  • 248.
  • 249.
  • 250.
  • 251.
  • 252.
  • 253.
  • 254.
  • 255.
  • 256.
  • 257.
  • 258.
  • 259.
  • 260.
  • 261.
  • 262.
  • 263.
  • 264.
  • 265.
  • 266.
  • 267.
  • 268.
  • 269.
  • 270.
  • 271.
  • 272.
  • 273.
  • 274.
  • 275.
  • 276.
  • 277.
  • 278.
  • 279.
  • 280.
  • 281.
  • 282.
  • 283.
  • 284.
  • 285.
  • 286.
  • 287.
  • 288.
  • 289.
  • 290.
  • 291.
  • 292.
  • 293.
  • 294.
  • 295.
  • 296.
  • 297.
  • 298.
  • 299.
  • 300.
  • 301.
  • 302.
  • 303.
  • 304.
  • 305.
  • 306.
  • 307.
  • 308.
  • 309.
  • 310.
  • 311.
  • 312.
  • 313.
  • 314.
  • 315.
  • 316.
  • 317.
  • 318.
  • 319.
  • 320.
  • 321.
  • 322.
  • 323.
  • 324.
  • 325.
  • 326.
  • 327.
  • 328.
  • 329.
  • 330.
  • 331.
  • 332.
  • 333.
  • 334.
  • 335.
  • 336.
  • 337.
  • 338.
  • 339.
  • 340.
  • 341.
  • 342.
  • 343.
  • 344.
  • 345.
  • 346.
  • 347.
  • 348.
  • 349.
  • 350.
  • 351.
  • 352.
  • 353.
  • 354.
  • 355.
  • 356.
  • 357.
  • 358.
  • 359.
  • 360.
  • 361.
  • 362.
  • 363.
  • 364.
  • 365.
  • 366.
  • 367.
  • 368.
  • 369.
  • 370.
  • 371.
  • 372.
  • 373.
  • 374.
  • 375.
  • 376.
  • 377.
  • 378.
  • 379.
  • 380.
  • 381.
  • 382.
  • 383.
  • 384.
  • 385.
  • 386.
  • 387.
  • 388.
  • 389.
  • 390.
  • 391.
  • 392.
  • 393.
  • 394.
  • 395.
  • 396.
  • 397.
  • 398.
  • 399.
  • 400.
  • 401.
  • 402.
  • 403.
  • 404.
  • 405.
  • 406.

代码中不难看出,要求对应的配置项以spring.datasource开头,上一段源码中有一段加入各种数据源配置的地方

@Import({DataSourceConfiguration.Hikari.class, DataSourceConfiguration.Tomcat.class, DataSourceConfiguration.Dbcp2.class, DataSourceConfiguration.OracleUcp.class, DataSourceConfiguration.Generic.class, DataSourceJmxConfiguration.class})
  • 1.

再深入看一下源码,以第一个数据源配置类为例

/**
 * 数据源配置类,用于根据不同的数据源类型创建对应的数据源实例。
 */
abstract class DataSourceConfiguration {
    
    /**
     * 根据连接详情和数据源类型创建数据源实例。
     * 
     * @param connectionDetails 连接详情,包含 JDBC URL、驱动类名、用户名和密码等信息。
     * @param type 数据源的类类型。
     * @param classLoader 类加载器。
     * @param <T> 数据源类型。
     * @return 创建的数据源实例。
     */
    private static <T> T createDataSource(JdbcConnectionDetails connectionDetails, Class<? extends DataSource> type, ClassLoader classLoader) {
        // 使用 DataSourceBuilder 创建并配置数据源实例,然后返回。
        return DataSourceBuilder.create(classLoader).type(type).driverClassName(connectionDetails.getDriverClassName()).url(connectionDetails.getJdbcUrl()).username(connectionDetails.getUsername()).password(connectionDetails.getPassword()).build();
    }

    /**
     * 配置通用的数据源 bean,当不存在自定义数据源 bean 且配置了 spring.datasource.type 时使用。
     */
    @Configuration(
        proxyBeanMethods = false
    )
    @ConditionalOnMissingBean({DataSource.class})
    @ConditionalOnProperty(
        name = {"spring.datasource.type"}
    )
    static class Generic {
        /**
         * 根据连接属性和连接详情创建并配置数据源 bean。
         * 
         * @param properties 数据源属性。
         * @param connectionDetails 连接详情。
         * @return 配置后的数据源实例。
         */
        @Bean
        DataSource dataSource(DataSourceProperties properties, JdbcConnectionDetails connectionDetails) {
            // 调用 createDataSource 方法创建并返回数据源实例。
            return (DataSource)DataSourceConfiguration.createDataSource(connectionDetails, properties.getType(), properties.getClassLoader());
        }
    }

    /**
     * 配置 Oracle UCP 数据源 bean,当类路径下存在 PoolDataSourceImpl 类且未定义 DataSource bean 时使用。
     */
    @Configuration(
        proxyBeanMethods = false
    )
    @ConditionalOnClass({PoolDataSourceImpl.class, OracleConnection.class})
    @ConditionalOnMissingBean({DataSource.class})
    @ConditionalOnProperty(
        name = {"spring.datasource.type"},
        havingValue = "oracle.ucp.jdbc.PoolDataSource",
        matchIfMissing = true
    )
    static class OracleUcp {
        /**
         * 创建并配置 Oracle UCP 数据源 bean。
         * 
         * @param properties 数据源属性。
         * @param connectionDetails 连接详情。
         * @return 配置后的 Oracle UCP 数据源实例。
         * @throws SQLException 如果配置数据源时发生错误。
         */
        @Bean
        @ConfigurationProperties(
            prefix = "spring.datasource.oracleucp"
        )
        PoolDataSourceImpl dataSource(DataSourceProperties properties, JdbcConnectionDetails connectionDetails) throws SQLException {
            PoolDataSourceImpl dataSource = (PoolDataSourceImpl)DataSourceConfiguration.createDataSource(connectionDetails, PoolDataSourceImpl.class, properties.getClassLoader());
            // 如果设置了数据源名称,则应用到 Oracle UCP 数据源。
            if (StringUtils.hasText(properties.getName())) {
                dataSource.setConnectionPoolName(properties.getName());
            }
            return dataSource;
        }
    }

    /**
     * 配置 DBCP2 数据源 bean,当类路径下存在 BasicDataSource 类且未定义 DataSource bean 时使用。
     */
    @Configuration(
        proxyBeanMethods = false
    )
    @ConditionalOnClass({BasicDataSource.class})
    @ConditionalOnMissingBean({DataSource.class})
    @ConditionalOnProperty(
        name = {"spring.datasource.type"},
        havingValue = "org.apache.commons.dbcp2.BasicDataSource",
        matchIfMissing = true
    )
    static class Dbcp2 {
        /**
         * 创建并配置 DBCP2 数据源 bean。
         * 
         * @param properties 数据源属性。
         * @param connectionDetails 连接详情。
         * @return 配置后的 DBCP2 数据源实例。
         */
        @Bean
        @ConfigurationProperties(
            prefix = "spring.datasource.dbcp2"
        )
        BasicDataSource dataSource(DataSourceProperties properties, JdbcConnectionDetails connectionDetails) {
            Class<? extends DataSource> dataSourceType = BasicDataSource.class;
            return (BasicDataSource)DataSourceConfiguration.createDataSource(connectionDetails, dataSourceType, properties.getClassLoader());
        }
    }

    /**
     * 配置 HikariCP 数据源 bean,当类路径下存在 HikariDataSource 类且未定义 DataSource bean 时使用。
     */
    @Configuration(
        proxyBeanMethods = false
    )
    @ConditionalOnClass({HikariDataSource.class})
    @ConditionalOnMissingBean({DataSource.class})
    @ConditionalOnProperty(
        name = {"spring.datasource.type"},
        havingValue = "com.zaxxer.hikari.HikariDataSource",
        matchIfMissing = true
    )
    static class Hikari {
        /**
         * 创建并配置 HikariCP 数据源 bean。
         * 
         * @param properties 数据源属性。
         * @param connectionDetails 连接详情。
         * @return 配置后的 HikariCP 数据源实例。
         */
        @Bean
        @ConfigurationProperties(
            prefix = "spring.datasource.hikari"
        )
        HikariDataSource dataSource(DataSourceProperties properties, JdbcConnectionDetails connectionDetails) {
            HikariDataSource dataSource = (HikariDataSource)DataSourceConfiguration.createDataSource(connectionDetails, HikariDataSource.class, properties.getClassLoader());
            // 如果设置了数据源名称,则应用到 HikariCP 数据源。
            if (StringUtils.hasText(properties.getName())) {
                dataSource.setPoolName(properties.getName());
            }
            return dataSource;
        }
    }

    /**
     * 配置 Tomcat 数据源 bean,当类路径下存在 org.apache.tomcat.jdbc.pool.DataSource 类且未定义 DataSource bean 时使用。
     */
    @Configuration(
        proxyBeanMethods = false
    )
    @ConditionalOnClass({org.apache.tomcat.jdbc.pool.DataSource.class})
    @ConditionalOnMissingBean({DataSource.class})
    @ConditionalOnProperty(
        name = {"spring.datasource.type"},
        havingValue = "org.apache.tomcat.jdbc.pool.DataSource",
        matchIfMissing = true
    )
    static class Tomcat {
        /**
         * 创建并配置 Tomcat 数据源 bean。
         * 
         * @param properties 数据源属性。
         * @param connectionDetails 连接详情。
         * @return 配置后的 Tomcat 数据源实例。
         */
        @Bean
        @ConfigurationProperties(
            prefix = "spring.datasource.tomcat"
        )
        org.apache.tomcat.jdbc.pool.DataSource dataSource(DataSourceProperties properties, JdbcConnectionDetails connectionDetails) {
            Class<? extends DataSource> dataSourceType = org.apache.tomcat.jdbc.pool.DataSource.class;
            org.apache.tomcat.jdbc.pool.DataSource dataSource = (org.apache.tomcat.jdbc.pool.DataSource)DataSourceConfiguration.createDataSource(connectionDetails, dataSourceType, properties.getClassLoader());
            DatabaseDriver databaseDriver = DatabaseDriver.fromJdbcUrl(connectionDetails.getJdbcUrl());
            String validationQuery = databaseDriver.getValidationQuery();
            // 如果存在验证查询语句,则启用borrow时的验证并设置验证查询语句。
            if (validationQuery != null) {
                dataSource.setTestOnBorrow(true);
                dataSource.setValidationQuery(validationQuery);
            }
            return dataSource;
        }
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.
  • 96.
  • 97.
  • 98.
  • 99.
  • 100.
  • 101.
  • 102.
  • 103.
  • 104.
  • 105.
  • 106.
  • 107.
  • 108.
  • 109.
  • 110.
  • 111.
  • 112.
  • 113.
  • 114.
  • 115.
  • 116.
  • 117.
  • 118.
  • 119.
  • 120.
  • 121.
  • 122.
  • 123.
  • 124.
  • 125.
  • 126.
  • 127.
  • 128.
  • 129.
  • 130.
  • 131.
  • 132.
  • 133.
  • 134.
  • 135.
  • 136.
  • 137.
  • 138.
  • 139.
  • 140.
  • 141.
  • 142.
  • 143.
  • 144.
  • 145.
  • 146.
  • 147.
  • 148.
  • 149.
  • 150.
  • 151.
  • 152.
  • 153.
  • 154.
  • 155.
  • 156.
  • 157.
  • 158.
  • 159.
  • 160.
  • 161.
  • 162.
  • 163.
  • 164.
  • 165.
  • 166.
  • 167.
  • 168.
  • 169.
  • 170.
  • 171.
  • 172.
  • 173.
  • 174.
  • 175.
  • 176.
  • 177.
  • 178.
  • 179.
  • 180.
  • 181.
  • 182.
  • 183.
  • 184.
  • 185.

如此Spring Boot就可以通过我们的配置,将数据源创建出来了,其他的也是同理,比如Spring MVC、Redis等等,近一步测试一下,先写一个控制器, 代码如下

package com.sbdev.controller.db;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Map;

@RestController
@RequestMapping("/role")
public class DataSourceController {

    // 注入JdbcTemplate,它由Spring Boot自动创建不需要我们干预
    @Autowired
    private JdbcTemplate jdbcTemplate = null;

    /**
     * 获取角色
     * @param id 角色编号
     * @return 角色信息
     */
    @GetMapping("/info/{id}")
    public Map<String, Object> getRole(@PathVariable("id") Long id) {
        System.out.println("DataSource类型:" + jdbcTemplate.getDataSource().getClass().getName());
        Map<String, Object> roleMap = null;
        String sql = "select id, role_name, note from t_role where id = ?";
        roleMap = jdbcTemplate.queryForMap(sql, id);
        return roleMap;
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.

然后访问地址http://localhost:8001/SBDev/role/info/2即可

互联网应用主流框架整合之Spring Boot开发_spring boot_03


Spring Boot默认使用Hikari数据源,有时候我们想切换不同的数据源,Spring Boot也给予了支持,例如另一个数据源是DBCP2,先在POM添加该依赖

<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-dbcp2 -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-dbcp2</artifactId>
            <version>2.12.0</version>
        </dependency>
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.

接着修改application.yml文件数据库的配置,如下所示

# 应用程序名称,用于标识和区分不同的Spring Boot应用
spring.application.name: 
  - SBDev

spring:
  # 数据源配置,用于连接和管理数据库
  datasource:
    # 数据库驱动类名,指定连接MySQL数据库所需的驱动类
    driver-class-name: com.mysql.cj.jdbc.Driver
    # 数据库连接URL,指定连接的数据库地址、端口、数据库名称以及连接参数
    url: jdbc:mysql://localhost:3306/ssm?useUnicode=true&characterEncoding=utf-8&useSSL=false
    # 数据库用户名,用于身份验证和授权
    username: root
    # 数据库密码,与用户名配合用于身份验证和授权
    password: Ms123!@#
    # 数据源类型,使用Apache Commons DBCP2提供的基本数据源
    type: org.apache.commons.dbcp2.BasicDataSource
    # DBCP2数据源特定配置,用于管理连接池的大小和行为
    dbcp2:
      # 最大空闲连接数,超过该数目的空闲连接将被关闭
      max-idle: 20
      # 等待连接池分配连接的最大时间,超过该时间仍未获取到连接将抛出异常
      max-wait-millis: 5000
      # 连接池允许的最大连接数,超过该数目将无法获取新的连接
      max-total: 50
      # 最小空闲连接数,连接池会维护至少这么多的空闲连接

# 服务器配置,用于设置服务器端口和上下文路径
server:
  servlet:
    # 应用程序上下文路径,用于区分不同的Spring Boot应用或服务
    context-path: /SBDev
  # 服务器端口,指定应用监听的端口号
  port: 8001
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
Spring Boot整合MyBatis

在Spring Boot中整合MyBatis和传统方式差不多,只是部分MyBatis组件可以在SpringBoot配置文件中进行配置,且常见的类都会自动初始化,不需要编写,

添加依赖
<!-- https://mvnrepository.com/artifact/org.mybatis.spring.boot/mybatis-spring-boot-starter -->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>3.0.3</version>
        </dependency>
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
建表
use ssm;

Create Table t_user(
	id int(12) not null auto_increment comment '主键',
	username varchar(255) not null comment '用户名',
	password varchar(255) not null comment '密码',
    sex int(3) not null default 0 comment '性别',
    note varchar(255)  comment '备注',
	primary key(id),
    CHECK (sex in (0,1))
);

insert into t_user(username,password,sex,note) values('admin','123456',0,'管理员');
insert into t_user(username,password,sex,note) values('user','123456',1,'普通用户');
insert into t_user(username,password,sex,note) values('user2','123456',1,'普通用户');
insert into t_user(username,password,sex,note) values('user3','123456',1,'普通用户');
insert into t_user(username,password,sex,note) values('user4','123456',1,'普通用户');
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
写POJO
package com.sbdev.dic;


public enum SexEnum {

    MALE(0, "男"),
    FEMALE(1, "女");

    private Integer id;
    private String value;
    SexEnum(Integer id, String value) {
        this.id = id;
        this.value = value;
    }

    /**
     * 获取根据编号获取性别枚举
     * @param id 编号
     * @return 枚举
     */
    public static SexEnum getSexEnum(Integer id) {
        for (SexEnum sex : SexEnum.values()) {
            if (sex.getId().equals(id)) {
                return  sex;
            }
        }
        return null;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
package com.sbdev.pojo;

import com.sbdev.dic.SexEnum;
import org.apache.ibatis.type.Alias;

import java.io.Serializable;

@Alias("user")
public class User implements Serializable {
    private static final long serialVersionUID = 2386785787854557L;
    private Long id;
    private String userName;
    private SexEnum sex;
    private String note;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public SexEnum getSex() {
        return sex;
    }

    public void setSex(SexEnum sex) {
        this.sex = sex;
    }

    public String getNote() {
        return note;
    }

    public void setNote(String note) {
        this.note = note;
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
写映射文件
<?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.learn.ssm.chapter25.dao.UserDao">
    <select id="getUser" parameterType="long" resultType="user">
        select id, user_name as userName, sex, note from t_user where id = #{id}
    </select>

    <insert id="insertUser" parameterType="user" useGeneratedKeys="true" keyProperty="id">
        insert  into t_user(user_name, sex, note) values (#{userName}, #{sex}, #{note})
    </insert>
</mapper>
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
定义DAO接口层
package com.sbdev.dao;


import com.sbdev.pojo.User;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface UserDao {

    public User getUser(Long id);

    public Integer insertUser(User user);
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
编写TypeHandler

因为用户的性别是个枚举,为了更方便的使用它,需要编写一个TypeHandler

package com.sbdev.type.handler;

import com.sbdev.dic.SexEnum;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import org.apache.ibatis.type.MappedTypes;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * 性别枚举类型处理器,用于MyBatis中将数据库中的整型数据与SexEnum枚举类型相互转换。
 * 使用@MappedJdbcTypes和@MappedTypes注解分别指定了对应的JDBC类型和Java类型。
 */
@MappedJdbcTypes(JdbcType.INTEGER)
@MappedTypes({SexEnum.class})
public class SexTypeHandler extends BaseTypeHandler<SexEnum> {

    /**
     * 设置非空参数。
     * 将枚举类型SexEnum的id值设置到PreparedStatement中,对应数据库中的整型字段。
     * @param ps      PreparedStatement对象
     * @param idx     参数索引
     * @param sex     性别枚举值
     * @param jdbcType JDBC类型
     * @throws SQLException 如果设置参数时发生错误
     */
    @Override
    public void setNonNullParameter(
            PreparedStatement ps, int idx, SexEnum sex, JdbcType jdbcType) throws SQLException {
        ps.setInt(idx, sex.getId());
    }

    /**
     * 从ResultSet中获取非空结果。
     * 根据列名获取整型值,并将其转换为SexEnum枚举类型。
     * @param rs      ResultSet对象
     * @param columnName 列名
     * @return 性别枚举值
     * @throws SQLException 如果获取结果时发生错误
     */
    @Override
    public SexEnum getNullableResult(ResultSet rs, String columnName) throws SQLException {
        Integer id = rs.getInt(columnName);
        return SexEnum.getSexEnum(id);
    }

    /**
     * 从ResultSet中获取非空结果。
     * 根据索引获取整型值,并将其转换为SexEnum枚举类型。
     * @param rs      ResultSet对象
     * @param idx     列索引
     * @return 性别枚举值
     * @throws SQLException 如果获取结果时发生错误
     */
    @Override
    public SexEnum getNullableResult(ResultSet rs, int idx) throws SQLException {
        Integer id = rs.getInt(idx);
        return SexEnum.getSexEnum(id);
    }

    /**
     * 从CallableStatement中获取非空结果。
     * 根据索引获取整型值,并将其转换为SexEnum枚举类型。
     * @param cs      CallableStatement对象
     * @param idx     列索引
     * @return 性别枚举值
     * @throws SQLException 如果获取结果时发生错误
     */
    @Override
    public SexEnum getNullableResult(CallableStatement cs, int idx) throws SQLException {
        Integer id = cs.getInt(idx);
        return SexEnum.getSexEnum(id);
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
配置MyBatis

在传统的MyBatis使用中,我们还需要XML配置文件或者使用Java代码继承配置类进行配置,在SpringBoot中,通过application.yml进行配置即可

# 应用程序名称,用于标识和区分不同的Spring Boot应用
spring.application.name: SBDev

spring:
  # 数据源配置,用于连接和管理数据库
  datasource:
    # 数据库驱动类名,指定连接MySQL数据库所需的驱动类
    driver-class-name: com.mysql.cj.jdbc.Driver
    # 数据库连接URL,指定连接的数据库地址、端口、数据库名称以及连接参数
    url: jdbc:mysql://localhost:3306/ssm?useUnicode=true&characterEncoding=utf-8&useSSL=false
    # 数据库用户名,用于身份验证和授权
    username: root
    # 数据库密码,与用户名配合用于身份验证
    password: Ms123!@#
    # 数据源类型,使用Apache Commons DBCP2提供的基本数据源
    type: org.apache.commons.dbcp2.BasicDataSource
    # DBCP2特定的配置,用于管理连接池的大小和行为
    dbcp2:
      # 最大空闲连接数,超过该数目的空闲连接将被关闭
      max-idle: 20
      # 等待连接的最大时间,超过该时间仍未获取到连接将抛出异常
      max-wait-millis: 5000
      # 连接池允许的最大连接数,超过该数目将拒绝新的连接请求
      max-total: 50


# 服务器配置,用于设置应用的访问路径和端口
server:
  # 应用程序的上下文路径,用于区分不同的服务或模块
  servlet:
    context-path: /SBDev
  # 服务器端口,指定应用监听的网络端口号
  port: 8001


# mybatis配置项
mybatis:
  # 映射文件路径
  mapper-locations: classpath:mapper/*.xml
  # TypeHandler扫描包
  type-handlers-package: com.sbdev.type.handler
  # 扫描别名
  type-aliases-package: com.sbdev.pojo

logging:
  level:
    # 日志级别
    root: DEBUG
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
开发业务层
package com.sbdev.service;

import com.sbdev.pojo.User;

public interface UserService {

    public User getUser(Long id);

    public Integer insertUser(User user);

}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
package com.sbdev.service.impl;

import com.sbdev.dao.UserDao;
import com.sbdev.pojo.User;
import com.sbdev.service.UserService;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserDao userDao  = null;

    @Override
    @Cacheable(value="redisCache", key="'redis_user_'+#id")
    public User getUser(Long id) {
        return userDao.getUser(id);
    }
    @Override
    // 事务管理器由Spring Boot自动装配,无需自己配置
    @Transactional(isolation = Isolation.READ_COMMITTED)
    public Integer insertUser(User user) {
        return userDao.insertUser(user);
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
开发控制器
package com.sbdev.controller;


import com.sbdev.dic.SexEnum;
import com.sbdev.pojo.User;
import com.sbdev.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController // REST风格网站
@RequestMapping("/user")
public class UserController {
    // 用户服务接口
    @Autowired
    private UserService userService = null;

    /**
     * 获取用户信息
     * @param id 用户编号
     * @return 用户信息
     */
    @GetMapping("/info/{id}")
    public User getUser(@PathVariable("id") Long id) {
        User user = userService.getUser(id);
        return user;
    }

    @GetMapping("/addition/{id}")
    public User insertUser(@PathVariable("id") Long id) {
        User user = new User();
        user.setSex(SexEnum.getSexEnum(id.intValue()%2));
        user.setUserName("user_name_" + id);
        user.setNote("note_" + id);
        userService.insertUser(user);
        return user;
    }

    @GetMapping("/print/{user}")
    public User print(User user) {
        return user;
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
配置SpringBoot启动
/**
 * 应用程序的入口点。
 * 使用@SpringBootApplication注解标记这个类作为Spring Boot应用程序的起点。
 * scanBasePackages属性指定了Spring应用程序上下文应该扫描的包,即在这个包及其子包下寻找组件(如控制器、服务、配置类等)。
 * 这里指定的包是"com.sbdev",意味着所有在这个包下面的组件都会被Spring Boot自动识别和管理。
 */
@SpringBootApplication(
        scanBasePackages = "com.sbdev"
)
/**
 * 配置MyBatis的Mapper扫描器。
 * 通过此注解,Spring Boot将自动扫描指定包下的所有Mapper接口,并将其注册到MyBatis的SqlSessionFactory或SqlSessionTemplate中。
 * @param basePackages 指定需要扫描的Mapper接口所在的包。Spring Boot会递归扫描指定包及其子包下的所有接口。
 * @param annotationClass 指定需要扫描的接口上应存在的注解。这里指定为@Mapper,意味着只有标注了@Mapper注解的接口才会被扫描和处理。
 * @param sqlSessionFactoryRef 指定SqlSessionFactory的Bean名称。扫描到的Mapper接口将使用这个SqlSessionFactory来创建SqlSession。
 * @param sqlSessionTemplateRef 指定SqlSessionTemplate的Bean名称。扫描到的Mapper接口将使用这个SqlSessionTemplate来执行SQL操作。
 */
@MapperScan(
        basePackages = "com.sbdev",
        annotationClass = Mapper.class,
        sqlSessionFactoryRef = "sqlSessionFactory",
        sqlSessionTemplateRef = "sqlSessionTemplate"
)

@EnableCaching
public class SbDevApplication {

    public static void main(String[] args) {
        SpringApplication.run(SbDevApplication.class, args);
    }
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.

SpringBoot启动类配置,注释写的比较清楚

注意这个配置sqlSessionFactoryRef = "sqlSessionFactory",sqlSessionTemplateRef = "sqlSessionTemplate",在这里可以删除它们,因为在项目中不存在多个SqlSessionFactorySqlSessionTemplate,但是从头到尾都没有sqlSessionFactorysqlSessionTemplate相关的配置或定义,那在这里却可以直接配置的原因便是当我们配置了数据源javax.sql.DataSource后,Spring Boot就会自动创建数据源的Bean,并且将它装配到Spring IoC容器中,而这个过程无需开发,也是Spring Boot的特色

插件
package com.sbdev.plugin;


import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;

import java.util.Properties;
/**
 * 消费时间插件,用于拦截MyBatis的Executor执行查询操作,计算查询操作的消耗时间。
 * 该插件通过实现Interceptor接口,利用MyBatis的插件机制,在查询执行前后插入计时逻辑。
 */
@Intercepts(
        @Signature(
                type = Executor.class,
                method = "query",
                args = {
                        MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class
                }))
public class ConsumptionTimePlugin implements Interceptor {

    /**
     * 对Executor的query方法进行拦截。
     * 在执行查询操作前后记录时间,以计算查询操作的耗时,并输出到控制台。
     *
     * @param invocation 查询操作的调用信息,包含执行查询所需的所有参数。
     * @return 查询操作的结果对象。
     * @throws Throwable 查询操作中可能抛出的任何异常。
     */
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        // 记录查询开始时间
        long start = System.currentTimeMillis();
        // 执行查询操作
        Object returnObj = invocation.proceed();
        // 记录查询结束时间
        long end = System.currentTimeMillis();
        // 计算并输出查询耗时
        System.out.println("耗时【" + (end - start)+"】毫秒");
        return returnObj;
    }

    /**
     * 为指定的目标对象创建一个插件代理。
     * 该方法用于实现插件的包装逻辑,将当前插件应用于目标对象,以拦截目标对象的方法调用。
     *
     * @param target 被插件化的对象,即MyBatis的Executor实例。
     * @return 包装了当前插件的目标对象的代理实例。
     */
    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    /**
     * 设置插件的属性。
     * 该方法用于接收从配置文件中读取的插件属性,当前插件未使用任何属性,因此该方法为空实现。
     *
     * @param properties 插件的属性配置。
     */
    @Override
    public void setProperties(Properties properties) {
        // 该插件不使用任何属性,因此该方法为空实现
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.

这是一个MyBatis插件,可以用来监控执行查询SQL消耗的时间,因为插件相对独立,可以将其配置到MyBatis自身的配置文件中,在resources目录下创建mybatis-config.xml,来配置插件

<?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">
<!-- 配置文件的根元素,用于定义MyBatis的全局属性、类型别名、映射器等 -->
<configuration>
    <!-- 插件配置,用于注册自定义的插件 -->
    <plugins>
        <!-- 注册一个插件,该插件实现了com.sbdev.plugin.ConsumptionTimePlugin接口 -->
        <!-- 该插件用于记录方法的执行时间,实现性能监控 -->
        <plugin interceptor="com.sbdev.plugin.ConsumptionTimePlugin"/>
    </plugins>
</configuration>
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.

将MyBatis配置文件加到SpringBoot的配置里,修改application.yml如下

# 应用程序名称,用于标识和区分不同的Spring Boot应用
spring.application.name: SBDev

spring:
  # 数据源配置,用于连接和管理数据库
  datasource:
    # 数据库驱动类名,指定连接MySQL数据库所需的驱动类
    driver-class-name: com.mysql.cj.jdbc.Driver
    # 数据库连接URL,指定连接的数据库地址、端口、数据库名称以及连接参数
    url: jdbc:mysql://localhost:3306/ssm?useUnicode=true&characterEncoding=utf-8&useSSL=false
    # 数据库用户名,用于身份验证和授权
    username: root
    # 数据库密码,与用户名配合用于身份验证
    password: Ms123!@#
    # 数据源类型,使用Apache Commons DBCP2提供的基本数据源
    type: org.apache.commons.dbcp2.BasicDataSource
    # DBCP2特定的配置,用于管理连接池的大小和行为
    dbcp2:
      # 最大空闲连接数,超过该数目的空闲连接将被关闭
      max-idle: 20
      # 等待连接的最大时间,超过该时间仍未获取到连接将抛出异常
      max-wait-millis: 5000
      # 连接池允许的最大连接数,超过该数目将拒绝新的连接请求
      max-total: 50


# 服务器配置,用于设置应用的访问路径和端口
server:
  # 应用程序的上下文路径,用于区分不同的服务或模块
  servlet:
    context-path: /SBDev
  # 服务器端口,指定应用监听的网络端口号
  port: 8001


# mybatis配置项
mybatis:
  # 映射文件路径
  mapper-locations: classpath:mapper/*.xml
  # TypeHandler扫描包
  type-handlers-package: com.sbdev.type.handler
  # 扫描别名
  type-aliases-package: com.sbdev.pojo
  # 配置MyBatis配置文件的位置
  # 使用classpath指定配置文件位于类路径下,便于资源的统一管理和访问
  config-location: classpath:mybatis-config.xml


logging:
  level:
    # 日志级别
    root: DEBUG
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.

实际上还有很多配置项,可以自行研究,这样配置完,Spring Boot就会启用这个插件了

互联网应用主流框架整合之Spring Boot开发_bc_04

互联网应用主流框架整合之Spring Boot开发_spring boot_05

数据库事务

在Spring Boot初始化数据源的时候,会同时初始化对应的数据库事务管理器,因此不需要配置任何数据库事务的内容可以直接使用,例如如下代码

package com.sbdev.service.impl;

import com.sbdev.dao.UserDao;
import com.sbdev.pojo.User;
import com.sbdev.service.UserService;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserDao userDao  = null;

    @Override
    @Cacheable(value="redisCache", key="'redis_user_'+#id")
    public User getUser(Long id) {
        return userDao.getUser(id);
    }

    @Override
    // 事务管理器由Spring Boot自动装配,无需自己配置
    @Transactional(isolation = Isolation.READ_COMMITTED)
    public Integer insertUser(User user) {
        return userDao.insertUser(user);
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.

为了方便使用Spring Boot还提供了默认隔离级别的配置,如下所示

spring:
  # 数据源配置,用于连接和管理数据库
  datasource:
    # 配置Hikari连接池的事务隔离级别为读未提交
    # 这一设置影响使用Hikari连接池的数据库连接的默认事务隔离行为
    hikari:
      transaction-isolation: 2
    # -1:默认隔离级别
    # 1:读未提交
    # 2:读已提交
    # 4:可重复读
    # 8:串行化
    # 配置Tomcat连接池的默认事务隔离级别为读未提交
    # 此设置适用于所有通过Tomcat连接池获取的数据库连接
    tomcat:
      default-transaction-isolation: 2

    # 数据库驱动类名,指定连接MySQL数据库所需的驱动类
    driver-class-name: com.mysql.cj.jdbc.Driver
    # 数据库连接URL,指定连接的数据库地址、端口、数据库名称以及连接参数
    url: jdbc:mysql://localhost:3306/ssm?useUnicode=true&characterEncoding=utf-8&useSSL=false
    # 数据库用户名,用于身份验证和授权
    username: root
    # 数据库密码,与用户名配合用于身份验证
    password: Ms123!@#
    # 数据源类型,使用Apache Commons DBCP2提供的基本数据源
    type: org.apache.commons.dbcp2.BasicDataSource
    # DBCP2特定的配置,用于管理连接池的大小和行为
    dbcp2:
      # 最大空闲连接数,超过该数目的空闲连接将被关闭
      max-idle: 20
      # 等待连接的最大时间,超过该时间仍未获取到连接将抛出异常
      max-wait-millis: 5000
      # 连接池允许的最大连接数,超过该数目将拒绝新的连接请求
      max-total: 50
      # 设置默认事务隔离级别为二级隔离
      default-transaction-isolation: 2
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.

Spring Boot和Spring MVC

在大部分情况下,在Spring Boot中使用Spring MVC的方法和传统Spring MVC并无太大的不同,但又一些特殊用法

使用WebMvcConfigurer接口

通常我们可以通过实现WebMvcConfiguration接口来自定义Spring MVC的组件,例如需要开发一个用户拦截器,如下代码所示

/**
 * 用户拦截器类,用于在请求处理的不同阶段执行自定义逻辑。
 * 实现了Spring MVC的HandlerInterceptor接口。
 */
package com.sbdev.interceptor;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

public class UserInterceptor implements HandlerInterceptor {

    /**
     * 在请求处理之前执行的逻辑。
     * 该方法用于进行权限检查或初始化一些资源。
     * @param request  当前请求的HttpServletRequest对象
     * @param response 当前请求的HttpServletResponse对象
     * @param handler  将要处理请求的目标对象
     * @return true表示继续处理请求,false表示中断请求处理
     * @throws Exception 如果在预处理阶段发生异常
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle");
        return true;
    }

    /**
     * 在请求处理完成后,但在视图渲染之前执行的逻辑。
     * 该方法可用于进行一些数据处理或清理工作。
     * @param request  当前请求的HttpServletRequest对象
     * @param response 当前请求的HttpServletResponse对象
     * @param handler  处理请求的目标对象
     * @param modelAndView 视图模型对象,可能为null,表示不返回视图
     * @throws Exception 如果在后处理阶段发生异常
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle");
    }

    /**
     * 在整个请求处理完成后,包括视图渲染,执行的逻辑。
     * 该方法可用于进行一些资源的释放或日志记录等清理工作。
     * @param request  当前请求的HttpServletRequest对象
     * @param response 当前请求的HttpServletResponse对象
     * @param handler  处理请求的目标对象
     * @param ex       在请求处理过程中抛出的异常,可能为null
     * @throws Exception 如果在完成处理阶段发生异常
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion");
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
package com.sbdev.config;

import com.sbdev.interceptor.UserInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * Web配置类,用于自定义Spring MVC的配置。
 */
@Configuration
public class WebConfig implements WebMvcConfigurer {

    /**
     * 添加拦截器配置。
     * @param registry 拦截器注册表,用于注册和管理拦截器。
     *                 这里通过添加UserInterceptor拦截器并指定拦截路径为/user/**,
     *                 实现对用户相关请求的拦截处理。
     */
    public void addInterceptors(InterceptorRegistry registry) {
        // 添加UserInterceptor拦截器
        registry.addInterceptor(new UserInterceptor())
                // 指定拦截器拦截的URL路径
                .addPathPatterns("/user/**");
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.

这样就可以配置各类Spring MVC组件了

使用SpringBoot的Spring MVC配置
# 应用程序名称,用于标识和区分不同的Spring Boot应用
spring.application.name: SBDev

spring:
  # 数据源配置,用于连接和管理数据库
  datasource:
    # 配置Hikari连接池的事务隔离级别为读未提交
    # 这一设置影响使用Hikari连接池的数据库连接的默认事务隔离行为
    hikari:
      transaction-isolation: 2
    # -1:默认隔离级别
    # 1:读未提交
    # 2:读已提交
    # 4:可重复读
    # 8:串行化
    # 配置Tomcat连接池的默认事务隔离级别为读未提交
    # 此设置适用于所有通过Tomcat连接池获取的数据库连接
    tomcat:
      default-transaction-isolation: 2

    # 数据库驱动类名,指定连接MySQL数据库所需的驱动类
    driver-class-name: com.mysql.cj.jdbc.Driver
    # 数据库连接URL,指定连接的数据库地址、端口、数据库名称以及连接参数
    url: jdbc:mysql://localhost:3306/ssm?useUnicode=true&characterEncoding=utf-8&useSSL=false
    # 数据库用户名,用于身份验证和授权
    username: root
    # 数据库密码,与用户名配合用于身份验证
    password: Ms123!@#
    # 数据源类型,使用Apache Commons DBCP2提供的基本数据源
    type: org.apache.commons.dbcp2.BasicDataSource
    # DBCP2特定的配置,用于管理连接池的大小和行为
    dbcp2:
      # 最大空闲连接数,超过该数目的空闲连接将被关闭
      max-idle: 20
      # 等待连接的最大时间,超过该时间仍未获取到连接将抛出异常
      max-wait-millis: 5000
      # 连接池允许的最大连接数,超过该数目将拒绝新的连接请求
      max-total: 50
      # 设置默认事务隔离级别为二级隔离
      default-transaction-isolation: 2

  # 配置 MVC 框架的相关设置
  mvc:
    # 配置用户服务的 Servlet 信息
    servlet:
      # 定义 Servlet 的访问路径
      path: /user
      # 设置 Servlet 在应用启动时加载的顺序
      load-on-startup: 1
    # 配置视图解析的相关设置
    view:
      # 定义视图文件的后缀名
      suffix: .jsp
      # 定义视图文件的目录前缀
      prefix: /WEB-INF/jsp/

  # 配置 Web 应用的国际化设置
  web:
    # 设置固定的地域解析器
    locale-resolver: fixed
    # 指定应用的默认地域为中文(中国)
    locale: zh_CN

  # 配置 Jackson 的相关设置,用于序列化和反序列化日期时间
  jackson:
    # 定义日期时间的格式化样式
    date-format: yyyy-MM-dd HH:mm:ss




# 服务器配置,用于设置应用的访问路径和端口
server:
  # 应用程序的上下文路径,用于区分不同的服务或模块
  servlet:
    context-path: /SBDev
  # 服务器端口,指定应用监听的网络端口号
  port: 8001


# mybatis配置项
mybatis:
  # 映射文件路径
  mapper-locations: classpath:mapper/*.xml
  # TypeHandler扫描包
  type-handlers-package: com.sbdev.type.handler
  # 扫描别名
  type-aliases-package: com.sbdev.pojo
  # 配置MyBatis配置文件的位置
  # 使用classpath指定配置文件位于类路径下,便于资源的统一管理和访问
  config-location: classpath:mybatis-config.xml



logging:
  level:
    # 日志级别
    root: DEBUG
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.
  • 96.
  • 97.
  • 98.

在IDEA里双击Shift键,搜MybatisProperties或者搜WebMvcProperties能看到如下内容,都是Spring Boot配置文件中的配置项

互联网应用主流框架整合之Spring Boot开发_spring boot_06


互联网应用主流框架整合之Spring Boot开发_spring boot_07

使用转换器

定义转换器,例如用户POJO,可以约定浏览器提交的格式为{id}-{userName}-{sex}-{note}可以使用Converter机制处理

package com.sbdev.comverter;

import com.sbdev.dic.SexEnum;
import com.sbdev.pojo.User;
import org.springframework.core.convert.converter.Converter;
import org.springframework.stereotype.Component;

@Component
public class UserConverter implements Converter<String, User> {


    /**
     * 约定提交格式“{id}-{userName}-{sex}-{note}”
     * @param source 源字符串
     * @return 用户对象
     */
    @Override
    public User convert(String source) {
        if (source == null) {
            return null;
        }
        String []arr = source.split("-");
        if (arr.length != 4) { // 不符合格式
            return null;
        }
        User user = new User();
        user.setId(Long.parseLong(arr[0]));
        user.setUserName(arr[1]);
        user.setSex(SexEnum.getSexEnum(Integer.parseInt(arr[2])));
        user.setNote(arr[3]);
        return user;
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.

这里标注了@Component,这意味着它将被扫描装配到Spring IoC容器中,然后可以通过控制器来进行测试

@GetMapping("/print/{user}")
    public User print(User user) {
        return user;
    }
  • 1.
  • 2.
  • 3.
  • 4.

然后在浏览器中输入输入地址user/print/1-username-0-note测试转换器是否生效即可,这里我们并没有给Spring MVC注册UserConverter,但是Spring MVC还是使用了UserConverter去转换参数,这事因为Spring Boot已经为我们做了注册,但前提是需要将它们装配到Spring IoC容器中去

在Spring MVC的配置类WebMvcAutoConfiguration中调用了ApplicationConversionService的静态addBeans方法,源码如下

/**
     * 将豆类工厂中的转换器、格式化器、解析器注册到格式化器注册表中。
     * 此方法旨在支持自动注册由Spring Bean工厂提供的各种转换器和格式化器,以增强系统的数据处理能力。
     * @param registry 格式化器注册表,用于接收和管理转换器、格式化器、解析器。
     * @param beanFactory Spring的Bean工厂,用于获取所有实现了转换器、格式化器、解析器接口的Bean实例。
     */
    public static void addBeans(FormatterRegistry registry, ListableBeanFactory beanFactory) {
        // 使用LinkedHashSet来保存转换器、格式化器、解析器实例,以保持注册顺序。
        Set<Object> beans = new LinkedHashSet<>();
        // 分别获取并合并GenericConverter、Converter、Printer、Parser类型的Bean实例。
        beans.addAll(beanFactory.getBeansOfType(GenericConverter.class).values());
        beans.addAll(beanFactory.getBeansOfType(Converter.class).values());
        beans.addAll(beanFactory.getBeansOfType(Printer.class).values());
        beans.addAll(beanFactory.getBeansOfType(Parser.class).values());
        // 遍历所有获取到的Bean实例。
        Iterator var3 = beans.iterator();
        while(var3.hasNext()) {
            Object bean = var3.next();
            // 根据实例类型,分别注册到对应的格式化器注册表中。
            // 这里使用了Java的实例类型检查来决定注册哪种类型的实例。
            if (bean instanceof GenericConverter genericConverter) {
                registry.addConverter(genericConverter);
            } else if (bean instanceof Converter<?, ?> converter) {
                registry.addConverter(converter);
            } else if (bean instanceof Formatter<?> formatter) {
                registry.addFormatter(formatter);
            } else if (bean instanceof Printer<?> printer) {
                registry.addPrinter(printer);
            } else if (bean instanceof Parser<?> parser) {
                registry.addParser(parser);
            }
        }
    }
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.

SpringBoot会将所有在Spring IoC容器中的Converter、GenericConverter、Formatter都注册到Spring MVC的转换机制中,所以即使我们没有显示的注册,Spring MVC也能发现我们自定义的转换器

Spring Boot 使用Redis

使用Redis需要添加相关依赖

<!-- 添加Tomcat-JDBC依赖 -->
        <dependency>
            <groupId>org.apache.tomcat</groupId>
            <artifactId>tomcat-jdbc</artifactId>
        </dependency>

        <!-- 引入Spring Boot的Redis支持,但排除Lettuce客户端 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <exclusions>
                <!--不依赖Redis的异步客户端lettuce-->
                <exclusion>
                    <groupId>io.lettuce</groupId>
                    <artifactId>lettuce-core</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <!-- 引入Jedis作为Redis的同步客户端 -->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
        </dependency>
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.

Spring Boot默认依赖Lettuce客户端,但这里排除了它,因为Lettuce是一个可伸缩线程安全的Redis客户端,多个线程可以共享一个Redis连接,所以会牺牲一部分性能,但一般来说缓存对线程安全的需求并不高,更注重性能,而Jedis是一种多线程非安全的客户端,性能更好

配置Redis
data:
    redis:
      host: 127.0.0.1
      port: 6379
    jedis:
      pool:
        # 最大连接数
        max-active: 20
        # 最大等待时间
        max-wait: 2000ms
        # 最大空闲连接数
        max-idle: 10
        # 最小空闲等待连接数
        min-idle: 5
    #    # 哨兵模式配置
    #    sentinel:
    #      # 主服务器
    #      master: 192.168.80.130:26379
    #      # 节点
    #      nodes: 192.168.80.131:26379, 192.168.80.132:26379
    #    # 集群配置
    #    cluster:
    #      # 集群节点
    #      nodes: 192.168.80.133:7001, 192.168.80.133:7002, 192.168.80.133:7003, 192.168.80.133:7004, 192.168.80.133:7005, 192.168.80.133:7006
    #      # 单一连接最大转向次数
    #      max-redirects: 10
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.

在Spring Boot中自动配置类为RedisAutoConfiguration属性类为RedisProperties,更多的配置可以在这里边找到配置好后便可以使用Redis了

package com.sbdev.controller;


import com.sbdev.dic.SexEnum;
import com.sbdev.pojo.User;
import com.sbdev.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.Map;

@RestController
@RequestMapping("/redis")
public class RedisController {
    // StringRedisTemplate是RedisTemplate的子类,但是该属性名“redisTemplate”和Bean名称一致,
    // 因此可以使用@Autowired注入,如果属性名称不为“redisTemplate”,则注入失败,抛出异常
    @Autowired
//    private RedisTemplate<Object, Object> redisTemplate = null;
    private RedisTemplate<String, Object> redisTemplate = null;

    // 注入StringRedisTemplate
    @Autowired
    private StringRedisTemplate stringRedisTemplate = null;

    // 测试字符串
    @GetMapping("/string/{key}/{value}")
    public Map<String, String> string(@PathVariable("key") String key,@PathVariable("value") String value) {
        stringRedisTemplate.opsForValue().set(key, value);
        Map<String, String> result = new HashMap<>();
        result.put(key, value);
        return result;
    }

    // 测试对象
    @GetMapping("/object/{id}")
    public User object(@PathVariable("id") Long id){
        User user = new User();
        user.setId(id);
        user.setUserName("user_name" + id);
        user.setNote("note_" + id);
        user.setSex(SexEnum.getSexEnum(id.intValue() % 2));
        redisTemplate.opsForValue().set("user_" + id, user);
        return user;
    }

    @Autowired
    private UserService userService = null;

    @GetMapping("/user/{id}")
    public User getUser(@PathVariable("id") Long id) {
        return userService.getUser(id);
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.

然后根据控制器中的地址请求即可完成测试即可

Redis缓存管理器

首先在Spring Boot的配置文件中添加缓存配置

data:
    redis:
      host: 127.0.0.1
      port: 6379
    jedis:
      pool:
        # 最大连接数
        max-active: 20
        # 最大等待时间
        max-wait: 2000ms
        # 最大空闲连接数
        max-idle: 10
        # 最小空闲等待连接数
        min-idle: 5
    #    # 哨兵模式配置
    #    sentinel:
    #      # 主服务器
    #      master: 192.168.80.130:26379
    #      # 节点
    #      nodes: 192.168.80.131:26379, 192.168.80.132:26379
    #    # 集群配置
    #    cluster:
    #      # 集群节点
    #      nodes: 192.168.80.133:7001, 192.168.80.133:7002, 192.168.80.133:7003, 192.168.80.133:7004, 192.168.80.133:7005, 192.168.80.133:7006
    #      # 单一连接最大转向次数
    #      max-redirects: 10
    # 缓存管理器配置
    cache:
      # 缓存管理器名称
      cache-names: redisCache
      # 缓存管理器类型,可不配,Spring Boot会自动发现
      # type: redis
      redis:
        # 十分钟超时时间,配置为0 则永不超时
        time-to-live: 600000ms
        # 缓存空值
        cache-null-values: true
        # 缓存key前缀
        key-prefix: 'sbdev::'
        # key是否使用前缀
        use-key-prefix: true
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.

然后通过缓存注解的方式来使用Redis

package com.sbdev.service.impl;

import com.sbdev.dao.UserDao;
import com.sbdev.pojo.User;
import com.sbdev.service.UserService;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserDao userDao  = null;

    /**
     * 从Redis缓存中获取用户信息。
     * 使用了@Cacheable注解,使得该方法的结果可以被缓存。如果缓存中已存在对应键的用户信息,
     * 则直接从缓存中返回,避免了再次查询数据库。缓存的键由'redis_user_'和用户ID拼接而成,
     * 保证了键的唯一性。
     * @param id 用户的ID,作为查询和缓存键的一部分。
     * @return 用户对象,包含用户信息。
     */
    @Override
    @Cacheable(value = "redisCache", key = "'redis_user_'+#id")
    public User getUser(Long id) {
        // 通过UserDao查询Redis中的用户信息。
        return userDao.getUser(id);
    }

    @Override
    // 事务管理器由Spring Boot自动装配,无需自己配置
    @Transactional(isolation = Isolation.READ_COMMITTED)
    public Integer insertUser(User user) {
        return userDao.insertUser(user);
    }

}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.

然后通过控制器对应的方法进行测试

@Autowired
    private UserService userService = null;

    @GetMapping("/user/{id}")
    public User getUser(@PathVariable("id") Long id) {
        return userService.getUser(id);
    }
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.