Máscaras com Hibernate

0 respostas
fviana

Máscaras com Hibernate

1 - Introdução
Em um projeto de migração, deparei-me com um problema comum, mas de difícil resolução (utilizando muita ‘gambiarra’). Recebi um damp de um banco de dados com várias tabelas, uma delas, TB_ENDERECO, possuía uma coluna de CEP, porém esta era do tipo numérica no banco, mas deveria ser sempre visualizada com a máscara 99999-999.
Criei então um UserType do hibernate, responsável pela formatação da máscara ao recuperar e remoção da máscara ao gravar o valor.

2 - Construção do utilitário
O primeiro passo é construir o utilitário que irá adicionar e remover a máscara.

package br.com.guj.hibernate;
/**
 * Classe utilitária com métodos utilizados para formatar e remover formato
 * @author fabio.viana
 */
public class MaskUtils{
	/**
	 * Método que remove a mascara da string
	 */
	public static String removeMask(String numero) {
		if (StringUtils.isNotEmpty(numero)) {
			numero = numero.replaceAll("/", "");
			numero = numero.replaceAll(".", "");
			numero = numero.replaceAll("-", "");
			numero = numero.replaceAll("\\", "");
			numero = numero.replaceAll("_", "");
			numero = numero.replaceAll("(", "");
			numero = numero.replaceAll(")", "");
			numero = numero.replaceAll(" ", "");
		}
		return numero;
	}
	/**
	 * Método que adiciona a mascara na string
	 */
	public static String addMask(String pMask, String pValue){
		if (pValue == null || pValue.trim().equals(""))
			return null;
		
		for(int i = 0; i < pValue.length(); i++){
			pMask = pMask.replaceFirst("#", pValue.substring(i, i + 1));
		}
		
		return pMask.replaceAll("#", "");
	}
}

3 - O Tipo Formatador
Para criar um tipo de dados para o hibernate basta criar uma classe implementando a interface org.hibernate.usertype.UserType. Nela há métodos que lê e grava o valor diretamente do banco de dados, além de outros métodos auxiliares.
Para nosso formatador ficar bem reutilizável teremos que implementar outra interface, a org.hibernate.usertype.ParameterizedType, que indica pro Factory do hibernate que o type possue parâmetros que devem ser enviados para ele.

package br.com.guj.hibernate;

import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Properties;

import org.apache.commons.beanutils.BeanUtilsBean;
import org.apache.commons.lang.StringUtils;
import org.hibernate.HibernateException;
import org.hibernate.usertype.ParameterizedType;
import org.hibernate.usertype.UserType;
/**
 * @author fabio.viana
 */
public class MaskFormatType implements UserType, ParameterizedType {
	/**
	 * Máscara utilizada
	 */
	private String mask = null;
	/**
	 * Tipo que será setado no PreparedStatement
	 */
	private Class classType = Long.class;
	/**
	 * Tipo de dado da coluna
	 */
	private int type = Types.NUMERIC;
	
	public MaskFormatType(){
		
	}
	/**
	 * Tipo de dado da coluna
	 */
	public int[] sqlTypes() {
		return new int[] { type };
	}
	/**
	 * Tipo que o UserType retorna
	 */
	public Class returnedClass() {
		return String.class;
	}
	/**
	 * Compara dois valores formatados
	 */
	public boolean equals(Object obj0, Object obj1) throws HibernateException {
		String x = (String) obj0;
		String y = (String) obj1;
		if (x == null){
			x = "";
		}
		if (y == null){
			y = "";
		}
		return x.equals(y);
	}
	/**
	 * Obtem o valor do banco, adiciona a máscara e retorna o valor formatado
	 */
	public Object nullSafeGet(ResultSet rs, String[] names, Object owner) throws HibernateException, SQLException {
		try {
			Object value = rs.getObject(names[0]);
			if (value == null)
				return null;
			
			String valueStr = BeanUtilsBean.getInstance().getConvertUtils().convert(value);
			return MaskUtils.addMask(mask, valueStr);
		} catch (Exception e) {
			return null;
		}
	}
	/**
	 * Pega o valor com a máscara, remove a máscara e seta no banco
	 */
	public void nullSafeSet(PreparedStatement st, Object value, int index) throws HibernateException, SQLException {
		Object valSet = BeanUtilsBean.getInstance().getConvertUtils().convert(MaskUtils.removeMask((String)value), classType);
		
		if (valSet == null)
			st.setNull(index, type);
		else
			st.setObject(index, valSet);
	}
	/**
	 * Copia uma nova instancia do valor
	 */
	public Object deepCopy(Object value) throws HibernateException {
		if (value == null)
			return null;
		else
			return new String(value.toString());
	}
	/**
	 * Indica que o valor é mutável
	 */
	public boolean isMutable() {
		return true;
	}
	/**
	 * Serializa o valor original
	 */
	public Serializable disassemble(Object value) throws HibernateException {
		return (String)value;
	}
	/**
	 * Deserializa o valor em cache
	 */
	public Object assemble(Serializable cached, Object owner) throws HibernateException {
		return cached;
	}
	/**
	 * Retorna um possível subistituto
	 */
	public Object replace(Object original, Object target, Object owner) throws HibernateException {
		return original;
	}
	/**
	 * Retorna o hashCode do valor formatado
	 */
	public int hashCode(Object obj) throws HibernateException {
		return obj.hashCode();
	}
	/**
	 * Seta os parametros de configuração da máscara
	 */
	public void setParameterValues(Properties props) {
		this.mask = (String) props.get("mask");
		String classType = (String) props.get("classType");
		if (StringUtils.isNotEmpty(classType)){
			try {
				this.classType = (Class) Class.forName(classType);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		String type = (String) props.get("type");
		if (StringUtils.isNotEmpty(type)){
			try {
				this.type = Integer.parseInt(type);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
}

Neste formatador existe 3 parametros, a máscara, o classType (tipo a ser setado para gravação) e o type (tipo da coluna no banco).
Obs.: Observe que estou utilizando a api commons-beans para a conversão de tipos, que pode ser baixado no site da Apache.

4 - Utilização

/**
 * @author fabio.viana
 */
@Entity
public class Endereco implements Serializable{
	@Id
	@OneToOne
	private Pessoa pessoa;
	
	@Type (type="br.com.guj.hibernate.MaskFormatType", parameters={@Parameter(name="mask", value="#####-###") , @Parameter(name="classType", value="java.lang.Long"), @Parameter(name="type", value="2") })
	private String cep;
	
	...
}

Repare que o cep é do tipo string, mas no banco o tipo é numérico (2 representa o tipo NUMERIC - ver java.sql.Types), sendo registrado pelo parâmetro ‘type’, e no hibernate (na verdade no PreparedStatement) o tipo a ser instanciado e gravado é java.lang.Long, sendo registrado pelo parâmetro classType.
Sendo assim, ao recuperar um Endereço, a propriedade cep virá com a máscara, e ao salvar um Endereço, a coluna cep receberá um número.

5 - Conclusão
Este é um mero exemplo de utilização de UserType do hibernate. Em vários casos podemos utilizar este recurso para facilitar o desenvolvimento. Nesta mesma migração, criamos também um UserType para criar uma lista a partir de um campo varchar2 que armazena telefones no mesmo campo utilizando; como separador.

Então, é só…

Criado 28 de fevereiro de 2007
Respostas 0
Participantes 1