ZetCode

Spring Boot @Repository

最后修改于 2023 年 8 月 2 日

Spring Boot @Repository 教程展示了如何在 Spring 应用程序中使用 @Repository 注解。

Spring 是一个流行的 Java 应用程序框架,而 Spring Boot 是 Spring 的一个演进,它有助于轻松创建独立的、生产级的基于 Spring 的应用程序。

@Repository

@Repository 是一个 Spring 注解,用于指示被修饰的类是一个存储库。存储库是一种封装存储、检索和搜索行为的机制,它模拟了一个对象集合。它是 @Component 注解的特化,允许通过类路径扫描自动检测实现类。

@ComponentScan 确保使用 @Component 及其衍生类(包括 @Repository)修饰的类被找到并注册为 Spring Bean。@ComponentScan 自动包含在 @SpringBootApplication 中。

Spring Boot @Repository 示例

以下应用程序演示了 @Repository 的用法。它向用户显示一个 HTML 表格中的国家/地区列表。

build.gradle
...
src
├── main
│   ├── java
│   │   └── com
│   │       └── zetcode
│   │           ├── Application.java
│   │           ├── controller
│   │           │   └── MyController.java
│   │           ├── model
│   │           │   └── Country.java
│   │           ├── repository
│   │           │   └── CountryRepository.java
│   │           └── service
│   │               ├── CountryService.java
│   │               └── ICountryService.java
│   └── resources
│       ├── application.yml
│       ├── import.sql
│       ├── static
│       │   └── index.html
│       └── templates
│           └── showCountries.ftlh
└── test
    ├── java
    └── resources

这是项目结构。

build.gradle
plugins {
    id 'org.springframework.boot' version '3.1.1'
    id 'io.spring.dependency-management' version '1.1.0'
    id 'java'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '17'

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.boot:spring-boot-starter-freemarker'
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    runtimeOnly 'com.h2database:h2'
}

这是 Gradle 构建文件。 h2 依赖项包括 H2 数据库驱动程序。

Spring Boot starters 是一组方便的依赖项描述符,它们大大简化了配置。spring-boot-starter-web 启用 Web 应用程序,包括经典应用程序和 RESTFul 应用程序。 spring-boot-starter-web-freemarker 是一个用于使用 Freemarker 模板引擎构建 Web 应用程序的 starter。它使用 Tomcat 作为默认的嵌入式容器。spring-boot-starter-data-jpa 是一个用于将 Spring Data JPA 与 Hibernate 一起使用的 starter。

resources/application.yml
server:
  port: 8086
  servlet:
    context-path: /SpringBootRepository
  
spring:
  main:
    banner-mode: "off"
  jpa:
    database: h2
    hibernate:
      dialect: org.hibernate.dialect.H2Dialect
      ddl-auto: create-drop

application.yml 文件中,我们编写了 Spring Boot 应用程序的各种配置设置。port 设置服务器端口,context-path 设置上下文路径(应用程序名称)。在这些设置之后,我们通过 localhost:8086/SpringBootRepository/ 访问应用程序。使用 banner-mode 属性,我们关闭 Spring banner。

JPA 的 database 值指定要操作的目标数据库。我们指定了 Hibernate 方言,在本例中为 org.hibernate.dialect.H2Dialectddl-auto 是数据定义语言模式;create-drop 选项自动创建和删除数据库模式。H2 数据库在内存中运行。

resources/import.sql
INSERT INTO countries(name, population) VALUES('China', 1382050000);
INSERT INTO countries(name, population) VALUES('India', 1313210000);
INSERT INTO countries(name, population) VALUES('USA', 324666000);
INSERT INTO countries(name, population) VALUES('Indonesia', 260581000);
INSERT INTO countries(name, population) VALUES('Brazil', 207221000);
INSERT INTO countries(name, population) VALUES('Pakistan', 196626000);
INSERT INTO countries(name, population) VALUES('Nigeria', 186988000);
INSERT INTO countries(name, population) VALUES('Bangladesh', 162099000);
INSERT INTO countries(name, population) VALUES('Nigeria', 186988000);
INSERT INTO countries(name, population) VALUES('Russia', 146838000);
INSERT INTO countries(name, population) VALUES('Japan', 126830000);
INSERT INTO countries(name, population) VALUES('Mexico', 122273000);
INSERT INTO countries(name, population) VALUES('Philippines', 103738000);
INSERT INTO countries(name, population) VALUES('Ethiopia', 101853000);
INSERT INTO countries(name, population) VALUES('Vietnam', 92700000);
INSERT INTO countries(name, population) VALUES('Egypt', 92641000);
INSERT INTO countries(name, population) VALUES('Germany', 82800000);
INSERT INTO countries(name, population) VALUES('the Congo', 82243000);
INSERT INTO countries(name, population) VALUES('Iran', 82800000);
INSERT INTO countries(name, population) VALUES('Turkey', 79814000);
INSERT INTO countries(name, population) VALUES('Thailand', 68147000);
INSERT INTO countries(name, population) VALUES('France', 66984000);
INSERT INTO countries(name, population) VALUES('United Kingdom', 60589000);
INSERT INTO countries(name, population) VALUES('South Africa', 55908000);
INSERT INTO countries(name, population) VALUES('Myanmar', 51446000);
INSERT INTO countries(name, population) VALUES('South Korea', 68147000);
INSERT INTO countries(name, population) VALUES('Colombia', 49129000);
INSERT INTO countries(name, population) VALUES('Kenya', 47251000);
INSERT INTO countries(name, population) VALUES('Spain', 46812000);
INSERT INTO countries(name, population) VALUES('Argentina', 43850000);
INSERT INTO countries(name, population) VALUES('Ukraine', 42603000);
INSERT INTO countries(name, population) VALUES('Sudan', 41176000);
INSERT INTO countries(name, population) VALUES('Algeria', 40400000);
INSERT INTO countries(name, population) VALUES('Poland', 38439000);

