vlambda博客
学习文章列表

读书笔记《developing-java-applications-with-spring-and-spring-boot-ebook》Spring Boot入门

Chapter 20. Getting Started with Spring Boot

Spring Boot 有很多已经属于 Spring Boot 家族的启动器。本章将为您概述http://start.spring.io/ ,可用的入门模块,还将向您展示如何使项目 Bootiful,正如 Josh Long 喜欢这样称呼它。

在本章中,我们将学习以下主题:

  • Using a Spring Boot template and starter
  • Creating a simple application
  • Launching an application using Gradle
  • Using the command-line runners
  • Setting up a database connection
  • Setting up a data repository service
  • Scheduling executors

Introduction


在当今软件开发的快节奏世界中,应用程序的创建速度和对快速原型的需求变得越来越重要。如果您正在使用 JVM 语言开发软件,那么 Spring Boot 正是这种框架,它将为您提供强大的功能和灵活性,使您能够快速生产高质量的软件。那么,让我们来看看 Spring Boot 如何帮助您使您的应用程序 Bootiful。

Using a Spring Boot template and starter


Spring Boot 带有超过 40 个不同的 入门模块,它们提供 适用于许多不同框架的即用型集成库,例如关系型和 NoSQL 的数据库连接、Web 服务、社交网络集成、监控库、日志记录、模板渲染和清单还在继续。虽然涵盖这些组件中的每一个实际上并不可行,但我们将介绍重要和流行的组件,以了解 Spring Boot 为我们提供的应用程序开发的可能性和易用性。

How to do it...

我们将从创建一个基本的简单项目框架开始,Spring Boot 将帮助我们实现这一点:

  1. Head over to http://start.spring.io
  2. Fill out a simple form with the details about our project
  3. Click on Generate Project alt + a premade project skeleton will download; this is where we begin

How it works...

您将看到 Project Dependencies 部分,我们可以在其中选择应用程序将执行的功能类型:它会连接到数据库吗?会有网页界面吗?我们是否计划与任何社交网络集成以提供运营支持?等等。通过选择所需的技术,适当的启动库将自动添加到我们预生成的项目模板的依赖项列表中。

在继续生成项目之前,让我们先了解一下 Spring Boot 启动器到底是什么以及它为我们提供的好处。

Spring Boot 旨在使轻松开始创建 一个应用程序。 Spring Boot 启动器是引导库,其中包含启动特定功能所需的所有相关传递依赖项的集合。每个 starter 都有一个特殊的文件,其中包含 Spring 提供的所有提供的依赖项的列表。让我们以 spring-boot-starter-test 定义为例来看看以下链接:

https://github.com/spring-projects/spring-boot/blob/master/spring-boot-project/spring-boot- starters/spring-boot-starter-test/src/main/resources/META-INF/spring.provides

在这里,我们将看到以下代码:

provides: spring-test, spring-boot, junit, mockito, hamcrest-library, jsonassert, json-path 

这告诉我们,通过在构建中包含 spring-boot-starter-test 作为依赖项,我们将自动获得 spring-test, spring-boot, junit, mockito, hamcrest-libraryjsonassertjson-path。这些库将为我们提供所有必要的东西,以便开始为我们将开发的软件编写应用程序测试,而无需手动将这些依赖项单独添加到构建文件中。

提供了 100 多个启动器,并且随着社区不断增加,列表越来越多,除非我们发现自己需要与一个相当常见或流行的框架集成,否则我们很可能已经有一个启动器可以利用。

下表向您展示了最值得注意的一些,以便您了解可用的内容:

初学者

说明

spring-boot-starter

这是为您提供所有基础功能的核心 Spring Boot 启动器。所有其他启动器都依赖它,因此无需显式声明它。

spring-boot-starter-actuator

此启动器为您提供监视、管理应用程序和审计的功能。

spring-boot-starter-jdbc

