Собственный запрос Spring JPA с проекцией дает исключение ConverterNotFoundException

Я использую Spring JPA, и мне нужно иметь собственный запрос. С этим запросом мне нужно получить только два поля из таблицы, поэтому я пытаюсь использовать проекции. Это не работает, это ошибка, которую я получаю:

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

Спасибо за помощь. Я действительно не понимаю, что я делаю неправильно.

3 ответа

Решение

Запрос должен использовать выражение конструктора:

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

И я не знаю Lombok, но убедитесь, что есть конструктор, который принимает два идентификатора в качестве параметров.

С данными о весне вы можете сократить средний человек и просто использовать

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/

JPA 2.1 представляет интересную функцию ConstructorResult, если вы хотите оставить ее родной.

Вы можете вернуть список Object Array (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];
     }
@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(). Даже если я не понимаю, почему параметр после get должен быть в верхнем регистре, в сценарии с нижним регистром он не работал должным образом. т.е. интерфейс с getDeger(), getSaat() в моем случае не работает должным образом.

Другие вопросы по тегам