Ho questa tabella:
CREATE TABLE IF NOT EXISTS client (
id BIGSERIAL NOT NULL,
ip VARCHAR(39) NOT NULL,
data TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT client_pk PRIMARY KEY(id),
CONSTRAINT client_uk UNIQUE (ip)
);
Ho aggiunto al database questi record:
@Test
public void questotestnonfunziona(){
System.out.println("");
System.out.println("4) trovaIpTraDateEdOre()");
System.out.println("");
GestioneDataOra gestioneDataOra = new GestioneDataOra();
clientRepository.cancellaTutti();
clientRepository.inserisciClientConData(
new Client(
0L,
"ip-da-non-conteggiare-1",
gestioneDataOra.dataOraDaStringa(
"2000-12-01 05 +02:00",
"yyyy-MM-dd HH ZZZZZ"
)
)
);
clientRepository.inserisciClientConData(
new Client(
0L,
"ip-da-non-conteggiare-2",
gestioneDataOra.dataOraDaStringa(
"2000-12-01 06 +02:00",
"yyyy-MM-dd HH ZZZZZ"
)
)
);
clientRepository.inserisciClientConData(
new Client(
0L,
"ip-da-non-conteggiare-3",
gestioneDataOra.dataOraDaStringa(
"2000-12-01 07 +02:00",
"yyyy-MM-dd HH ZZZZZ"
)
)
);
for(int i=1; i<=5; i++){clientRepository.inserisciClient(new Client(0L,"ip-da-trovare"+i,null));}
OffsetDateTime dataOraInizialeInclusa = gestioneDataOra.dataOraDaStringa(
"2021-10-05 01:00:00 +02:00",
"yyyy-MM-dd HH:mm:ss ZZZZZ");
System.out.println(dataOraInizialeInclusa);
OffsetDateTime dataOraFinaleEsclusa = gestioneDataOra.dataOraDaStringa(
"3000-10-05 01:00:00 +02:00",
"yyyy-MM-dd HH:mm:ss ZZZZZ");
System.out.println(dataOraFinaleEsclusa);
assertTrue(clientRepository.trovaIpTraDateEdOre(dataOraInizialeInclusa,dataOraFinaleEsclusa).equals(5L));
clientRepository.cancellaTutti();
assertTrue(clientRepository.numeroClient()!=null && clientRepository.numeroClient().equals(0L));
}
Il database che ottengo è questo:
1,ip-da-non-conteggiare-1,2000-12-01 03:00:00.000000
2,ip-da-non-conteggiare-2,2000-12-01 04:00:00.000000
3,ip-da-non-conteggiare-3,2000-12-01 05:00:00.000000
4,ip-da-trovare1,2021-10-11 13:07:20.873068
5,ip-da-trovare2,2021-10-11 13:07:20.873849
6,ip-da-trovare3,2021-10-11 13:07:20.874633
7,ip-da-trovare4,2021-10-11 13:07:20.875814
8,ip-da-trovare5,2021-10-11 13:07:20.876532
Se lancio questa query con l'IDE:
SELECT COUNT(*) FROM client WHERE data >= TIMESTAMP WITH TIME ZONE '2021-10-05 01:00:00 +02:00' AND data < TIMESTAMP WITH TIME ZONE '3000-10-05 01:00:00 +02:00';
ottengo 5.
Ma se provo a fare la stessa cosa con jdbcTemplate ottengo un errore (
ovviamente togliendo il try/catch).
@Transactional(readOnly=true)
public Long trovaIpTraDateEdOre(OffsetDateTime dataOraInizialeInclusa, OffsetDateTime dataOraFinaleEsclusa) {
try{
String sql = "SELECT COUNT(*) FROM client WHERE " +
"data >= TIMESTAMP WITH TIME ZONE ? AND data < TIMESTAMP WITH TIME ZONE ?;";
int[] tipi = {Types.TIMESTAMP_WITH_TIMEZONE, Types.TIMESTAMP_WITH_TIMEZONE};
return jdbcTemplate.queryForObject(sql, Long.class, tipi);
}catch (Exception e){
return 0L;
}
}
org.springframework.dao.DataIntegrityViolationException: PreparedStatementCallback; SQL [SELECT COUNT(*) FROM client WHERE data >= TIMESTAMP WITH TIME ZONE ? AND data < TIMESTAMP WITH TIME ZONE ?;]; Nessun valore specificato come parametro 2.; nested exception is org.postgresql.util.PSQLException: Nessun valore specificato come parametro 2.
Perché il metodo trovaIpTraDateEdOre() fallisce?
Dove sbaglio?
Io ho capito questo (correggetemi se sbaglio):
'TIMESTAMP WITH TIME ZONE' corrisponde a 'OffsetDateTime' ma usa come fuso orario '+00:00' ovvero l'ora UTC. In pratica se l'oggetto OffsetDateTime trasmette al DBMS come orario 15:07 ed ha un fuso orario impostato su "Europe/Rome" ovvero +02:00, in PostgreSQL l'ora diventa 13:07 ovvero 15:07 +02:00. In altri periodi dell'anno, in Italia, quando il fuso orario cambia +02:00 diventa +01:00.
'CURRENT_TIMESTAMP' prende l'ora dal server su cui è installato PostgreSQL. Quando sono in locale e quindi in sviluppo PostgreSQL preleva dal mio OS l'ora 15:07, toglie 2 ore di fuso orario sulla base della data odierna e salva nel DBMS l'ora UTC ovvero quella priva di fuso orario o con fuso orario nullo.
L'oggetto in arrivo da PostgreSQL con jdbcTemplate è Timestamp e va convertito in OffsetDateTime mentre quello fornito a jdbcTemplate deve essere OffsetDateTime.
Ho anche provato a fornire per logica a jdbcTemplate un Timestamp ma non ho risolto (ottengo lo stesso errore).
public class Client {
private Long id;
private String ip;
private OffsetDateTime data;
public Client() {
}
public Client(Long id, String ip, OffsetDateTime data) {
this.id = id;
this.ip = ip;
this.data = data;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
public OffsetDateTime getData() {
return data;
}
public void setData(OffsetDateTime data) {
this.data = data;
}
}
public class ClientRowMapper implements RowMapper<Client> {
GestioneDataOra gestioneDataOra = new GestioneDataOra();
@Override
public Client mapRow(ResultSet rs, int rowNum) throws SQLException {
Client client = new Client();
client.setId(rs.getLong("id"));
client.setIp(rs.getString("ip"));
client.setData(gestioneDataOra.daTimestampAdOffsetDateTime(rs.getTimestamp("data")));
return client;
}
}
public OffsetDateTime daTimestampAdOffsetDateTime(Timestamp timestamp){
try{
OffsetDateTime offsetDateTime = OffsetDateTime.ofInstant(timestamp.toInstant(), ZoneId.of(zona));
return offsetDateTime;
}catch (Exception e){
return null;
}
}