此启动器为您提供连接和使用 JDBC 数据库、连接池等的支持。

spring-boot-starter-data-jpa

spring-boot-starter-data-*

JPA starter 为您提供 所需的库,因此您可以使用Java Persistence API JPA): Hibernate 等。

各种 data-* family 启动器提供对许多数据存储的支持,例如 MongoDB、Data REST 或 Solr。

spring-boot-starter-security

这带来了 Spring Security 所需的所有依赖项。

spring-boot-starter-social-*

这使您可以与 Facebook、Twitter 和 LinkedIn 集成。

spring-boot-starter-test

这是一个包含 spring-test 和各种测试框架的依赖项的启动器:JUnit 和 Mockito 等。

spring-boot-starter-web

这为您提供了 Web 应用程序开发所需的所有依赖项。它可以通过 spring-boot-starter-hateoas, spring-boot-starter-websocket, spring-boot-starter-mobile,或 spring-boot-starter-ws,以及各种模板渲染启动器: sping-boot-starter-thymeleafspring-boot-starter-mustache

spring-cloud-starter-*

各种 cloud-* 系列启动器为许多框架提供支持,例如 Netflix OSS、Consul 或 AWS。

Creating a simple application


现在我们对可用的 starters 有了基本的了解,让我们继续创建我们的 应用程序 模板位于 http://start.spring.io

How to do it...

我们要创建的application是一个图书目录management 系统。它将记录已出版的书籍、作者是谁、审稿人、出版社等。我们将项目命名为 BookPub,并应用以下步骤:

  1. First let's switch to the full version by clicking the link below the Generate Project alt + button
  2. Choose Gradle Project at the top
  3. Use Spring Boot version 2.0.0(SNAPSHOT)
  4. Use the default proposed Group name: com.example
  5. Enter bookpub for an Artifact field
  6. Provide BookPub as a Name for the application
  7. Specify com.example.bookpub as our Package Name
  8. Select Jar as Packaging
  9. Use Java Version as 8
  10. Select the H2, JDBC, and JPA starters from the Search for dependencies selection so that we can get the needed artifacts in our build file to connect to an H2 database
  11. Click on Generate Project alt + to download the project archive

How it works...

点击 Generate Project alt + 按钮将下载 bookpub.zip 存档,我们将从我们的工作目录中提取。在新创建的 bookpub 目录中,我们将看到一个定义我们构建的 build.gradle 文件。它已经预配置了正确版本的 Spring Boot 插件和库,甚至包括我们选择的额外启动器。以下是 build.gradle 文件的代码:

dependencies { 
  compile("org.springframework.boot:spring-boot-starter-data-jpa") 
  compile("org.springframework.boot:spring-boot-starter-jdbc") 
  runtime("com.h2database:h2") 
  testCompile("org.springframework.boot:spring-boot-starter-test")  
} 

我们选择了以下首发:

  • org.springframework.boot:spring-boot-starter-data-jpa: This starter pulls in the JPA dependency.
  • org.springframework.boot:spring-boot-starter-jdbc: This starter pulls in the JDBC supporting libraries.
  • com.h2database: H2 is a particular type of database implementation, namely H2.
  • org.springframework.boot:spring-boot-starter-test: This starter pulls all the necessary dependencies for running tests. It is only being used during the test phase of the build, and it is not included during the regular application compile time and runtime. 

如您所见, runtime("com.h2database:h2") 依赖项是运行时依赖项。这是因为我们并不真正需要,甚至可能不想知道数据库的确切类型我们将在编译时连接。当 application<一个 id="id325626006" class="indexterm"> 被启动。我们将在本章稍后部分研究这种情况发生的方式和地点的内部工作原理。

data-jpajdbc 是 Spring Boot 启动器工件。如果我们在下载这些依赖 JAR 或使用 Maven Central 后查看它们,我们会发现它们不包含任何实际的类,只包含各种元数据。感兴趣的两个包含文件是 pom.xmlspring.provides。我们先看一下spring-boot-starter-jdbc JAR工件中的spring.provides文件,如下:

