[英]Fetching part of one to many property using Spring Data Projections
I want to return a tuple of Parent.id
field and List<Child.id>
.我想返回一个
Parent.id
字段和List<Child.id>
的元组。
Parent
: Parent
:
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@Entity
public class Parent implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue
@Column(name = "id")
private Long parentId;
//we actually use Set and override hashcode&equals
@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL)
private List<Child> children = new ArrayList<>();
public void addChild(Child child) {
child.setParent(this);
children.add(child);
}
public void removeChild(Child child) {
child.setParent(null);
children.remove(child);
}
public Long getParentId() {
return id;
}
public List<Child> getReadOnlyChildren() {
return Collections.unmodifiableList(children);
}
}
Child
: Child
:
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import java.io.Serializable;
@Entity
public class Child implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue
@Column(name = "id")
private Long childId;
@ManyToOne
@JoinColumn(name = "id")
private Parent parent;
public Long getChildId() {
return id;
}
public Parent getParent() {
return parent;
}
/**
* Only for usage in {@link Parent}
*/
void setParent(final Parent parent) {
this.parent = parent;
}
}
The Spring Data Projection: Spring 数据投影:
import java.util.List;
interface IdAndChildrenIds {
Long getParentId();
List<ChildId> getChildren();
}
interface ChildId {
Long getChildId();
}
The ParentRepository
this is where problems begin: ParentRepository
这是问题开始的地方:
import org.springframework.data.repository.CrudRepository;
public interface ParentRepository extends CrudRepository<Parent, Long> {
IdAndChildrenIds findIdAndChildrenIdsById(Long id);
}
But that doesn't work because the property doesn't comply with JavaBean standard (getter getReadOnlyChildren
instead of getChildren
), so I configured ObjectMapper
to recognize private fields:但这不起作用,因为该属性不符合 JavaBean 标准(getter
getReadOnlyChildren
而不是getChildren
),因此我将ObjectMapper
配置为识别私有字段:
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import java.util.List;
@Configuration
@EnableWebMvc
public class HibernateConfiguration extends WebMvcConfigurerAdapter {
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
ObjectMapper mapper = new Jackson2ObjectMapperBuilder().build();
mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
converters.add(new MappingJackson2HttpMessageConverter(mapper));
}
}
Then, it still doesn't work because the property is LAZY
initialized and it cannot be fetched outside a transaction (and because I wrote spring.jpa.open-in-view=false
in application.properties
due to that being a better practice).然后,它仍然不起作用,因为该属性是
LAZY
初始化的,并且无法在事务之外获取(并且因为我在application.properties
编写了spring.jpa.open-in-view=false
因为这是一种更好的做法) . So, I must specify explicit join
using query and also must use aliases so Spring Data recognizes the properties:因此,我必须使用查询指定显式
join
并且还必须使用别名,以便 Spring Data 识别属性:
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;
public interface ParentRepository extends CrudRepository<Parent, Long> {
@Query("select " +
" c.parent.parentId as parentId, " +
" c.childId as childId" +
"from Child c inner join a.parent p " +
"where p.parentId=:id")
IdAndChildrenIds findIdAndChildrenIdsById(@Param("id") long id);
}
But this again doesn't work javax.persistence.NonUniqueResultException: result returns more than one elements
because the specified select
gives a list of tuples: List<{parentId, childId}>
, while I want one tuple of {parentId, List<childId>}
.但这又不起作用
javax.persistence.NonUniqueResultException: result returns more than one elements
因为指定的select
给出了一个元组List<{parentId, childId}>
: List<{parentId, childId}>
,而我想要一个元组{parentId, List<childId>}
。
So, regarding this answer, I added @Value("#{target.parentId}")
to Long getParentId();
所以,关于这个答案,我在
Long getParentId();
添加了@Value("#{target.parentId}")
Long getParentId();
. . But that did not have any effect in my case.
但这对我来说没有任何影响。 I still get
NonUniqueResultException
.我仍然得到
NonUniqueResultException
。
Then, I tried changing the return value of the method from IdAndChildrenIds
to IdAndChildrenIds
just to see if the error goes away, even though that solution would not help.然后,我尝试将方法的返回值从
IdAndChildrenIds
为IdAndChildrenIds
只是为了查看错误是否消失,即使该解决方案无济于事。 But that didn't work either:但这也不起作用:
Could not write JSON: No serializer found for class org.springframework.aop.framework.DefaultAdvisorChainFactory and no properties discovered to create BeanSerializer
As I said, field visibility is already set to ANY
.正如我所说,字段可见性已经设置为
ANY
。
Versions:版本:
- Spring Boot 1.5.9.RELEASE - Spring Boot Starter Data JPA - Spring Boot Starter Web - Spring HATEOAS
Looking at this now, weird that I want parent id and ids of its children while knowing the parent id already.现在看看这个,奇怪的是我想要父 ID 和它的孩子的 ID,同时已经知道父 ID。
interface ChildRepo{
@org.spring...Query(value = "select id from children where parent_id = :parentId", nativeQuery = true)
List<Long> findIdsByParentId(Long parentId);
}
@lombok.Value
class IdsDto{
Long parentId;
List<Long> childrenIds;
}
public IdsDto createTupleThing(Long parentId){
return new IdsDto(parentId, childRepo.findIdsByParentId(parentId);
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.