Spring Data JPA 与 JSON 字段过滤注解(详细说明)¶
本章涵盖 JPA 实体映射注解、Spring Data 仓库扩展注解、以及 Jackson JSON 字段控制与动态过滤实践。
一、JPA 核心注解¶
1.1 @Entity¶
作用: 标识持久化实体类,对应数据库表中的一行记录。
定义位置: 类上。
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, length = 100)
private String name;
@Column(unique = true)
private String email;
}
补充说明:
- 类必须有无参构造函数(JPA 反射实例化)。
- 实体默认表名为类名(可通过
@Table修改)。
1.2 @Id 与 @GeneratedValue¶
作用: 定义主键及主键生成策略。
| 策略 | 说明 |
|---|---|
IDENTITY |
数据库自增(MySQL 常用) |
SEQUENCE |
序列(Oracle/PostgreSQL) |
TABLE |
使用表维护主键序列 |
AUTO |
由 JPA 自动选择 |
1.3 @Column / @Table / @UniqueConstraint / @Index¶
作用: 映射数据库表与列属性。
@Table(
name = "users",
uniqueConstraints = @UniqueConstraint(columnNames = {"email"}),
indexes = {@Index(name = "idx_name", columnList = "name")}
)
public class User { ... }
常用属性:
nullable:是否允许为空unique:是否唯一length:字符字段长度precision/scale:数值精度
1.4 @Transient¶
作用: 排除字段不参与持久化(类似于 Java transient 但更明确)。
@Transient
private String token; // 仅用于内存,不入库
1.5 关联映射¶
作用: 建立实体间关系并管理外键。
@Entity
public class Order {
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private User user;
@OneToMany(mappedBy = "order", cascade = CascadeType.ALL, orphanRemoval = true)
private List<OrderItem> items = new ArrayList<>();
}
说明:
fetch:加载策略(EAGER/LAZY)cascade:级联操作类型(ALL、PERSIST、REMOVE等)orphanRemoval:孤儿删除,父对象移除后自动删除子对象
1.6 其他常见注解¶
| 注解 | 说明 |
|---|---|
@Enumerated(EnumType.STRING) |
枚举以字符串存储 |
@Lob |
大对象(TEXT/BLOB) |
@Version |
乐观锁版本号 |
@Temporal |
旧版日期类型标注(Date、Calendar) |
二、Spring Data JPA 特有注解¶
2.1 @Repository¶
作用: 标识持久层组件,并启用异常转换。
对继承自
JpaRepository的接口可省略,Spring 会自动识别。
2.2 @EnableJpaRepositories¶
作用: 开启 JPA 仓库扫描功能。
定义位置: 配置类上。
Spring Boot 默认已开启。
@Configuration
@EnableJpaRepositories(basePackages = "com.example.demo.repo")
public class JpaConfig {}
2.3 @EntityGraph¶
作用: 指定在查询时一起抓取的关联字段,避免 N+1 查询。
@EntityGraph(attributePaths = {"orders"})
List<User> findAll();
说明:
- 默认抓取策略
EAGER,但可通过@EntityGraph动态控制。 - 常用于
findAll()或自定义查询。
2.4 @Query / @Modifying / @Param¶
作用: 自定义 JPQL 或原生 SQL 查询。
@Query("SELECT u FROM User u WHERE u.email = :email")
User findByEmail(@Param("email") String email);
@Modifying
@Query("UPDATE User u SET u.name = :name WHERE u.id = :id")
int rename(@Param("id") Long id, @Param("name") String name);
说明:
@Modifying必须搭配@Transactional才能执行更新/删除。- 可通过
nativeQuery = true启用原生 SQL。
2.5 派生查询(Derived Query)¶
作用: 根据方法名自动生成查询语句。
List<User> findByEmailAndStatusOrderByCreatedAtDesc(String email, int status);
常见关键字:
findBy/existsBy/countByAnd/OrBetween/LessThan/GreaterThanLike/StartingWith/EndingWithOrderBy...Asc/Desc
三、Jackson JSON 字段控制注解¶
3.1 @JsonIgnore / @JsonIgnoreProperties¶
作用: 忽略字段或字段集合的序列化与反序列化。
@JsonIgnoreProperties({"password", "salt"})
public class UserVO {
private String username;
private String password;
private String salt;
}
说明:
@JsonIgnore用于单个字段。@JsonIgnoreProperties用于类级,支持多个字段。
3.2 @JsonProperty¶
作用: 重命名字段、或控制单向序列化/反序列化。
@JsonProperty("user_name")
private String name;
说明:
- 可用
access = JsonProperty.Access.WRITE_ONLY实现“只反序列化、不序列化”。
3.3 @JsonInclude¶
作用: 控制哪些字段被包含。
| 模式 | 说明 |
|---|---|
ALWAYS |
默认,所有字段均输出 |
NON_NULL |
忽略 null |
NON_EMPTY |
忽略空字符串、空集合 |
NON_DEFAULT |
忽略默认值 |
@JsonInclude(JsonInclude.Include.NON_NULL)
private String phone;
3.4 @JsonFormat¶
作用: 格式化时间或数字输出。
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Tokyo")
private LocalDateTime createdAt;
3.5 @JsonView¶
作用: 通过视图分组输出不同层级字段集合。
public class Views {
public static class Public {}
public static class Detail extends Public {}
}
public class UserVO {
@JsonView(Views.Public.class) Long id;
@JsonView(Views.Public.class) String name;
@JsonView(Views.Detail.class) String email;
}
@GetMapping("/users/{id}")
@JsonView(Views.Public.class)
public UserVO get(@PathVariable Long id){ return service.getUser(id); }
说明:
Detail继承Public,输出时包含父类字段。
3.6 @JsonFilter(动态过滤)¶
作用: 通过 FilterProvider 动态指定序列化字段。
@JsonFilter("userFilter")
public class UserVO {
Long id; String name; String email; String phone;
}
@GetMapping("/users/{id}")
public MappingJacksonValue get(@PathVariable Long id) {
UserVO vo = service.get(id);
var value = new MappingJacksonValue(vo);
var filters = new SimpleFilterProvider()
.addFilter("userFilter", SimpleBeanPropertyFilter.filterOutAllExcept("id","name"));
value.setFilters(filters);
return value;
}
补充:
- 需在
ObjectMapper中注册 FilterProvider。 - 常用于动态字段输出(如用户隐私控制)。
四、综合示例:JPA + Validation + JSON¶
@Entity
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@NotBlank
@Column(nullable = false, length = 100)
private String name;
@Positive
@Column(nullable = false)
private BigDecimal price;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Tokyo")
private LocalDateTime createdAt = LocalDateTime.now();
}
@RestController
@RequestMapping("/products")
public class ProductApi {
private final ProductRepository repo;
public ProductApi(ProductRepository repo){ this.repo = repo; }
@PostMapping
public Product create(@Valid @RequestBody Product p) {
return repo.save(p);
}
@GetMapping("/{id}")
public Product get(@PathVariable Long id){
return repo.findById(id).orElseThrow();
}
}
五、最佳实践总结¶
- 实体设计:字段必须与业务逻辑匹配;避免双向多对多;尽量用单向关系。
- 延迟加载:默认使用
LAZY,必要时显式@EntityGraph。 - DTO/VO 分离:输出层使用
@JsonView或 MapStruct,避免直接暴露实体。 - 校验 + JSON:搭配 Bean Validation 与 Jackson 注解实现前后端一致约束。
- 动态过滤:
@JsonFilter用于隐私控制或接口字段定制。 - 分页与排序:使用
Pageable参数,避免手写分页逻辑。 - 异常处理:结合全局
@RestControllerAdvice捕获DataIntegrityViolationException。