【问题标题】:Spring JPA native query with Projection gives "ConverterNotFoundException"带有 Projection 的 Spring JPA 本机查询给出“ConverterNotFoundException”
【发布时间】:2018-09-05 02:53:04
【问题描述】:

我正在使用 Spring JPA,我需要一个本机查询。通过该查询,我只需要从表中获取两个字段,因此我尝试使用Projections。它不起作用,这是我得到的错误:

org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [org.springframework.data.jpa.repository.query.AbstractJpaQuery$TupleConverter$TupleBackedMap] to type [com.example.IdsOnly]

我试图准确地遵循我链接的那个页面的说明,我试图让我的查询非本地(如果我使用投影,我真的需要它是本地的吗,顺便说一句?),但我总是得到那个错误。
如果我使用一个接口它可以工作,但结果是代理,我真的需要它们是“正常结果”,我可以变成 json。

所以,这是我的代码。实体:

import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@Entity
@Table(name = "TestTable")
public class TestTable {

    @Id
    @Basic(optional = false)
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    @Column(name = "Id")
    private Integer id;
    @Column(name = "OtherId")
    private String otherId;
    @Column(name = "CreationDate")
    @Temporal(TemporalType.TIMESTAMP)
    private Date creationDate;
    @Column(name = "Type")
    private Integer type;
}

投影的类:

import lombok.Value;

@Value // This annotation fills in the "hashCode" and "equals" methods, plus the all-arguments constructor
public class IdsOnly {

    private final Integer id;
    private final String otherId;
}

存储库:

public interface TestTableRepository extends JpaRepository<TestTable, Integer> {

    @Query(value = "select Id, OtherId from TestTable where CreationDate > ?1 and Type in (?2)", nativeQuery = true)
    public Collection<IdsOnly> findEntriesAfterDate(Date creationDate, List<Integer> types);
}

以及试图获取数据的代码:

@Autowired
TestTableRepository ttRepo;
...
    Date theDate = ...
    List<Integer> theListOfTypes = ...
    ...
    Collection<IdsOnly> results = ttRepo.findEntriesAfterDate(theDate, theListOfTypes);  

感谢您的帮助。我真的不明白我做错了什么。

【问题讨论】:

  • 代码中的MyProjectionClass 类在哪里
  • 抱歉,我在将实际代码设为“伪”时更改了它的名称。我编辑了问题,使错误显示“com.example.IdsOnly”。

标签: java spring spring-data-jpa projection


【解决方案1】:

使用弹簧数据,您可以切断中间人并简单地定义

public interface IdsOnly {
  Integer getId();
  String getOtherId();
}

并使用原生查询,例如;

@Query(value = "Id, OtherId from TestTable where CreationDate > ?1 and Type in (?2)", nativeQuery = true)
    public Collection<IdsOnly> findEntriesAfterDate(Date creationDate, List<Integer> types);

查看https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#projections

【讨论】:

  • @InêsGomes,它基本上运行一个原生 sql 查询并将输出模式转换为 POJO,所以你可以用 sql 做什么你也可以在这里做,我不太明白你的问题,但是,如果您可以发送遇到的错误和问题,我会尽力提供帮助,我知道这里的社区也很乐意提供帮助,所以也许您应该专门发布一个新问题
  • 我将接受的答案更改为这个,因为它实际上是针对本地查询的,并且比另一个拥有更多的选票。
  • 你可以用 nativeQuery=true 完全一样
  • 结果中的布尔字段怎么样?每当您尝试执行 interface.getBooleanField 时,这些似乎都会破坏代码。
【解决方案2】:

查询应该使用constructor expression:

@Query("select new com.example.IdsOnly(t.id, t.otherId) from TestTable t where t.creationDate > ?1 and t.type in (?2)")

我不知道 Lombok,但请确保有一个将两个 ID 作为参数的构造函数。

【讨论】:

  • 如果你不知道Lombok,一定要看看! >_> 谢谢,我试试这个。所以我想我的查询毕竟不需要是原生的。
  • 成功了。非常感谢。顺便说一句,我还必须用实体的变量名(所以,“id”、“otherId”、“creationDate”和“type”)编写列名。
  • 评论:解决方案不适用于 nativeQuery
  • 好的,我选择了@shahaf 的答案作为接受的答案,因为它用于本地查询并且有更多的选票。
  • 问题是原生查询
【解决方案3】:

JPA 2.1 引入了一个有趣的ConstructorResult 功能,如果您想保持它原生

【讨论】:

  • 谢谢,我看到了,但对于我的简单需求来说,它似乎过于复杂(而且我认为 Projections 也适用于原生查询)。如果我真的需要我的查询是本地的,我肯定会使用它。我现在拥有的那些,最终都不需要了。
  • 除了在 JPA 实体上提供提到的 SqlResultSetMappingConstructorResult 然后直接执行查询之外,我还发现没有办法让我的本机 SQL 查询结果映射到自定义类型在EntityManager。所以不幸的是,鉴于这些约束(本机查询 + 自定义投影),无法使用 Spring Data 的 @Query 注释。
【解决方案4】:

您可以返回对象数组(List)的列表作为存储库类中本机查询方法的返回类型。

@Query(
            value = "SELECT [type],sum([cost]),[currency] FROM [CostDetails] " +
                    "where product_id = ? group by [type],[currency] ",
            nativeQuery = true
    )
    public List<Object[]> getCostDetailsByProduct(Long productId);
for(Object[] obj : objectList){
     String type = (String) obj[0];
     Double cost = (Double) obj[1];
     String currency = (String) obj[2];
     }

【讨论】:

    【解决方案5】:
    @Query(value = "select  isler.saat_dilimi as SAAT, isler.deger as DEGER from isler where isler.id=:id", nativeQuery = true) 
    List<Period> getById(@Param("id") Long id);
    
    
    public interface Period{
        Long getDEGER(); 
    
        Long getSAAT();
    
    }
    

    如上面给出的本机查询示例代码所示,将返回值转换为任何值,例如“SAAT”、“DEGER”,然后定义具有 getDEGER() 和 getSAAT() 的接口“period”。即使我不明白为什么 get 之后的参数必须是大写的,在小写的情况下它也不能正常工作。 IE。与 getDeger() 的接口,getSaat() 在我的情况下无法正常工作。

    【讨论】:

    • 所以你有 select isler.saat_dilimi as saatgetSaat() 没有工作?那会很奇怪。
    • @nonzaprej 抱歉回复晚了。你是对的。您是否知道是否有可能将本机查询结果设置为 dto 而不是界面投影?
    • 不,我不知道。我在另一个项目中也需要它,我放弃并使用相同的字段创建了接口和 DTO(好吧,接口只有方法,但你知道我的意思)并在 DTO 中创建了一个构造函数方法接口中的所有值作为唯一参数给出。喜欢:public class PeriodDTO { private Long deger; private Long saat; public PeriodDTO(Period periodInterface) { this.deger = periodInterface.getDEGER(); this.saat = periodInterface.getSAAT(); } } 这很丑,但有点……
    • @nonzaprej 谢谢回复,是的,我也觉得,这个解决方案很丑
    • 此处的参数是按顺序填写的,因此请在界面或查询中更改字段顺序 - 使它们匹配(第一个是 SAAT,第二个:DEGER)
    猜你喜欢
    • 2019-04-15
    • 1970-01-01
    • 2017-04-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-05
    • 1970-01-01
    • 2020-02-19
    相关资源
    最近更新 更多