Encapsulation d'une date dans un Bean

A partir du moment où l'on utilise Spring pour développer une application web (MVC), on est confronté tôt ou tard à la problématique des backing beans Spring.

Un backing bean est un simple objet Java encapsulant des données métier qui va servir à réaliser un mapping automatisé d'un côté vers une base de données, et de l'autre vers un formulaire HTML. C'est cette dernière utilisation qui va nous intéresser aujourd'hui.

public class User {

  private String name = null;
  private String firstName = null;
  private Date birthDate = null;

  public User() {
  }

  public String getName() {
    return this.name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public String getFirstName() {
    return this.firstName;
  }

  public void setFirstName(String firstName) {
    this.firstName = firstName;
  }

  public Date getBirthDate() {
    return this.birthDate;
  }

  public void setBirthDate(Date birthDate) {
    this.birthDate = birthDate;
  }

}

Nous avons ici un objet classique User qui encapsule une date de naissance en utilisant un objet Date.
La prochaine étape consiste à créer un formulaire pour ajouter un utilisateur.

<form:form>
  <table>
    <tr>
      <td><label>Nom :</label></td>
      <td><form:input path="name"></form:input></td>
    </tr>
    <tr>
      <td><label>Prénom :</label></td>
      <td><form:input path="firstName"></form:input></td>
    </tr>
    <tr>
      <td><label>Date de naissance :</label></td>
      <td><form:input path="birthDate"></form:input></td>
    </tr>
  </table>
  <input type="submit" value="Créer" />
</form:form>

Si on utilise une telle méthode, alors on va être confronté à deux problèmes très rapidement :
  • Comment Spring va convertir automatiquement une date au format jj/mm/aaaa vers un objet Date ?
  • Comment Spring va convertir automatiquement un objet Date au format jj/mm/aaaa pour l'afficher dans le formulaire ?
Pour répondre au premier besoin, il existe ce qu'on appelle des CustomBeanEditor. Ce sont des objets spéciaux permettant de décrire comment convertir une chaîne de caractères vers un objet complexe (un objet Date dans notre cas).

Toutefois, ces CustomBeanEditor ne répondent pas au second problème. De plus, si nous avons besoin d'utiliser des format différents entre l'affichage texte (jj/mm/aaaa), le stockage dans la base de données (aaaammjj) et éventuellement une saisie utilisateur nous ne pouvons pas utiliser plusieurs CustomBeanEditor pour le même type java.util.Date.

Commençons par écrire un objet spécifique qui sera chargé d'encapsuler un objet Date et qui saura le convertir vers différents formats.

public class DateDay {

  private static DateFormat textFormat = new SimpleDateFormat("dd/MM/yyyy");
  private static DateFormat dbFormat = new SimpleDateFormat("yyyyMMdd");

  private Date date = null;

  // Constructeur public
  public DateDay() {
    this.date = new Date();
  }

  // Constructeur privé
  private DateDay(Date date) {
    this.date = date;
  }

  public Date getDate() {
    return this.date;
  }

  public String toTextString() {
    return textFormat.format(this.date);
  }

  public String toDbString() {
    return dbFormat.format(this.date);
  }

  public static DateDay parseText(String src) {
    try {
      return new DateDay(textFormat.parse(src));
    }
    catch(ParseException e) {
      throw new RuntimeException("Bad format");
    }
  }

  public static DateDay parseDb(String src) {
    try {
      return new DateDay(dbFormat.parse(src));
    }
    catch(ParseException e) {
      throw new RuntimeException("Bad format");
    }
  }
}

Il ne reste plus qu'à utiliser à bon escient ce nouvel objet en modifiant notre classe User.

public class User {
  
  private DateDay birthDate = null;
  ...

  public User() {
    this.birthDate = new DateDay();
  }

  ...

  public String getBirthDateText() {
    return birthDate.toTextString();
  }

  public void setBirthDateText(String birthDateText) {
    this.birthDate = DateDay.parseText(birthDateText);
  }

}

La dernière étape consiste à modifier très légèrement notre formulaire Spring pour prendre en compte ces changements.

...
    <tr>
      <td><label>Date de naissance :</label></td>
      <td><form:input path="birthDateText"></form:input></td>
    </tr>
...