web-dev-qa-db-fra.com

Utilisation de setDate dans PreparedStatement

Afin de rendre notre code plus standard, il nous a été demandé de changer tous les emplacements où nous avons codé en dur nos variables SQL en instructions préparées et de les lier à la place.

Je suis cependant confronté à un problème avec le setDate().

Voici le code:

        DateFormat dateFormatYMD = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
        DateFormat dateFormatMDY = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss");
        Date now = new Date();
        String vDateYMD = dateFormatYMD.format(now);
        String vDateMDY = dateFormatMDY.format(now);
        String vDateMDYSQL =  vDateMDY ;
        Java.sql.Date date = new Java.sql.Date(0000-00-00);

   requestSQL = "INSERT INTO CREDIT_REQ_TITLE_ORDER (REQUEST_ID," + 
                " ORDER_DT, FOLLOWUP_DT) " +  "values(?,?,?,)";


                prs = conn.prepareStatement(requestSQL);

                prs.setInt(1,new Integer(requestID));

                prs.setDate(2,date.valueOf(vDateMDYSQL));
                prs.setDate(3,date.valueOf(sqlFollowupDT));

Je reçois cette erreur lorsque le SQL est exécuté:

    Java.lang.IllegalArgumentException
    at Java.sql.Date.valueOf(Date.Java:138)
    at com.cmsi.eValuate.TAF.TAFModuleMain.CallTAF(TAFModuleMain.Java:1211)

Devrais-je utiliser setString() à la place avec un to_date()?

43
roymustang86

Utilisation de Java.sql.Date