模式由 Hibernate 自动创建;之后,执行 import.sql 文件以填充表数据。

com/zetcode/model/Country.java
package com.zetcode.model;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import java.util.Objects;

@Entity
@Table(name = "countries")
public class Country {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;
    private int population;

    public Country() {
    }

    public Country(Long id, String name, int population) {
        this.id = id;
        this.name = name;
        this.population = population;
    }

    public Long getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getPopulation() {
        return population;
    }

    public void setPopulation(int population) {
        this.population = population;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Country country = (Country) o;
        return population == country.population &&
                Objects.equals(id, country.id) &&
                Objects.equals(name, country.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, name, population);
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder("Country{");
        sb.append("id=").append(id);
        sb.append(", name='").append(name).append('\'');
        sb.append(", population=").append(population);
        sb.append('}');
        return sb.toString();
    }
}

这是 Country 实体。每个实体都必须定义至少两个注解:@Entity@Id。之前,我们将 ddl-auto 选项设置为 create-drop,这意味着 Hibernate 将从此实体创建表模式。

@Entity
@Table(name = "countries")
public class Country {

@Entity 注解指定该类是一个实体,并映射到一个数据库表。@Table 注解指定用于映射的数据库表的名称。

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Id 注解指定实体的主键,@GeneratedValue 为主键值提供生成策略。

com/zetcode/repository/CountryRepository.java
package com.zetcode.repository;

import com.zetcode.model.Country;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface CountryRepository extends CrudRepository<Country, Long> {

}

CountryRepository 使用 @Repository 注解进行修饰。

通过继承 Spring CrudRepository,我们为我们的数据存储库实现了一些方法,包括 findAll。这节省了一些样板代码。

com/zetcode/service/ICountryService.java
package com.zetcode.service;

import com.zetcode.model.Country;
import java.util.List;

public interface ICountryService {

    List<Country> findAll();
}

ICountryService 包含 findAll 契约方法。

com/zetcode/service/CountryService.java
package com.zetcode.service;

import com.zetcode.model.Country;
import com.zetcode.repository.CountryRepository;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class CountryService implements ICountryService {

    private final CountryRepository repository;

    public CountryService(CountryRepository repository) {
        this.repository = repository;
    }

    @Override
    public List<Country> findAll() {

        return (List<Country>) repository.findAll();
    }
}

CountryService 包含 findAll 方法的实现。

private final CountryRepository repository;

public CountryService(CountryRepository repository) {
    this.repository = repository;
}

CountryRepository 被注入。

@Override
public List<Country> findAll() {

    return (List<Country>) repository.findAll();
}

findAll 方法返回数据库中所有国家/地区的列表。

com/zetcode/controller/MyController.java
package com.zetcode.controller;

import com.zetcode.model.Country;
import com.zetcode.service.ICountryService;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.servlet.ModelAndView;

import java.util.HashMap;
import java.util.List;

@Controller
public class MyController {

    private final ICountryService countryService;

    public MyController(ICountryService countryService) {
        this.countryService = countryService;
    }

    @GetMapping("/countries")
    public ModelAndView getCountries() {

        var countries = (List<Country>) countryService.findAll();

        var params = new HashMap<String, Object>();
        params.put("countries", countries);

        return new ModelAndView("showCountries", params);
    }
}

MyController 处理来自客户端的请求。

@Controller
public class MyController {

控制器使用 @Controller 注解进行注解。

private final ICountryService countryService;

public MyController(ICountryService countryService) {
    this.countryService = countryService;
}

ICountryService 被注入到 countryService 属性中。

var countries = (List<Country>) countryService.findAll();

从服务对象中,我们使用 findAll 方法检索所有国家/地区。

var params = new HashMap<String, Object>();
params.put("countries", countries);

return new ModelAndView("showCountries", params);

处理被发送到 showCountries.ftlh 模板文件,以及国家/地区列表。

resources/static/index.html
<!DOCTYPE html>
<html>
    <head>
        <title>Home page</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
    </head>
    <body>
        <p>
            <a href="countries">Show countries</a>
        </p>
    </body>
</html>

这是主页。它包含一个获取所有国家/地区的链接。

resources/templates/showCountries.ftlh
<!DOCTYPE html>
<html>
    <head>
        <title>Show countries</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        </head>

    <body>

        <h2>List of countries</h2>

        <table>
            <tr>
                <th>Id</th>
                <th>Name</th>
                <th>Population</th>
            </tr>

            <#list countries as country>
                <tr>
                    <td>${country.id}</td>
                    <td>${country.name}</td>
                    <td>${country.population}</td>
                </tr>
            </#list>
        </table>
    </body>
</html>

这是 showCountries.ftlh 模板文件。使用 #list 指令,我们显示列表中的所有项目。

com/zetcode/Application.java
package com.zetcode;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application  {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

Application 是设置 Spring Boot 应用程序的入口点。@SpringBootApplication 注解启用自动配置和组件扫描。它是 @Configuration@EnableAutoConfiguration@ComponentScan 注解的便捷注解。

$ ./gradlew bootRun

我们运行应用程序并导航到 localhost:8086/SpringBootRepository/

在本文中,我们展示了如何在 Spring 应用程序中使用 @Repository 注解。

作者

我的名字是 Jan Bodnar,我是一位充满激情的程序员,拥有丰富的编程经验。自 2007 年以来,我一直在撰写编程文章。到目前为止,我已撰写了超过 1,400 篇文章和 8 本电子书。我拥有超过十年的编程教学经验。

列出 所有 Spring Boot 教程