【发布时间】: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<{parentId, childId}>,而我想要一个{parentId, List<childId>} 的元组。
所以,关于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<IdAndChildrenIds> -
@pvpkiran 感谢您的回答,但您可能没有阅读本段末尾的例外情况。我说我知道这个异常意味着什么,我只需要一个结果
-
你找到解决这个问题的办法了吗?
-
@SuganthanMadhavanPillai 我不记得我到底做了什么,但现在我将创建一个自定义存储库方法并手动选择上述查询(返回
List<Tuple<Long, Long>>;JPA 将其视为Object[])然后我将它在内存中转换为所需的Map<Long, List<Long>>(当然更好一些 DTO)。这样,我可以保持存储库和调用代码干净。 -
@Sam,您能否添加您的解决方案,以便对其他人有益。
标签: java spring hibernate jackson jpql