Si votre table a une colonne de type DATE:

  • Java.lang.String

    La méthode Java.sql.Date.valueOf(Java.lang.String) a reçu une chaîne représentant une date au format yyyy-[m]m-[d]d. par exemple.:

    ps.setDate(2, Java.sql.Date.valueOf("2013-09-04"));
    
  • Java.util.Date

    Supposons que vous ayez une variable endDate de type Java.util.Date, Vous effectuez la conversion comme suit:

    ps.setDate(2, new Java.sql.Date(endDate.getTime());
    
  • Actuel

    Si vous souhaitez insérer la date du jour:

    ps.setDate(2, new Java.sql.Date(System.currentTimeMillis()));
    
    // Since Java 8
    ps.setDate(2, Java.sql.Date.valueOf(Java.time.LocalDate.now()));
    

Utilisation de Java.sql.Timestamp

Si votre table a une colonne de type TIMESTAMP ou DATETIME:

  • Java.lang.String

    La méthode Java.sql.Timestamp.valueOf(Java.lang.String) a reçu une chaîne représentant une date au format yyyy-[m]m-[d]d hh:mm:ss[.f...]. par exemple.:

    ps.setTimestamp(2, Java.sql.Timestamp.valueOf("2013-09-04 13:30:00");
    
  • Java.util.Date

    Supposons que vous ayez une variable endDate de type Java.util.Date, Vous effectuez la conversion comme suit:

    ps.setTimestamp(2, new Java.sql.Timestamp(endDate.getTime()));
    
  • Actuel

    Si vous avez besoin de l'horodatage actuel:

    ps.setTimestamp(2, new Java.sql.Timestamp(System.currentTimeMillis()));
    
    // Since Java 8
    ps.setTimestamp(2, Java.sql.Timestamp.from(Java.time.Instant.now()));
    ps.setTimestamp(2, Java.sql.Timestamp.valueOf(Java.time.LocalDateTime.now()));
    
130
Paul Vargas

tl; dr

Avec JDBC 4.2 ou version ultérieure et Java 8 ou version ultérieure:

myPreparedStatement.setObject( … , myLocalDate  )

…et…

myResultSet.getObject( … , LocalDate.class )

Détails

La réponse de Vargas mentionne bien les types Java.time, mais fait uniquement référence à la conversion en Java.sql.Date. Pas besoin de convertir si votre pilote est mis à jour.

Java.time

Le cadre Java.time est intégré à Java 8 et versions ultérieures. Ces classes supplantent les anciennes classes de date-heure problématiques telles que Java.util.Date, .Calendar, & Java.text.SimpleDateFormat. L'équipe Joda-Time conseille également la migration vers Java.time.

Pour en savoir plus, voir le Tutoriel Oracle . Et recherchez Stack Overflow pour de nombreux exemples et explications.

Une grande partie de la fonctionnalité Java.time est rétroportée sur Java 6 & 7 in ThreeTen-Backport et est ensuite adaptée pour Android dans ThreeTenABP .

LocalDate

En Java.time, le Java.time.LocalDate classe représente une valeur de date uniquement sans heure et sans fuseau horaire.

Si vous utilisez un pilote JDBC conforme à JDBC 4.2 ou une spécification ultérieure, inutile d'utiliser l'ancien Java.sql.Date classe. Vous pouvez passer/récupérer les objets LocalDate directement vers/depuis votre base de données via PreparedStatement::setObject et ResultSet::getObject .

LocalDate localDate = LocalDate.now( ZoneId.of( "America/Montreal" ) );
myPreparedStatement.setObject( 1 , localDate  );

…et…

LocalDate localDate = myResultSet.getObject( 1 , LocalDate.class );

Avant JDBC 4.2, convertissez

Si votre pilote ne peut pas gérer directement les types Java.time, passez à la conversion en types Java.sql. Mais minimisez leur utilisation, avec votre logique métier utilisant uniquement des types Java.time.

De nouvelles méthodes ont été ajoutées aux anciennes classes pour la conversion vers/à partir de types Java.time. Pour Java.sql.Date voir les méthodes valueOf et toLocalDate .

Java.sql.Date sqlDate = Java.sql.Date.valueOf( localDate );

…et…

LocalDate localDate = sqlDate.toLocalDate();

Placeholder value

Méfiez-vous de l'utilisation de 0000-00-00 comme valeur de marque de réservation, comme indiqué dans le code de votre question. Toutes les bases de données et autres logiciels ne permettent pas de remonter aussi loin dans le temps. Je suggère d'utiliser quelque chose comme le plus communément utilisé date de référence Epoch Unix/Posix de 1970, 1970-01-01.

LocalDate Epoch_DATE = LocalDate.ofEpochDay( 0 ); // 1970-01-01 is day 0 in Epoch counting.

À propos de Java.time

Le cadre Java.time est intégré à Java 8 et versions ultérieures. Ces classes supplantent l'ancien problème - legacy classes date-heure telles que Java.util.Date , Calendar , & SimpleDateFormat .

Le projet Joda-Time , actuellement en mode de maintenance , conseille de migrer vers le Java.time = cours.

Pour en savoir plus, consultez le Oracle Tutorial . Et recherchez Stack Overflow pour de nombreux exemples et explications. La spécification est JSR 31 .

Vous pouvez échanger des objets Java.time directement avec votre base de données. Utilisez un pilote JDBC compatible avec JDBC 4.2 ou une version ultérieure. Pas besoin de chaînes, pas besoin de Java.sql.* Des classes.

Où obtenir les classes Java.time?

Le projet ThreeTen-Extra étend Java.time avec des classes supplémentaires. Ce projet est un terrain d’essai pour d’éventuels ajouts à Java.time. Vous pouvez trouver ici quelques classes utiles telles que Interval , YearWeek , YearQuarter , et plus .

15
Basil Bourque

Le problème que vous rencontrez est que vous passez des formats incompatibles à partir d'un fichier Java.util.Date formaté pour construire une instance de Java.sql.Date, Qui ne se comporte pas de la même manière lorsque vous utilisez valueOf() car ils utilisent des formats différents.

Je vois aussi que vous souhaitez conserver les heures et les minutes et je pense que vous feriez mieux de changer le type de données en Java.sql.Timestamp, Qui prend en charge les heures et les minutes, ainsi que de changer le champ de votre base de données en DATETIME ou similaire (selon votre fournisseur de base de données).

En tout cas, si vous voulez changer de Java.util.Date to Java.sql.Date, Je suggère d'utiliser

Java.util.Date date = Calendar.getInstance().getTime();
Java.sql.Date sqlDate = new Java.sql.Date(date.getTime()); 
// ... more code here
prs.setDate(sqlDate);
3
Cristian Meneses

Le docs dit explicitement que Java.sql.Date jettera:

  • IllegalArgumentException - si la date donnée n'est pas au format d'échappement de date JDBC (yyyy-[m]m-[d]d)

De plus, vous ne devriez pas avoir besoin de convertir une date en String puis en sql.date, cela semble superflu (et sujet aux bogues!). Au lieu de cela, vous pourriez:

Java.sql.Date sqlDate := new Java.sql.Date(now.getTime());
prs.setDate(2, sqlDate);
prs.setDate(3, sqlDate);
2
Vincent Malgrat

Si vous souhaitez ajouter la date actuelle dans la base de données, j’éviterai de calculer la date dans Java pour commencer. Déterminer "maintenant" sur le Java ( côté client) conduit à des incohérences éventuelles dans la base de données si le côté client est mal configuré, a une heure incorrecte, un fuseau horaire incorrect, etc. À la place, la date peut être définie du côté serveur de la manière suivante:

requestSQL = "INSERT INTO CREDIT_REQ_TITLE_ORDER ("   +
                "REQUEST_ID, ORDER_DT, FOLLOWUP_DT) " +
                "VALUES(?, SYSDATE, SYSDATE + 30)";

...

prs.setInt(1, new Integer(requestID));

De cette façon, un seul paramètre de liaison est requis et les dates calculées du côté serveur seront cohérentes. Encore mieux serait d’ajouter un déclencheur d’insertion à CREDIT_REQ_TITLE_ORDER et que le déclencheur insère les dates. Cela peut aider à renforcer la cohérence entre différentes applications clientes (par exemple, une personne essayant de résoudre un problème via sqlplus.

1
Glenn