简体   繁体   English

使用 Spring Data Projections 获取一对多属性的一部分

[英]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.然后,我尝试将方法的返回值从IdAndChildrenIdsIdAndChildrenIds只是为了查看错误是否消失,即使该解决方案无济于事。 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM