【问题标题】:Fetching part of one to many property using Spring Data Projections使用 Spring Data Projections 获取一对多属性的一部分
【发布时间】:2018-06-09 21:46:04
【问题描述】:

我想返回一个包含Parent.id 字段和List<Child.id> 的元组。


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:

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;
    }
}

Spring 数据投影:

import java.util.List;

interface IdAndChildrenIds {

    Long getParentId();

    List<ChildId> getChildren();
}

interface ChildId {

    Long getChildId();
}

ParentRepository 这是问题开始的地方:

import org.springframework.data.repository.CrudRepository;

public interface ParentRepository extends CrudRepository<Parent, Long> {

    IdAndChildrenIds findIdAndChildrenIdsById(Long id);
}

但这不起作用,因为该属性不符合 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));
    }
}

然后,它仍然不起作用,因为该属性已初始化 LAZY 并且无法在事务之外获取(并且因为我在 application.properties 中写了 spring.jpa.open-in-view=false,因为这是一个更好的做法)。因此,我必须使用查询明确指定 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);
}

但这又不起作用javax.persistence.NonUniqueResultException: result returns more than one elements,因为指定的select 给出了一个元组列表:List&lt;{parentId, childId}&gt;,而我想要一个{parentId, List&lt;childId&gt;} 的元组。

所以,关于this 的答案,我将@Value("#{target.parentId}") 添加到Long getParentId();。但这对我来说没有任何影响。我仍然得到NonUniqueResultException

然后,我尝试将方法的返回值从 IdAndChildrenIds 更改为 IdAndChildrenIds 只是为了查看错误是否消失,即使该解决方案没有帮助。但这也不起作用:

无法编写 JSON:没有找到类 org.springframework.aop.framework.DefaultAdvisorChainFactory 的序列化程序,也没有发现用于创建 BeanSerializer 的属性

正如我所说,字段可见性已设置为 ANY


版本:

 - Spring Boot 1.5.9.RELEASE
 - Spring Boot 入门数据 JPA
 - Spring Boot Starter Web
 - 春天的仇恨

【问题讨论】:

  • NonUniqueResultException 表示查询从数据库返回多于一行,但在您的方法中,您只接受一个条目。 .您应该将IdAndChildrenIds 更改为List&lt;IdAndChildrenIds&gt;
  • @pvpkiran 感谢您的回答,但您可能没有阅读本段末尾的例外情况。我说我知道这个异常意味着什么,我只需要一个结果
  • 你找到解决这个问题的办法了吗?
  • @SuganthanMadhavanPillai 我不记得我到底做了什么,但现在我将创建一个自定义存储库方法并手动选择上述查询(返回List&lt;Tuple&lt;Long, Long&gt;&gt;;JPA 将其视为Object[])然后我将它在内存中转换为所需的Map&lt;Long, List&lt;Long&gt;&gt;(当然更好一些 DTO)。这样,我可以保持存储库和调用代码干净。
  • @Sam,您能否添加您的解决方案,以便对其他人有益。

标签: java spring hibernate jackson jpql


【解决方案1】:

现在看这个,很奇怪,我想要父 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);
}

【讨论】:

  • 我们两次调用db,如何根除?
  • 你为什么要叫它两次我不明白?
  • 在您提供的示例中,首先我们在 findIdsByParentId 中获取 parentId,然后使用 childRepo 获取子值,这意味着调用 db 两次。我们想根除它。
  • 查看类似于stackoverflow.com/q/60641117/5557885的东西,这是我的解决方案。
  • 只有一个调用“childRepo.findIdsByParentId(parentId)”。想必你已经知道了父id
猜你喜欢
  • 2017-06-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-03-14
相关资源
最近更新 更多