为什么Spring Boot中使用JPA Hibernate投影会抛出异常?

huangapple go评论60阅读模式
英文:

Why spring boot jpa hibernate projection throws exception?

问题

我正在使用Spring Boot,并尝试使用投影(projections),因为我的类很大,并且很少有时候我需要一次性获取所有数据。所以我上网查找了Spring Boot的官方网站,介绍了投影(projections)。理论上应该很简单,但出于某种原因它抛出了这个异常:

java.lang.IllegalArgumentException: 请求的元组值 [index=0, realType=com.media.daos.ProjectDAO] 无法分配给请求的类型 [java.util.Set]
    at org.hibernate.jpa.spi.TupleBuilderTransformer$HqlTupleImpl.get(TupleBuilderTransformer.java:136)
    at org.hibernate.jpa.spi.TupleBuilderTransformer$HqlTupleImpl.get(TupleBuilderTransformer.java:172)
    at org.springframework.data.jpa.repository.query.AbstractJpaQuery$TupleConverter.convert(AbstractJpaQuery.java:324)
    ...

我不太明白为什么会出现这个问题,因为在这个查询中并没有涉及到java.util.Set类型:

@Value
public class ProjectDetails {
    private String id, name, os, type;
}

@Query(value = "select t.projects from TeamDAO t where t.name = :name", countQuery = "select count(p) from TeamDAO t inner join t.projects as p where t.name = :name")
Page<ProjectDetails> getProjectsByName(@Param("name") String name, Pageable page);

PS:计数查询是必要的,我在另一个讨论线程中讨论过这个问题,它是解决Hibernate SQLSyntaxException的方法。即使它不应该被使用,也没有理由让它影响到投影,所以我不知道为什么它不起作用。你有什么想法吗?

编辑:根据要求提供了ProjectDAOTeamDAO的代码:

public class ProjectDAO extends BaseEntity implements DAO {
    @Value
    public class ProjectDetails {
        private String id, name, os, type;
    }

    // 其他属性和关联
}

public class TeamDAO extends BaseEntity implements DAO {
    // 其他属性和关联
}

id属性来自BaseEntity类,因此在这些类中并未显示出来。我还考虑过可能是查询的问题,所以我将 select t.projects 改为 select t.projects.name as name, t.projects.id as id, t.projects.os as os, t.projects.type as type,但然后会显示 an illegal attempt to dereference collection 的错误。

英文:

im working with spring boot, and trying to use projections since my classes are quite big, and there arent many times when i need all the data at once. So i went online and found Spring Boot's website who present projections. It should be pretty simple yes, but for some reason it throws this exception:

java.lang.IllegalArgumentException: Requested tuple value [index=0, realType=com.media.daos.ProjectDAO] cannot be assigned to requested type [java.util.Set]
	at org.hibernate.jpa.spi.TupleBuilderTransformer$HqlTupleImpl.get(TupleBuilderTransformer.java:136)
	at org.hibernate.jpa.spi.TupleBuilderTransformer$HqlTupleImpl.get(TupleBuilderTransformer.java:172)
	at org.springframework.data.jpa.repository.query.AbstractJpaQuery$TupleConverter.convert(AbstractJpaQuery.java:324)
	at org.springframework.data.repository.query.ResultProcessor$ChainingConverter.convert(ResultProcessor.java:228)
	at org.springframework.data.repository.query.ResultProcessor$ChainingConverter.lambda$and$0(ResultProcessor.java:214)
	at org.springframework.data.repository.query.ResultProcessor$ChainingConverter.convert(ResultProcessor.java:228)
	at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:195)
	at java.base/java.util.ArrayList$Itr.forEachRemaining(ArrayList.java:1033)
	at java.base/java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1801)
	at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484)
	at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)
	at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:913)
	at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
	at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:578)
	at org.springframework.data.domain.Chunk.getConvertedContent(Chunk.java:167)
	at org.springframework.data.domain.PageImpl.map(PageImpl.java:106)
	at org.springframework.data.domain.PageImpl.map(PageImpl.java:30)
...

I don't really get why because there is legit no talk about a java.util.Set in this query:

    @Value
	public class ProjectDetails {
		
		private String id, name, os, type;
	}

@Query(value = &quot;select t.projects from TeamDAO t where t.name = :name&quot;, countQuery = &quot;select count(p) from TeamDAO t inner join t.projects as p where t.name = :name&quot;)
	Page&lt;ProjectDetails&gt; getProjectsByName(@Param(&quot;name&quot;) String name, Pageable page);

