#java #spring #postgresql #spring-jdbc
Вопрос:
Я пытаюсь выполнить функцию sql с помощью классов SimpleJdbcCall и BeanPropertyRowMapper, но не могу получить правильный список объектов. Похоже, в списке есть пара ключ-значение. Также у меня есть ошибка:
Inconvertible types; cannot cast 'com.example.demo.model.MyCalendarDto' to 'org.springframework.util.LinkedCaseInsensitiveMap'
Нужно отметить, что я обязан использовать именно эту функцию и не могу ее изменить. Я использую PostgreSQL 13. Может кто-нибудь показать мне ошибку?
- Стол:
-- cntr_m2.calendar definition
-- Drop table
-- DROP TABLE cntr_m2.calendar;
CREATE TABLE cntr_m2.calendar (
id_calendar int4 NOT NULL,
period_name varchar(255) NOT NULL,
calendar_date date NULL,
calendar_level int4 NOT NULL,
calendar_level_name varchar NULL,
year_number int4 NULL,
month_number int4 NULL
);
-- cntr_m2.calendar foreign keys
- Функция:
CREATE OR REPLACE FUNCTION cntr_m2.f_get_year(p_id_year_in integer DEFAULT NULL::integer)
RETURNS refcursor
LANGUAGE plpgsql
AS $function$
declare
ref refcursor;
begin
open ref for
select c.id_calendar as id_year,
c.year_number
from cntr_m2.calendar c
where c.id_calendar = coalesce(p_id_year_in, c.id_calendar)
and c.calendar_level_name = 'year';
return ref;
end;
$function$
;
- Класс сущностей:
package com.example.demo.model;
import java.util.Objects;
public class MyCalendarDto {
private Integer idYear;
private Integer yearNumber;
public MyCalendarDto() {
}
public Integer getIdYear() {
return idYear;
}
public void setIdYear(Integer idYear) {
this.idYear = idYear;
}
public Integer getYearNumber() {
return yearNumber;
}
public void setYearNumber(Integer yearNumber) {
this.yearNumber = yearNumber;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MyCalendarDto that = (MyCalendarDto) o;
return Objects.equals(idYear, that.idYear) amp;amp; Objects.equals(yearNumber, that.yearNumber);
}
@Override
public int hashCode() {
return Objects.hash(idYear, yearNumber);
}
}
- Хранилище
package com.example.demo.repository;
import com.example.demo.model.MyCalendarDto;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.jdbc.core.simple.SimpleJdbcCall;
import org.springframework.stereotype.Repository;
import javax.annotation.PostConstruct;
import javax.sql.DataSource;
import java.util.*;
@Repository
public class JdbcMyCalendarRepository implements MyCalendarRepository{
@Autowired
private DataSource dataSource;
private JdbcTemplate jdbcTemplate;
private SimpleJdbcCall simpleJdbcCall;
@PostConstruct
private void postConstruct(){
this.jdbcTemplate = new JdbcTemplate(dataSource);
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
jdbcTemplate.setResultsMapCaseInsensitive(true);
this.simpleJdbcCall = new SimpleJdbcCall(jdbcTemplate)
.withSchemaName("cntr_m2")
.withProcedureName("f_get_year")
.returningResultSet("#result-set-1",
BeanPropertyRowMapper.newInstance(MyCalendarDto.class));
}
@Override
public List<MyCalendarDto> findMyCalendars(Integer id) {
SqlParameterSource parameters = new MapSqlParameterSource()
.addValue("p_id_year_in", id);
Map out = simpleJdbcCall.execute(parameters);
if (out == null){
return Collections.emptyList();
}
return (List) out.get("#result-set-1");
}
}
Комментарии:
1. Вы возвращаете результат с фиксированным количеством столбцов. Почему бы не сделать это функцией возврата набора и не использовать
select * from f_get_year()
вместо этого? Тогда вы «просто» запускаете SELECT в Spring JDBC2. Я должен следовать правилам и использовать функции, которые уже существуют.
Ответ №1:
К сожалению, у Spring нет возможности угадать сопоставление между столбцами вашей процедуры и атрибутами POJO, за исключением случаев, когда их имена на 100% идентичны, поэтому вам нужно явно указать, как выполнить сопоставление между ними с помощью RowMapper<Your_POJO>
Здесь вы можете найти пример того, как это сделать. (см. 1.1 Пользовательский формат строк)
https://mkyong.com/spring/spring-jdbctemplate-querying-examples/
Ответ №2:
Это работает так по какой-то причине:
@Repository
public class JdbcMyCalendarRepository implements MyCalendarRepository{
@Autowired
private DataSource dataSource;
private JdbcTemplate jdbcTemplate;
private SimpleJdbcCall simpleJdbcCall;
@PostConstruct
private void postConstruct(){
jdbcTemplate = new JdbcTemplate(dataSource);
jdbcTemplate.setResultsMapCaseInsensitive(true);
simpleJdbcCall = new SimpleJdbcCall(jdbcTemplate)
.withSchemaName("cntr_m2")
.withProcedureName("f_get_year")
.declareParameters(
new SqlParameter("p_id_year_in", Types.INTEGER))
.withoutProcedureColumnMetaDataAccess()
.returningResultSet("calendars",
BeanPropertyRowMapper.newInstance(MyCalendarDto.class));
}
@Override
public List<MyCalendarDto> findMyCalendars(Integer id) {
Map out = simpleJdbcCall.execute(new MapSqlParameterSource().addValue("p_id_year_in", id));
if (out == null){
return Collections.emptyList();
}
return (List) out.get("calendars");
}
}