第二个功能–>数据库集成

QcrTiMo 发布于 9 天前 22 次阅读


在本文档里,我们将要实现与数据库交互,我们将把团队成员信息都存储到PostgreSQL数据库中,并从数据库中读取这些信息来展示。

在阅读这份文档之前,你要确保自己已经成功安装并运行了 PostgreSQL 数据库

如何验证?

按下Window+R,输入cmd打开终端,然后输入:

psql -U postgres -d QcrTiMo

注意:我这里的postgres是默认用户名,QcrTiMo是我自己创建的数据库名称。

如果输入完回车出现“用户postgres的口令:”,说明数据库存在。

然后我们输入密码(输入时终端不会有动静,无需担心,直接输入然后回车

如果数据库正常运行:

我们就可以放心大胆进行下一步了,然后输入\q退出数据库环境。

集成 PostgreSQL 数据库

配置数据库连接 (application.properties)

我们已经在搭建环境时讲过如何把Spring Boot连接到PostgreSQL数据库,如果有不懂的可以多看几眼或者评论留言。

将TeamMember类标记为JPA实体

现在我们需要修改TeamMember.java类,告诉JPA这个类应该映射到数据库中的一张表。

打开 src/main/java/com/NZI/web/model/TeamMember.java 文件,修改如下:

package com.NZI.web.model; //确保包名正确
import jakarta.persistence.*; //JPA注解包
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity     //标记这是一个JPA实体类,会映射到数据库表(表名通常是类名小写,即team_member)
@Table(name = "team_members")     //自定义表名,如果不指定,默认为 "teammember" 或 "team_member"
public class TeamMember {
    @Id    //标记这是主键字段
    @GeneratedValue(strategy = GenerationType.IDENTITY)     //主键生成策略:数据库自增
    private Long id;
    @Column(nullable = false, length = 100)     //定义列属性,例如非空,长度限制
    private String name;
    @Column(length = 100)
    private String role;
    @Lob     //对于可能较长的文本内容,可以使用@Lob (Large Object)
    @Column(columnDefinition = "TEXT")     //明确指定数据库类型为TEXT
    private String bio;
    @Column(name = "image_path", length = 255)     //自定义列名
    private String imagePath;      
}

下面我来解释一下代码各部分含义:

  1. @Entity: 表明这个类是一个 JPA 实体,它会映射到数据库中的一张表。
  2. @Table(name = "team_members"): 明确指定数据库中表的名称。如果不指定,Hibernate 会根据类名生成一个(例如 teammember),推荐明确指定。
  3. @Id: 指定 id 字段是表的主键。
  4. @GeneratedValue(strategy = GenerationType.IDENTITY): 配置主键的生成策略。
    • GenerationType.IDENTITY: 依赖数据库的自增列功能来生成主键 (例如 PostgreSQL 的 SERIAL 或 BIGSERIAL 类型)。这是常用的策略。
    • 其他策略还有 SEQUENCE (使用数据库序列), TABLE (使用特定表模拟序列), AUTO (JPA 提供商自动选择)。
  5. @Column(...): 用于更详细地定义字段如何映射到数据库列。
    • nullable = false: 表示该列不允许为 NULL。
    • length = 100: 表示字符串类型的列的最大长度。
    • name = "image_path": 自定义数据库中的列名。
  6. @Lob: 将字段标记为大对象。对于 String 类型的字段,这通常映射到数据库的 CLOB 或 TEXT 类型,适合存储较长的文本,如简介 bio。
  7. @Column(columnDefinition = "TEXT") 更明确地指定了PostgreSQL 中的类型。

创建Spring Data JPA Repository

Spring Data JPA可以极大地简化数据库访问层的代码。你只需要定义一个接口,继承JpaRepository,Spring Boot就会在运行时自动为你实现这个接口。

  • 在你的主包 (例如 com.NZI.web) 下创建一个新的子包repository。
  • 在repository包下,创建一个名为TeamMemberRepository.java 的接口
package com.NZI.web.repository;
import com.NZI.web.model.TeamMember;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository     //标记这是一个Spring boot管理的Repository Bean
public interface TeamMemberRepository extends JpaRepository<TeamMember, Long> {
    //Spring Data JPA会根据方法名自动生成查询实现
    //例如,查找所有按名字排序的成员(虽然JpaRepository已提供findAll())
    List<TeamMember> findAllByOrderByNameAsc();
    //你也可以添加自定义的查询方法,例如按角色查找
    List<TeamMember> findByRole(String role);
    List<TeamMember> findByName(String name);
    //JpaRepository 已经提供了很多常用的方法:
    //save(entity), findById(id), findAll(), deleteById(id), count(), etc.
}

代码解释:

  1. @Repository: 这是一个Spring的stereotype 注解,表明这个接口是一个数据访问对象 (DAO) 的组件。虽然对于继承JpaRepository的接口来说,这个注解不是严格必需的(Spring Data JPA会自动扫描并创建实现),但加上它可以更清晰地表达其职责。
  2. public interface TeamMemberRepository extends JpaRepository<TeamMember, Long>:
    • TeamMemberRepository 是我们定义的接口。
    • 它继承了 JpaRepository<T, ID>。
      • T: 仓库管理的实体类型,这里是 TeamMember。
      • ID: 实体主键的类型,这里是 TeamMember 的 id 字段类型,即 Long。
    • 通过继承 JpaRepository,TeamMemberRepository 立即拥有了许多内置的 CRUD (Create, Read, Update, Delete) 方法,如 save(), findById(), findAll(), deleteById() 等,以及分页和排序功能。
    • 你还可以通过遵循特定的命名约定来定义自己的查询方法 (如 findByRole(String role)),Spring Data JPA 会自动为你生成实现。

TeamController.java中使用Repository

现在我们需要更新 TeamController,让它从数据库中获取团队成员列表,而不是使用硬编码的数据。

打开 src/main/java/com/NZI/web/controller/TeamController.java,修改:

package com.NZI.web.Controller;
import com.NZI.web.model.TeamMember;
import com.NZI.web.repository.TeamMemberRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import java.util.List;
@Controller 
public class TeamController {
    private final TeamMemberRepository teamMemberRepository;
    @Autowired
    public TeamController(TeamMemberRepository teamMemberRepository){
        this.teamMemberRepository = teamMemberRepository;
    }
    @GetMapping("/team")
    public String showTeamPage(Model model) { 
        List&lt;TeamMember> teamMembers = teamMemberRepository.findAllByOrderByNameAsc();
        model.addAttribute("members", teamMembers);
        model.addAttribute("pageTitle", "我们的团队");
        return "team"; 
    }
}

代码解释:

  1. 导入 TeamMemberRepository。
  2. 导入 @Autowired (虽然对于单构造函数注入,从 Spring 4.3 开始它是可选的,但显式写出更清晰)。
  3. private final TeamMemberRepository teamMemberRepository;: 声明一个 TeamMemberRepository 类型的成员变量。使用 final 关键字鼓励通过构造函数进行初始化。
  4. @Autowired public TeamController(TeamMemberRepository teamMemberRepository):
    • 这是构造函数注入。当 Spring 创建 TeamController 的实例时,它会自动查找一个 TeamMemberRepository 类型的 Bean (也就是我们之前定义的接口的实现) 并将其作为参数传递给构造函数。这是推荐的依赖注入方式。
    • 或者,你也可以使用字段注入 (@Autowired 直接放在成员变量上) 或 Setter 注入,但构造函数注入更利于测试和保证对象状态的完整性。
  5. List<TeamMember> teamMembers = teamMemberRepository.findAllByOrderByNameAsc();:
    • 调用 teamMemberRepository 的 findAllByOrderByNameAsc() 方法 (或者直接用 findAll(),它返回所有成员,顺序可能不固定,除非数据库有默认排序或你在实体上定义了)。
    • 这个方法会执行相应的 SQL 查询 (类似 SELECT * FROM team_members ORDER BY name ASC) 并将结果映射成 TeamMember对象的列表。

接下来的任务

现在我们完成了后端信息连接数据库,接下来我们将在前端实现表单创建成员数据。

斯哈斯哈斯哈,佳代子啊啊啊啊啊啊ᕕ(◠ڼ◠)ᕗᕕ(◠ڼ◠)ᕗᕕ(◠ڼ◠)ᕗᕕ(◠ڼ◠)ᕗᕕ(◠ڼ◠)ᕗ
最后更新于 2025-05-19