PS: the count query is necessary, ive discussed this in another thread where it was the solution to an hibernate SQLSyntaxException. Even if it shouldn't be used, there is no reason for it to mess with projections so i dont know why it doesn't work
Any thoughts?

EDIT: per requested: ProjectDAO and TeamDAO

public class ProjectDAO extends BaseEntity implements DAO {
	
	@Value
	public class ProjectDetails {
		
		private String id, name, os, type;
	}
	
	private static final long serialVersionUID = -2990447415589237412L;
	
	@Column(unique = true, updatable = false)
	private String name;
	
	@Column(length = 1024)
	private String description;
	
	private String features, os, type;
	
	@LastModifiedDate
	private long lastModified;

	@CreatedDate
	private long createdDate;
	
	@CreatedBy
	private String createdBy;
	
	@LastModifiedBy
	private String lastModifiedBy;
	
	@ToString.Exclude
	@EqualsAndHashCode.Exclude
	@ManyToMany(cascade = { CascadeType.PERSIST , CascadeType.MERGE, CascadeType.REFRESH })
	private Set&lt;TeamDAO&gt; teams;

	@ToString.Exclude
	@EqualsAndHashCode.Exclude
	@ManyToMany(cascade = { CascadeType.PERSIST , CascadeType.MERGE, CascadeType.REFRESH })
	private Set&lt;ClientDAO&gt; clients;
	
}

public class TeamDAO extends BaseEntity implements DAO {

	private static final long serialVersionUID = 8649943850027398166L;

	@Column(length = 124, unique = true)
	private String name;

	private String leader;
	
	@LastModifiedDate
	private long lastModified;

	@CreatedDate
	private long createdDate;
	
	@CreatedBy
	private String createdBy;
	
	@LastModifiedBy
	private String lastModifiedBy;

	@ToString.Exclude
	@EqualsAndHashCode.Exclude
	@OneToMany(mappedBy = &quot;team&quot;, cascade = { CascadeType.PERSIST , CascadeType.MERGE, CascadeType.REFRESH })
	private Set&lt;UserDAO&gt; members;

	@ToString.Exclude
	@EqualsAndHashCode.Exclude
	@ManyToMany(mappedBy = &quot;teams&quot;, cascade = { CascadeType.PERSIST , CascadeType.MERGE, CascadeType.REFRESH })
	private Set&lt;ProjectDAO&gt; projects;
}

The id property comes from the BaseEntity class therefore doesn't show on this classes.
I have also considered it coult be the query, so instead of select t.projects i put select t.projects.name as name, t.projects.id as id, t.projects.os as os, t.projects.type as type but then says its an illegal attempt to dereference collection

答案1

得分: 1

好的,以下是翻译好的内容:

好的,我找到了。
我只需要重新修改我的查询。就是我发布的问题。@Query注解必须指定我只想要那些变量。我将查询的值更改为以下内容:

@Query(value = "select new com.media.daos.ProjectDetails(p.id, p.name, p.os, p.type) from TeamDAO t inner join t.projects as p where t.name = :name", countQuery = "select count(p) from TeamDAO t inner join t.projects as p where t.name = :name")

此外,我还发现 Hibernate 不喜欢嵌套类,所以 ProjectDetails 现在是一个独立的类。就像是一个新的文件中。

编辑:根据我所阅读的内容,实现一个映射器(不确定需要实现/扩展哪个 Spring/Hibernate 的类/接口)也可以起作用,但这会映射从数据库中获取的内容,因此会强制数据库发送所有内容,而编辑 SQL 则会强制 MySQL 仅发送那部分内容。但请不要完全引用我说的话。如果您想这样做,请先进行一些研究并进行事实核查。

英文:

Ok i found it.
I Just had to rework my query. It was the problem i posted. The @Query annotation had to specify that i just wanted those variables. I changed the value of the query to the following:

@Query(value = &quot;select new com.media.daos.ProjectDetails(p.id, p.name, p.os, p.type) from TeamDAO t inner join t.projects as p where t.name = :name&quot;, countQuery = &quot;select count(p) from TeamDAO t inner join t.projects as p where t.name = :name&quot;)

Also i found that hibernate does not like nested classes, so ProjectDetails is a standalone class now. As in a new file

EDIT: Also from what i've read, implementing a mapper (not sure about which spring/hibernate class/interface you need to implement/extend) should work as well, but that maps the what comes from the db, therefore forcing the db to send everything while editing the sql, forces mysql to just send that. But don't quote me on this. If you want to do this, please do some research and fact check first

huangapple
  • 本文由 发表于 2020年9月9日 09:13:56
  • 转载请务必保留本文链接:https://go.coder-hub.com/63803450.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定