[英]How to ignore field from Spring Data JPA projection mapping
I'm building a REST API for a Q&A web application.我正在为 Q&A web 应用程序构建 REST API。 It has communities where each community is defined by an id, name, description, members, and others.
它有社区,其中每个社区都由 id、名称、描述、成员和其他人定义。 Right now, I have an endpoint for fetching the given community data
/api/v1/communities/{id}
.现在,我有一个端点来获取给定的社区数据
/api/v1/communities/{id}
。
The response looks like this响应看起来像这样
{
"name": "VIP",
"id": 5,
"displayName": "VIP",
"subtopics": [
"Sports"
],
"primaryTopic": "Gaming",
"about": "",
"isPublic": false,
"isPrivate": true,
"isRestricted": false
}
The response is a DTO that's mapped automatically using Spring Data Projection.响应是使用 Spring 数据投影自动映射的 DTO。 Now, I want to add a new field to the DTO.
现在,我想向 DTO 添加一个新字段。 The field called
isMember
and it should store whether the currently authenticated user is a member of the given community.名为
isMember
的字段,它应该存储当前经过身份验证的用户是否是给定社区的成员。
public interface CommunityResponse {
Long getId();
String getName();
String getDisplayName();
String getAbout();
@Value("#{target.primaryTopic.getDisplayName()}")
String getPrimaryTopic();
@Value("#{target.getSubtopicsDisplayNames()}")
Set<String> getSubtopics();
@Value("#{target.isPublic()}")
@JsonProperty("isPublic")
boolean isPublic();
@Value("#{target.isPrivate()}")
@JsonProperty("isPrivate")
boolean isPrivate();
@Value("#{target.isRestricted()}")
@JsonProperty("isRestricted")
boolean isRestricted();
// boolean isMember();
}
I thought about using Class-based projection.我考虑过使用基于类的投影。 I converted the interface into a class and added the
isMember
field to the DTO and called setIsMember()
inside the service class, but I got an error that says no property 'isMember' found for type 'CommunityResponse'
我将接口转换为 class 并将
isMember
字段添加到 DTO 并在服务 class 中调用setIsMember()
,但我收到一个错误,提示no property 'isMember' found for type 'CommunityResponse'
As far as I understand, each field in a projection class or interface must either share the same name as a field in the entity or it should be derived from other fields using Spring's expression language.据我了解,投影 class 或接口中的每个字段必须与实体中的字段共享相同的名称,或者应该使用 Spring 的表达式语言从其他字段派生。
The problem is that isMember
is not a field from the entity and I don't know how to derive it using Spring's expression language.问题是
isMember
不是来自实体的字段,我不知道如何使用 Spring 的表达式语言派生它。 The logic for initializing isMember
looks like this:初始化
isMember
的逻辑如下所示:
public boolean isMember(Long communityId, Authentication authentication) {
UserDetails userDetails = (UserDetails) authentication.getPrincipal();
return userAccountRepository.findByEmail(userDetails.getUsername()).map(user -> {
return user.getJoinedCommunities().stream().anyMatch(community -> community.getId() == communityId);
}).orElse(false);
}
Other classes其他类
UserAccount.java
@Entity
@AllArgsConstructor
@NoArgsConstructor
@Data
public class UserAccount {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
@NotBlank(message = "Firstname is required")
private String firstname;
@NotBlank(message = "Lastname is required")
private String lastname;
@NotBlank(message = "Username is required")
private String username;
@NotBlank(message = "Email is required")
private String email;
@NotBlank(message = "Password is required")
private String hashedPassword;
private Instant creationDate;
private Boolean activated;
@ManyToMany(fetch = FetchType.LAZY, mappedBy = "members")
private Set<Community> joinedCommunities;
}
Community.java
@Entity
@Table(name = "community")
@AllArgsConstructor
@NoArgsConstructor
@Data
public class Community {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
@Enumerated(EnumType.STRING)
private CommunityType type;
@Column(unique = true)
private String name;
private String displayName;
private String about;
@Enumerated(EnumType.STRING)
private Topic primaryTopic;
@CollectionTable(name = "subtopic")
@ElementCollection(targetClass = Topic.class)
@Enumerated(EnumType.STRING)
@Column(name = "topic")
private Set<Topic> subtopicSet;
@ManyToMany
@JoinTable(name = "community_members",
joinColumns = { @JoinColumn(name = "community_id") },
inverseJoinColumns = { @JoinColumn(name = "member_id") })
private Set<UserAccount> members;
public String getDisplayName() {
return displayName;
}
public boolean isPublic() {
return type == CommunityType.PUBLIC;
}
public boolean isPrivate() {
return type == CommunityType.PRIVATE;
}
public boolean isRestricted() {
return type == CommunityType.RESTRICTED;
}
public Set<String> getSubtopicsDisplayNames() {
return getSubtopicSet().stream().map(Topic::getDisplayName).collect(Collectors.toSet());
}
}
CommunityRepository.java
public interface CommunityRepository extends JpaRepository<Community, Long> {
Optional<CommunityResponse> findCommunityById(Long id);
List<CommunityResponse> findCommunityResponseBy();
}
I thought about using Class-based projection.
我考虑过使用基于类的投影。 I converted the interface into a class and added the isMember field to the DTO
我将接口转换为 class 并将 isMember 字段添加到 DTO
You could try to derive isMember
within JPQL query constructing class-based projection:您可以尝试在构建基于类的投影的 JPQL 查询中派生
isMember
:
@Query("select new com.my.project.DTO(c.id, c.name, (:user member of c.members)) from Community c ...")
List<CommunityResponse> findCommunityResponseBy(UserAccount user);
Alternatively, in interface-based projection you could try或者,在基于界面的投影中,您可以尝试
@Value("#{target.members.contains(#user)}")
boolean isMember;
where #user
is the argument of findBy
method.其中
#user
是findBy
方法的参数。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.