Lorsqu'on utilise des objets hibernate dans des IHM, jsf notamment, il arrive que les chaînes de caractères ne contiennent que des espaces ou pire, qu'elles soient vides mais non null. Cela peut poser problème notamment lorsqu'un attribut n'est pas censé être null (nullable a false) ou qu'il y a des contraintes d'unicité. Pour résoudre ce souci, nous allons créer notre propre type de donnée hibernate, que nous appellerons "notEmptyString". L'idée est de retourner "null" si la chaîne est vide.
Attention, un type hibernate n'est pas un type de données java, et ce n'est pas non plus un type de données SQL. C'est un convertisseur qui traduit des types Java en types SQL et vice versa.
package fr.javapowa.hibernate.type;
import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import org.hibernate.Hibernate;
import org.hibernate.HibernateException;
import org.hibernate.usertype.UserType;
public class NotEmptyString implements UserType {
public int[] sqlTypes() {
return new int[] { Types.VARCHAR };
}
public Class returnedClass() {
return String.class;
}
public boolean equals(final Object _x, final Object _y) {
if (_x == _y) {
return true;
}
if ((_x == null) || (_y == null)) {
return false;
}
return _x.equals(_y);
}
public Object deepCopy(final Object _x) {
return _x;
}
public boolean isMutable() {
return false;
}
public Object nullSafeGet(final ResultSet rs, final String[] _names, final Object _owner)
throws HibernateException, SQLException {
return Hibernate.STRING.nullSafeGet(rs, _names[0]);
}
public void nullSafeSet(final PreparedStatement _st, final Object _value, final int _index)
throws HibernateException, SQLException {
if (_value != null) {
final String v = this.escape((String) _value);
Hibernate.STRING.nullSafeSet(_st, v, _index);
} else {
Hibernate.STRING.nullSafeSet(_st, null, _index);
}
}
public Object assemble(final Serializable _arg0, final Object _arg1) throws HibernateException {
return this.deepCopy(_arg0);
}
public Serializable disassemble(final Object _value) {
return (Serializable) this.deepCopy(_value);
}
private String escape(final String _string) {
if (_string == null || _string.trim().length() == 0) {
return null;
}
return _string;
}
public int hashCode(final Object _arg0) throws HibernateException {
return _arg0.hashCode();
}
public Object replace(final Object _arg0, final Object _arg1, final Object _arg2)
throws HibernateException {
return this.deepCopy(_arg0);
}
}
Ce code est très fortement inspiré du type String. La principale différence se situe au niveau de la méthode "nullSafeSet" qui permet d'écrire la valeur de la chaîne dans le prepared statement. Ici nous nous contentons de vérifier si la chaîne est vide ou pas, puis nous déléguons le travail au type String. Notez que nous n'effectuons pas de vérification sur le nullSafeGet, puisqu'aucune chaîne de caractères vide n'a pu être enregistrée. Le reste des méthodes se passe de commentaires. Remarquez simplement que le type est immuable.
Ensuite, il faut déclarer un fichier de mapping, que nous appellerons notEmptyString.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<typedef
name="notEmptyString"
class="fr.javapowa.hibernate.type.NotEmptyString" />
</hibernate-mapping>
N'oubliez pas de déclarer ce fichier là où vous déclarez votre mapping. Avec spring cela donne :
<!-- Hibernate SessionFactory -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" />
<property name="hibernateProperties">
<props>
....
</props>
</property>
<property name="mappingResources">
<list>
<value>notEmptyString.hbm.xml</value>
....
</list>
</property>
<property name="annotatedClasses">
<list>
....
</list>
</property>
<property name="dataSource" ref="dataSource" />
</bean>
Enfin, il ne nous reste plus qu'à utiliser le type de données lors de la déclaration des attributs. Si vous utilisez les annotations :
@Basic(fetch = FetchType.EAGER)
@Column(name = "name", length = 255, nullable = true)
@Type(type = "notEmptyString")
private String name;
Ou si vous utilisez des fichiers hbm :
<property name="nom" type="notEmptyString" nullable="true" />
Notez que vous pouvez utiliser directement le nom pleinement qualifié de la classe plutôt que le nom du type (ici fr.javapowa.hibernate.type.NotEmptyString).
Et voila. Vous ne pourrez plus avoir de chaine de caractères vide dans votre base.
[+/-] Read More...
[+/-] Summary only...