Menu

Guardar null en lugar de cadena vacía

2014-07-27
2014-07-28
  • Granjero Moe

    Granjero Moe - 2014-07-27

    OX guarda cadena vacía '' en la base de datos para los campos de tipo string que no han sido rellenados por el usuario.
    Esto es un problema si el campo tiene la constraint unique, al menos en PostgreSQL.
    El problema es que sólo te deja guardar una fila con el campo en cuestión con cadena vacía (porque es unique) mientras que si lo guardas con null puede haber más de una. Es decir el problema se da si tienes campos definidos así:

        @Stereotype("EMAIL")
        @Column(nullable = true, unique = true)
        private String correoElectronico;
    

    No sé si existe alguna forma de configurar OpenXava para que guarde null en lugar de cadena vacía.
    Yo les dejo mi solución por si alguien la necesita:

    Todas mis entidades heredan de una clase donde tengo definida mi primary key. En ella he definido un método que recorre todos los atributos de la clase (la que esté usando el método, que serán las clases hijas) y si es de tipo String y está vacío cambia la '' por un null.
    Este método lo llamo cada vez que se guarda el objeto anotándolo con @PrePersist y @PreUpdate.

    @MappedSuperclass
    @Data
    public abstract class BaseEntity {
    
        /**
         * Primary Key
         */
        @Id 
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        @Hidden
        private int id;
    
        @PreUpdate
        private void preUpdate() {
            permitirNulos();
        }
    
        @PrePersist
        private void prePersist() {
            permitirNulos();
        }
    
        /**
         * Mediante este método se establece a null algunas propiedades cuando son
         * cadena vacía.<br>
         * Esto es necesario para que funcione la constraint unique.
         */
        private void permitirNulos() {
            PropertyUtilsBean pub = new PropertyUtilsBean();
            Map<String, Object> properties = null;
            try {
                properties = pub.describe(this);
                for (Entry<String, Object> e : properties.entrySet()) {
                    System.out.println( "campo '" + e.getKey() );
                    if (e.getValue() != null) {
                        System.out.println( "campo '" + e.getKey() + "' de tipo " + e.getValue().getClass().toString() + " es string? " + e.getValue().getClass().isInstance("") );
                        if ( e.getValue() instanceof String && Is.empty(e.getValue()) ) {
                            try {
                                BeanUtils.setProperty(this, e.getKey(), null);
                            } catch (IllegalAccessException e1) {
                                // TODO Auto-generated catch block
                                e1.printStackTrace();
                            } catch (InvocationTargetException e1) {
                                // TODO Auto-generated catch block
                                e1.printStackTrace();
                            }
                        }
                    }
                } 
            } catch (IllegalAccessException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            } catch (InvocationTargetException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            } catch (NoSuchMethodException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            }
    
        } // permitirNulos
    
    }
    
     
  • Federico Alcantara

    Saludos:

    Postgres (y probablemente otros manejadores) es estricto en cuanto a los tipos de datos. Nosotros lo resolvimos de manera similar, básicamente con un listener que se lo anotamos a la clase. Lo único es que damos un paso más y es que inicializamos aquellos campos que no son nulos con valores predeterminados. Esto es para aquellos que no son manejados por las vistas.

    Este es una parte de ese listener:

        @SuppressWarnings({ "rawtypes", "unchecked" })
        public void initialize(Object object) {
            Class c = object.getClass();
            Object value;
            Class valueType;
            List<Field> fields = new ArrayList<Field>();
            addFields(fields, c);
            for (Field field : fields) {
                String fieldName = field.getName();
                if (!fieldName.equalsIgnoreCase("recnum")) {
                    boolean isNullable = false;
                    boolean isBooleanEditor = false;
                    if (field.isAnnotationPresent(Column.class)) {
                        Column column = field.getAnnotation(Column.class);
                        isNullable = column.nullable();
                    }
                    if (field.isAnnotationPresent(Editor.class)) {
                        Editor editor = field.getAnnotation(Editor.class);
                        if (editor.value().equals("BooleanString")) {
                            isBooleanEditor = true;
                        }
                    } else if (field.isAnnotationPresent(Editors.class)) {
                        Editors editors = field.getAnnotation(Editors.class);
                        for (Editor editor : editors.value()) {
                            if (editor.value().equals("BooleanString")) {
                                isBooleanEditor = true;
                                break;
                            }
                        }
                    }
                    boolean isLob = field.isAnnotationPresent(Lob.class);
                    if (!isLob) {
                        String getMethodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
                        Method method;
                        try {
                            method = c.getDeclaredMethod(getMethodName, new Class[]{});
                            if (method.isAnnotationPresent(Lob.class)) {
                                isLob = true;
                            }
                        } catch (SecurityException e) {
                            LOG.trace(e.getMessage());
                        } catch (NoSuchMethodException e) {
                            LOG.trace(e.getMessage());
                        }
                    }
    
                    try {
                        value = PropertyUtils.getProperty(object, fieldName);
                        valueType = PropertyUtils
                            .getPropertyType(object, fieldName);
                        //TODO: Bug in hibernate 3.6 for lob fields
                        if (isLob &&
                                valueType.isInstance("") &&
                                Is.emptyString((String)value) ) {
                            PropertyUtils.setProperty(object, fieldName, " ");
                            continue;
                        }
                        if (value == null && !isNullable) {
                            // InactiveActiveStatusType defaults to ACTIVE
                            if (valueType.isInstance(InactiveActiveStatusType.ACTIVE)) {
                                PropertyUtils.setProperty(object, fieldName, InactiveActiveStatusType.ACTIVE);
                            } else if (valueType.isEnum()) { // other enums
                                PropertyUtils.setProperty(object, fieldName, valueType.getEnumConstants()[0]);
                            } 
    
                            // Objects
                            if (valueType.isInstance("")) {
                                if (isBooleanEditor) {
                                    PropertyUtils.setProperty(object, fieldName, "0");
                                }
                                PropertyUtils.setProperty(object, fieldName, "");
                                continue;
                            } 
                            if (valueType.isInstance(new Integer(0))) {
                                PropertyUtils.setProperty(object, fieldName, new Integer("0"));
                                continue;
                            }
                            if (valueType.isInstance(new BigInteger("0"))) {
                                PropertyUtils.setProperty(object, fieldName,
                                        new BigInteger("0"));
                                continue;
                            }
                            if (valueType.isInstance(new BigDecimal("0"))) {
                                PropertyUtils.setProperty(object, fieldName,
                                        new BigDecimal("0"));
                                continue;
                            }
                            if (valueType.isInstance(new Date(0))) {
                                PropertyUtils.setProperty(object, fieldName,
                                        DateUtils.INSTANCE.youngestDate());
                                continue;
                            }
                            if (valueType.isInstance(new Long(0))) {
                                PropertyUtils.setProperty(object, fieldName,
                                        new Long(0));
                                continue;
                            }
                            if (valueType.isInstance(new Short("0"))) {
                                PropertyUtils.setProperty(object, fieldName, new Short(
                                        "0"));
                                continue;
                            }
                            if (valueType.isInstance(new Boolean("false"))) {
                                PropertyUtils.setProperty(object, fieldName,
                                        new Boolean(false));
                                continue;
                            }
                            // Primitives
                            if (valueType.equals(byte.class)) {
                                PropertyUtils.setProperty(object, fieldName, 0);
                                continue;
                            }
                            if (valueType.equals(short.class)) {
                                PropertyUtils.setProperty(object, fieldName, 0);
                                continue;
                            }
                            if (valueType.equals(int.class)) {
                                PropertyUtils.setProperty(object, fieldName, 0);
                                continue;
                            }
                            if (valueType.equals(long.class)) {
                                PropertyUtils.setProperty(object, fieldName, 0L);
                                continue;
                            }
                            if (valueType.equals(float.class)) {
                                PropertyUtils.setProperty(object, fieldName, 0.0f);
                                continue;
                            }
                            if (valueType.equals(double.class)) {
                                PropertyUtils.setProperty(object, fieldName, 0.0d);
                                continue;
                            }
                            if (valueType.equals(char.class)) {
                                PropertyUtils.setProperty(object, fieldName, 0);
                                continue;
                            }
                            if (valueType.equals(boolean.class)) {
                                PropertyUtils.setProperty(object, fieldName, false);
                                continue;
                            }
    
                        }
                    } catch (IllegalAccessException e) {
                        LOG.trace(e);
                    } catch (InvocationTargetException e) {
                        LOG.trace(e);
                    } catch (NoSuchMethodException e) {
                        LOG.trace(e);
                    }
                }
            }
        }
    

    Espero les sea útil,

    Federico

     
  • Javier Paniza

    Javier Paniza - 2014-07-28

    Hola Granjero,

    OpenXava no hace nada por defecto para convertir nulos en blancos, pero si los datos que se cogen del formulario HTML están vacíos se almacenan como cadena vacía, por lo que los nulos no llegarían.

    Tu caso se puede resolver con un formateador. Añade en el editors.xml (o editores.xml) de tu aplicación lo siguiente:

    <editor url="textEditor.jsp">
        <formatter class="com.tuapp.formatters.EmptyStringAsNullFormatter" >
        <for-type type="java.lang.String" />
    </editor>
    

    Tendrías que implementar EmptyStringAsNullFormatter que es un IFormatter.


    Ayuda a otros en este foro como yo te ayudo a ti.

     

Log in to post a comment.