在本文档里,我们将要实现与数据库交互,我们将把团队成员信息都存储到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;
}
下面我来解释一下代码各部分含义:
@Entity
: 表明这个类是一个 JPA 实体,它会映射到数据库中的一张表。@Table(name = "team_members")
: 明确指定数据库中表的名称。如果不指定,Hibernate 会根据类名生成一个(例如 teammember),推荐明确指定。@Id
: 指定 id 字段是表的主键。@GeneratedValue(strategy = GenerationType.IDENTITY)
: 配置主键的生成策略。- GenerationType.IDENTITY: 依赖数据库的自增列功能来生成主键 (例如 PostgreSQL 的 SERIAL 或 BIGSERIAL 类型)。这是常用的策略。
- 其他策略还有 SEQUENCE (使用数据库序列), TABLE (使用特定表模拟序列), AUTO (JPA 提供商自动选择)。
@Column(...)
: 用于更详细地定义字段如何映射到数据库列。- nullable = false: 表示该列不允许为 NULL。
- length = 100: 表示字符串类型的列的最大长度。
- name = "image_path": 自定义数据库中的列名。
@Lob
: 将字段标记为大对象。对于 String 类型的字段,这通常映射到数据库的 CLOB 或 TEXT 类型,适合存储较长的文本,如简介 bio。@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.
}
代码解释:
@Repository
: 这是一个Spring的stereotype 注解,表明这个接口是一个数据访问对象 (DAO) 的组件。虽然对于继承JpaRepository的接口来说,这个注解不是严格必需的(Spring Data JPA会自动扫描并创建实现),但加上它可以更清晰地表达其职责。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<TeamMember> teamMembers = teamMemberRepository.findAllByOrderByNameAsc();
model.addAttribute("members", teamMembers);
model.addAttribute("pageTitle", "我们的团队");
return "team";
}
}
代码解释:
- 导入 TeamMemberRepository。
- 导入 @Autowired (虽然对于单构造函数注入,从 Spring 4.3 开始它是可选的,但显式写出更清晰)。
private final TeamMemberRepository teamMemberRepository;
: 声明一个 TeamMemberRepository 类型的成员变量。使用 final 关键字鼓励通过构造函数进行初始化。@Autowired public TeamController(TeamMemberRepository teamMemberRepository)
:- 这是构造函数注入。当 Spring 创建 TeamController 的实例时,它会自动查找一个 TeamMemberRepository 类型的 Bean (也就是我们之前定义的接口的实现) 并将其作为参数传递给构造函数。这是推荐的依赖注入方式。
- 或者,你也可以使用字段注入 (@Autowired 直接放在成员变量上) 或 Setter 注入,但构造函数注入更利于测试和保证对象状态的完整性。
List<TeamMember> teamMembers = teamMemberRepository.findAllByOrderByNameAsc();
:- 调用 teamMemberRepository 的 findAllByOrderByNameAsc() 方法 (或者直接用 findAll(),它返回所有成员,顺序可能不固定,除非数据库有默认排序或你在实体上定义了)。
- 这个方法会执行相应的 SQL 查询 (类似 SELECT * FROM team_members ORDER BY name ASC) 并将结果映射成 TeamMember对象的列表。
接下来的任务
现在我们完成了后端信息连接数据库,接下来我们将在前端实现表单创建成员数据。
Comments NOTHING