provides: spring-jdbc,spring-tx,tomcat-jdbc 

这告诉我们,通过将此启动器作为我们的依赖项,我们将传递得到 spring-jdbcspring-tx、和我们构建中的 tomcat-jdbc 依赖库。 pom.xml 文件包含正确的依赖声明,Gradle 或 Maven 将使用这些声明在构建期间解析所需的依赖关系。这也适用于我们的第二个启动器:spring-boot-starter-data-jpa。这个启动器将传递给我们spring-ormhibernate-entity-manager和 spring-data-jpa 库。

此时,我们的应用程序类路径中有足够的库/类,以便让 Spring Boot 了解我们正在尝试运行什么样的应用程序以及 Spring Boot 需要自动配置哪些类型的设施和框架来拼接东西一起。

之前,我们提到类路径中存在 org.h2.Driver 类会触发 Spring Boot 为我们的应用程序自动配置 H2 数据库连接。要确切了解这将如何发生,让我们首先查看我们新创建的 application 模板,特别是在 BookPubApplication.java,位于项目根目录下的 src/main/java/com/example/bookpub 目录下。我们这样做如下:

    package com.example.bookpub; 
 
    import org.springframework.boot.SpringApplication; 
    import org.springframework.boot.autoconfigure.
    SpringBootApplication; 
 
    @SpringBootApplication 
    public class BookPubApplication { 
 
      public static void main(String[] args) { 
        SpringApplication.run(BookPubApplication.class, args); 
      } 
    } 

这实际上是我们完整且完全可运行的应用程序。这里没有很多代码,也绝对没有提到任何地方的配置或数据库。制作魔法的关键是 @SpringBootApplication 元注释。在这里,我们将找到指导 Spring Boot 自动设置的真正注释:

    @SpringBootConfiguration 
    @EnableAutoConfiguration 
    @ComponentScan (excludeFilters = @Filter(type =  
                                     FilterType.CUSTOM,  
                    classes = TypeExcludeFilter.class)) 
    public @interface SpringBootApplication {...} 

让我们看一下前面代码片段中提到的注释列表:

  • @SpringBootConfiguration: This annotation is in itself a meta-annotation; it tells Spring Boot that the annotated class contains Spring Boot configuration definitions, such as the @Bean, @Component, and @Service declarations, and so on. Inside, it uses the @Configuration annotation, which is a Spring annotation, and not just Spring Boot, as it is a Spring Framework core annotation, used to mark classes containing Spring configuration definitions.

Note

需要注意的是,在使用 Spring Boot Test 框架执行测试时,使用 @SpringBootConfiguration 而不是 @Configuration 很有帮助,因为此配置当使用 @SpringBootTest 注释测试时,测试框架会自动加载。正如 Javadoc 中所指出的,一个应用程序应该只包含一个 @SpringApplicationConfiguration,大多数惯用的 Spring Boot 应用程序将从 @SpringBootApplication 继承它

  • @ComponentScan: This annotation tells Spring that we want to scan our application packages starting from the package of our annotated class as a default package root for the other classes that may be annotated with @Configuration, @Controller, and other applicable annotations, which Spring will automatically include as part of the context configuration. The applied TypeExcludeFilter class provides filtering out for various classes to be excluded from ApplicationContext. It is mostly used by spring-boot-test to exclude classes that should be used only during tests; however, it is possible to add your own beans that extend from TypeExcludeFilter and provide filtering for other types that are deemed necessary.
  • @EnableAutoConfiguration: This annotation is a part of the Spring Boot annotation, which is a meta-annotation on its own (you will find that Spring libraries rely very heavily on the meta-annotations so they can group and compose configurations together). It imports the EnableAutoConfigurationImportSelector and AutoConfigurationPackages.Registrar classes that effectively instruct Spring to automatically configure the conditional beans depending on the classes available in the classpath. (We will cover the inner workings of autoconfiguration in detail in Chapter 23, Writing Custom Spring Boot Starters.)

SpringApplication.run(BookPubApplication.class, args); main 方法中的代码行基本上创建了一个 Spring 应用程序上下文,该上下文读取 BookPubApplication.class 并实例化一个上下文,这类似于如果我们不使用 Spring Boot 并坚持使用常规 Spring Framework 的情况。

Launching an application using Gradle


通常,创建任何应用程序 的第一步是拥有一个basic 可启动骨架。由于 Spring Boot 启动器已经为我们创建了应用程序模板,我们所要做的就是提取代码、构建并执行它。现在让我们转到控制台并使用 Gradle 启动应用程序。

How to do it...

将我们的目录的位置更改为 bookpub.zip 存档的提取位置,然后从命令行执行以下命令:

$ ./gradlew clean bootRun

Note

如果目录中没有 gradlew,请从 https://gradle.org/downloads 或通过 Homebrew 通过执行 brew install gradle 安装它。 Gradle安装完成后,运行gradle文件夹下的wrapper,得到Gradle wrapper 文件生成。另一种方法是调用 $gradleclean bootRun

上述命令的输出如下:

...  .   ____          _            __ _ _ / / ___'_ __ _ _(_)_ __  __ _    ( ( )___ | '_ | '_| | '_ / _` |     /  ___)| |_)| | | | | || (_| |  ) ) ) )  '  |____| .__|_| |_|_| |___, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot ::  (v2.0.0.BUILD-SNAPSHOT)2017-12-16 23:18:53.721 : Starting BookPubApplication on mbp with  
    PID 43850 2017-12-16 23:18:53.781 : Refreshing org.springframework.context.
    annotation.Annotatio2017-12-16 23:18:55.544 : Building JPA container 
    EntityManagerFactory for persistence 2017-12-16 23:18:55.565 : HHH000204: Processing 
    PersistenceUnitInfo [name: default  2017-12-16 23:18:55.624 : HHH000412: Hibernate Core  
    {5.2.12.Final}2017-12-16 23:18:55.625 : HHH000206: hibernate.properties not 
    found2017-12-16 23:18:55.627 : HHH000021: Bytecode provider name : 
    javassist2017-12-16 23:18:55.774 : HCANN000001: Hibernate Commons 
    Annotations {5.0.1.Final2017-12-16 23:18:55.850 : HHH000400: Using dialect: 
    org.hibernate.dialect.H2Dialect2017-12-16 23:18:55.902 : HHH000397: Using 
    ASTQueryTranslatorFactory2017-12-16 23:18:56.094 : HHH000227: Running hbm2ddl schema 
    export2017-12-16 23:18:56.096 : HHH000230: Schema export complete2017-12-16 23:18:56.337 : Registering beans for JMX exposure on 
    startup2017-12-16 23:18:56.345 : Started BookPubApplication in 3.024 
    seconds (JVM running...2017-12-16 23:18:56.346 : Closing 
    org.springframework.context.annotation.AnnotationC..2017-12-16 23:18:56.347 : Unregistering JMX-exposed beans on 
    shutdown2017-12-16 23:18:56.349 : Closing JPA EntityManagerFactory for 
    persistence unit 'def...2017-12-16 23:18:56.349 : HHH000227: Running hbm2ddl schema 
    export2017-12-16 23:18:56.350 : HHH000230: Schema export completeBUILD SUCCESSFULTotal time: 52.323 secs

How it works...

正如我们所见,应用程序启动得很好,但由于我们没有添加任何功能或配置任何服务,它立即存在。然而,从启动日志中,我们确实看到自动配置确实发生了。让我们看一下以下几行:

Building JPA container EntityManagerFactory for persistence unit 
    'default'HHH000412: Hibernate Core {5.2.12.Final}HHH000400: Using dialect: org.hibernate.dialect.H2Dialect

这些信息告诉我们,因为我们添加了 jdbcdata-jpa 启动器,所以创建了 JPA 容器并将使用 Hibernate 5.2.12.Final 使用 H2Dialect 管理持久性。这是可能的,因为我们在类路径中有正确的类。

Using the command-line runners


随着我们的 basic 应用程序骨架准备就绪,让我们通过让我们的应用程序做一些事情来为骨骼添加一些肉。

让我们首先创建一个名为 StartupRunner 的类。这将实现 CommandLineRunner 接口,该接口基本上只提供一种方法: public void run(String... args) --在应用程序启动后,Spring Boot 只会调用一次。

How to do it...

  1. Create the file named StartupRunner.java under the src/main/java/com/example/bookpub/ directory from the root of our project with the following content:
        package com.example.bookpub; 

        import com.example.bookpub.repository.BookRepository;
        import org.apache.commons.logging.Log; 
        import org.apache.commons.logging.LogFactory; 
        import org.springframework.beans.factory.annotation.Autowired;
        import org.springframework.boot.CommandLineRunner; 
        import org.springframework.scheduling.annotation.Scheduled;

        public class StartupRunner implements CommandLineRunner { 
            protected final Log logger = LogFactory.getLog(getClass()); 
            @Override 
            public void run(String... args) throws Exception { 
                logger.info("Hello"); 
            } 
        }
  1. After we have defined the class, let's proceed by defining it as @Bean in the BookPubApplication.java application configuration, which is located in the same folder as our newly created StartupRunner.java file as follows:
@Bean 
public StartupRunner schedulerRunner() { 
    return new StartupRunner(); 
} 

How it works...

如果我们再次运行我们的 application,通过执行 $ ./gradlew clean bootRun,我们会得到一个和上一个类似的输出。但是,我们也会在日志中看到我们的 Hello 消息,如下所示:

2017-12-16 21:57:51.048  INFO --- 
com.example.bookpub.StartupRunner         : Hello

即使程序会在执行时终止,至少我们让它做点什么!

命令行运行器是一种有用的功能,可以在启动后执行只需运行一次的各种类型的代码。有些人也将这里作为启动各种执行器线程的地方,但 Spring Boot 为这个任务提供了更好的解决方案,本章最后会讨论。 Spring Boot 使用命令行运行器接口来扫描其所有实现,并使用启动参数调用每个实例的 run 方法。我们还可以使用 @Order 注释或实现 Ordered 接口,以定义我们想要 Spring Boot 的确切顺序执行它们。例如,Spring Batch 依赖于运行器来触发作业的执行。

由于命令行运行程序在应用程序启动后被实例化和执行,我们可以利用依赖注入来连接我们需要的任何依赖项,例如数据源、服务和其他组件。这些可以在以后实现 run 时使用。

Note

需要注意的是,如果在 run(String... args) 方法中抛出任何异常,这将导致上下文关闭并且应用程序关闭。建议使用 try/catch 包装有风险的代码块,以防止这种情况发生。

Setting up a database connection


在每个应用程序中,都需要访问 一些数据并对其进行一些操作。最常见的是,这种数据源是某种数据存储,即数据库。 Spring Boot 使得连接到数据库并开始通过 JPA 等使用数据变得非常容易。

Getting ready

在我们之前的示例中,我们创建了一个基本应用程序,它将通过在日志中打印一条消息来执行命令行运行器。让我们通过添加到数据库的连接来增强这个应用程序

早些时候,我们已经在 jdbcdata-jpa 启动器以及 H2 数据库依赖项"literal">构建 文件。现在我们将配置 H2 数据库的内存中 instance

Note

对于嵌入式数据库,例如 H2,Hyper SQL Database (HSQLDB) 或 Derby,除了在 build 文件中包含对其中之一的依赖外,不需要实际配置。当在类路径中检测到这些数据库之一并且在代码中声明了 DataSource bean 依赖项时,Spring Boot 将自动为您创建一个。

为了证明仅仅通过在类路径中包含 H2 依赖,我们将自动获得一个默认数据库,让我们修改我们的 StartupRunner.java 文件如下所示:

public class StartupRunner implements CommandLineRunner { 
    protected final Log logger = LogFactory.getLog(getClass()); 
    @Autowired 
    private DataSource ds; 
    @Override 
    public void run(String... args) throws Exception { 
        logger.info("DataSource: "+ds.toString()); 
    } 
} 

现在,如果我们继续运行我们的应用程序,我们将在日志中看到数据源的名称,如下所示:

2017-12-16 21:46:22.067 com.example.bookpub.StartupRunner   
:DataSource: org.apache.tomcat.jdbc.pool.DataSource@4...  {...driverClassName=org.h2.Driver; ... }

因此,在后台,Spring Boot 认识到我们已经自动装配了一个 DataSource bean 依赖项,并自动创建了一个初始化内存中 H2 数据存储的依赖项。这一切都很好,但在早期的原型设计阶段或测试目的之外可能不太有用。谁会想要一个数据库,一旦您的应用程序关闭,所有数据就会消失,而您每次重新启动应用程序时都必须从头开始?

How to do it...

让我们更改默认值,以便创建一个不会将数据存储在内存中的嵌入式 H2 数据库,而是使用文件将数据保存在 application 通过执行以下步骤重新启动:

  1. Open the file named application.properties under the src/main/resources directory from the root of our project and add the following content:
spring.datasource.url = jdbc:h2:~/test;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE 
spring.datasource.username = sa 
spring.datasource.password = 
  1. Start the application by executing ./gradlew clean bootRun from the command line
  2. Check your home directory, and you should see the following file in there: test.mv.db

Note

用户主目录在 Linux 上位于 /home/ 下,在 Linux 上位于 /Users/ 下macOS X。

How it works...

尽管默认情况下,Spring Boot 通过检查类路径是否存在受支持的数据库驱动程序来对数据库配置做出某些假设,但它为您提供了简单的配置选项,通过一组在 spring.datasource

我们可以配置的东西有 url用户名密码driver-class-name 等等。如果您想从外部容器创建它的 JNDI 位置使用数据源,您可以使用 spring.datasource.jndi-name 属性进行配置。可能的属性的完整集合相当大,因此我们不会对所有属性进行深入研究。但是,我们将在 第 24 章中介绍更多选项,应用程序测试,我们将在这里讨论使用数据库模拟数据以进行应用程序测试。

Note

通过查看各种博客和示例,您可能会注意到有些地方在属性名称中使用破折号,例如 driver-class-name,而其他地方则使用驼峰式变体: driverClassName。在 Spring Boot 中,这实际上是两种同样支持的命名相同属性的方式,并且它们在内部被翻译成相同的东西。

如果您想 connect 到常规(非嵌入式)数据库,除了在类路径中有适当的驱动程序库之外,我们需要在配置中指定我们选择的驱动程序。以下代码片段是连接到 MySQL 的配置:

spring.datasource.driver-class-name: com.mysql.jdbc.Driverspring.datasource.url:   
    jdbc:mysql://localhost:3306/springbootcookbookspring.datasource.username: rootspring.datasource.password:

如果我们希望 Hibernate 根据我们的实体类自动创建模式,我们需要将以下行添加到配置中:

spring.jpa.hibernate.ddl-auto=create-drop

Note

不要在生产环境中这样做,否则在启动时,所有的表模式和数据都会被删除!在需要时使用更新或验证值。

您可以在抽象层更进一步,而不是自动装配 DataSource 对象,您可以直接使用  JdbcTemplate。这将指示 Spring Boot 自动创建一个 DataSource,然后创建一个包装数据源的 JdbcTemplate,从而为您提供一种更方便的方式以安全的方式与数据库进行交互。 JdbcTemplate 的代码如下:

@Autowired 
private JdbcTemplate jdbcTemplate; 

您还可以在 org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration 文件中查看 spring-boot-autoconfigure 源代码查看数据源创建魔法背后的代码。

Setting up a data repository service


连接到数据库然后执行 好的旧SQL,虽然简单明了,但不是最方便的操作方法数据,将其映射到一组域对象中,并操纵关系内容。这就是为什么出现了多个框架来帮助您将数据从表映射到对象的原因,更广为人知的是 对象-关系映射ORM)。这种框架中最著名的示例是Hibernate。

在前面的示例中,我们介绍了如何建立与数据库的连接并配置用户名和密码的设置,还讨论了要使用的驱动程序等等。在这个秘籍中,我们将通过添加一些实体对象来增强我们的应用程序,这些实体对象定义了数据库中的数据结构和一个 CrudRepository 接口来访问数据。

由于我们的应用程序是一个图书跟踪目录,因此显而易见的领域对象是 BookAuthor审阅者Publisher

How to do it...

  1. Create a new package folder named entity under the src/main/java/com/example/bookpub directory from the root of our project.
  2. In this newly created package, create a new class named Book with the following content:
@Entity 
public class Book { 
  @Id 
  @GeneratedValue 
  private Long id; 
  private String isbn; 
  private String title; 
  private String description; 
 
  @ManyToOne 
  private Author author; 
  @ManyToOne 
  private Publisher publisher; 
 
  @ManyToMany 
  private List<Reviewers> reviewers; 
 
  protected Book() {} 
 
  public Book(String isbn, String title, Author author, 
       Publisher publisher) { 
    this.isbn = isbn; 
    this.title = title; 
    this.author = author; 
    this.publisher = publisher; 
  } 
  //Skipping getters and setters to save space, but we do need them 
} 
  1. As any book should have an author and a publisher, and ideally some reviewers, we need to create these entity objects as well. Let's start by creating an Author entity class, under the same directory as our Book, as follows:
@Entity 
public class Author { 
  @Id 
  @GeneratedValue 
  private Long id; 
  private String firstName; 
  private String lastName; 
  @OneToMany(mappedBy = "author") 
  private List<Book> books; 
 
  protected Author() {} 
 
  public Author(String firstName, String lastName) {...} 
    //Skipping implementation to save space, but we do need 
       it all 
} 
  1. Similarly, we will create the Publisher and Reviewer classes, as shown in the following code:
@Entity 
public class Publisher { 
  @Id 
  @GeneratedValue 
  private Long id; 
  private String name; 
  @OneToMany(mappedBy = "publisher") 
  private List<Book> books; 
 
  protected Publisher() {} 
 
  public Publisher(String name) {...} 
} 
 
@Entity 
public class Reviewer { 
  @Id 
  @GeneratedValue 
  private Long id; 
  private String firstName; 
  private String lastName; 
 
  protected Reviewer() {} 
 
  public Reviewer(String firstName, String lastName) 
     {...}
} 
  1. Now we will create our BookRepository interface by extending Spring's CrudRepository interface under the src/main/java/com/example/bookpub/repository package, as follows:
@Repository 
public interface BookRepository 
       extends CrudRepository<Book, Long> { 
   public Book findBookByIsbn(String isbn); 
} 
  1. Finally, let's modify our StartupRunner class in order to print the number of books in our collection, instead of some random datasource string, by autowiring a newly created BookRepository and printing the result of a .count() call to the log, as follows:
public class StartupRunner implements CommandLineRunner { 
  @Autowired private BookRepository bookRepository; 
 
  public void run(String... args) throws Exception { 
    logger.info("Number of books: " + 
       bookRepository.count()); 
  } 
} 

How it works...

您可能已经注意到,我们没有编写任何 SQL,甚至没有提及任何关于数据库连接、构建查询或类似的事情。关于我们在代码中处理数据库支持的数据这一事实的唯一提示是存在类和字段注释:@Entity, @Repository@Id@GeneratedValue@ManyToOne< /code>,以及 @ManyToMany@OneToMany。这些注释是 JPA 的一部分,以及 CrudRepository 接口的扩展,是我们与 Spring 就需要将我们的对象映射到适当的表进行通信的方式和数据库中的字段,并为我们提供与这些数据交互的编程能力。

让我们看一下以下注释:

  • @Entity indicates that the annotated class should be mapped to a database table. The name of the table will be derived from the name of the class, but it can be configured, if needed. It is important to note that every entity class should have a default protected constructor, which is needed for automated instantiation and Hibernate interactions.
  • @Repository indicates that the interface is intended to provide you with the access and manipulation of data for a database. It also serves as an indication to Spring during the component scan that this instance should be created as a bean that will be available for use and injection into other beans in the application.
  • The CrudRepository interface defines the basic common methods to read, create, update, and delete data from a data repository. The extra methods that we will define in our BookRepository extension, public Book findBookByIsbn(String isbn), indicate that Spring JPA should map the call to this method to a SQL query selecting a book by its ISBN field. This is a convention-named mapping that translates the method name into a SQL query. It can be a very powerful ally, allowing you to build queries, such as findByNameIgnoringCase(String name) and others.
  • The @Id and @GeneratedValue annotations provide you with an indication that an annotated field should be mapped to a primary key column in the database and that the value for this field should be generated, instead of being explicitly entered.
  • The @ManyToOne and @ManyToMany annotations define the relational field associations that refer to the data stored in the other tables. In our case, multiple books belong to one author, and many reviewers review multiple books.
  • The mappedBy attribute in the @OneToMay annotation defines a reverse association mapping. It indicates to Hibernate that the mapping source of truth is defined in the Book class, in the author or publisher fields.

Note

有关 Spring Data 的所有巨大功能的更多信息,请访问http://docs.spring.io/spring-data/data-commons/docs/current/reference/html/

Scheduling executors


在本章前面,我们讨论了如何命令行运行器可以用作启动调度执行器线程池的地方间隔运行工作线程。虽然这当然是可能的,但 Spring 为您提供了更简洁的配置来实现相同的目标:@EnableScheduling

Getting ready

我们将增强我们的应用程序,使其每 10 秒打印一次我们存储库中的书籍计数。为此,我们将对 BookPubApplicationStartupRunner 类进行必要的修改。

How to do it...

  1. Let's add an @EnableScheduling annotation to the BookPubApplication class, as follows:
@SpringBootApplication 
@EnableScheduling 
public class BookPubApplication {...}
  1. As a @Scheduled annotation can be placed only on methods without arguments, let's add a new run() method to the StartupRunner class and annotate it with the @Scheduled annotation, as shown in the following line:
@Scheduled(initialDelay = 1000, fixedRate = 10000) 
public void run() { 
    logger.info("Number of books: " +  
        bookRepository.count()); 
} 
  1. Start the application by executing ./gradlew clean bootRun from the command line so as to observe the Number of books: 0 message that shows in the logs every 10 seconds.

How it works...

@EnableScheduling,正如我们在本书中讨论过和将要讨论的许多其他注解一样,不是 Spring Boot;它是一个 Spring Context 模块注解。类似于 @SpringBootApplication@EnableAutoConfiguration 注解,这是一个元注解并在内部导入  SchedulingConfiguration 通过 @Import(SchedulingConfiguration.class) 指令,可以在ScheduledAnnotationBeanPostProcessor 将由导入的配置创建,并将扫描声明的 Spring bean 是否存在 @Scheduled 注释。对于每个没有参数的注释方法,将创建适当的执行程序线程池。它将管理带注释的方法的计划调用。