sexta-feira, 19 de dezembro de 2008

Máscara javascript para qualquer formato

Fazer máscaras em javascript é sempre chato e trabalhoso. Para cada máscara do sistema deve-se criar uma função javascript e utilizá-la no sistema. A algum tempo criei uma função javascript que funciona para a grande maioria das mascaras que são utilizadas no sistema. A função é mostrada abaixo:


/**
* Funcao de mascara. Esta funcao recebe como parametro o campo (this) no qual a
* mascara sera aplicada, o formato da mascara (99/99/9999), o conteúdo (1
* numeros, 2 letras e 3 letras e numeros) e o evento passa-se o this.
*/
function mask(campo, formato, conteudo, event) {

valor = campo.value;

var i, j;
var caracs = [ '.', '/', '-', ':', '(', ')', ',' ];
var auxPonto = formato;
var auxBarra = formato;
var auxHifen = formato;
var auxDblPonto = formato;
var auxAbrePar = formato;
var auxFechaPar = formato;
var auxVirgula = formato;
var tamanho = formato.length;
var posPonto = new Array(tamanho);
var posBarra = new Array(tamanho);
var posHifen = new Array(tamanho);
var posDblPonto = new Array(tamanho);
var posAbrePar = new Array(tamanho);
var posFechaPar = new Array(tamanho);
var posVirgula = new Array(tamanho);
var keyPress = event;

campo.maxLength = tamanho;

if (event.keyCode != 17) {
switch (conteudo) {
case 1: // Verifica se soh podem ser entrados valores numericos
if (!(event.keyCode >= 48 && (event.keyCode <= 57)))
event.keyCode = 0;
break;
case 2: // Somente Letras
if (!((event.keyCode >= 97 && event.keyCode <= 122)
|| event.keyCode >= 65
&& event.keyCode <= 90))
event.keyCode = 0;
break;
case 3: // Letras e numeros
if (!((event.keyCode >= 48 && event.keyCode <= 57)
|| (event.keyCode >= 97 && event.keyCode <= 122)
|| (event.keyCode >= 65 && event.keyCode <= 90)))
event.keyCode = 0;
break;
}
}

// ----------------------------- PEGA A FORMATACAO DA MASCARA
// ------------------
for (i = 0; i < tamanho; i++) {

posPonto[i] = auxPonto.indexOf('.');
posBarra[i] = auxBarra.indexOf('/');
posHifen[i] = auxHifen.indexOf('-');
posDblPonto[i] = auxDblPonto.indexOf(':');
posAbrePar[i] = auxAbrePar.indexOf('(');
posFechaPar[i] = auxFechaPar.indexOf(')');
posVirgula[i] = auxVirgula.indexOf(',');

auxPonto = auxPonto.substring(posPonto[i] + 1, tamanho);
auxBarra = auxBarra.substring(posBarra[i] + 1, tamanho);
auxHifen = auxHifen.substring(posHifen[i] + 1, tamanho);
auxDblPonto = auxDblPonto.substring(posDblPonto[i] + 1, tamanho);
auxAbrePar = auxAbrePar.substring(posAbrePar[i] + 1, tamanho);
auxFechaPar = auxFechaPar.substring(posFechaPar[i] + 1, tamanho);
auxVirgula = auxVirgula.substring(posVirgula[i] + 1, tamanho);

if (i > 0) {
posPonto[i] = posPonto[i] + posPonto[i - 1];
posBarra[i] = posBarra[i] + posBarra[i - 1];
posHifen[i] = posHifen[i] + posHifen[i - 1];
posDblPonto[i] = posDblPonto[i] + posDblPonto[i - 1];
posAbrePar[i] = posAbrePar[i] + posAbrePar[i - 1];
posFechaPar[i] = posFechaPar[i] + posFechaPar[i - 1];
posVirgula[i] = posVirgula[i] + posVirgula[i - 1];
posPonto[i] = posPonto[i] + 1;
posBarra[i] = posBarra[i] + 1;
posHifen[i] = posHifen[i] + 1;
posDblPonto[i] = posDblPonto[i] + 1;
posAbrePar[i] = posAbrePar[i] + 1;
posFechaPar[i] = posFechaPar[i] + 1;
posVirgula[i] = posVirgula[i] + 1;
}
}

// Retirando a máscara
for (i = 0; i < campo.value.length; i++) {
valor = valor.replace('-', '');
valor = valor.replace('(', '');
valor = valor.replace(')', '');
valor = valor.replace(':', '');
valor = valor.replace('/', '');
valor = valor.replace('.', '');
valor = valor.replace(',', '');
}

// Faz a validação se for apenas número
// utilizado para fazer a validação de Ctrl+V
if (conteudo == 1) {
if (isNaN(valor)) {
if (isNaN(valor.charAt(valor.length - 2)))
valor = "";
else
valor = valor.substring(0, valor.length - 1);
}
}

indicePonto = 0;
indiceBarra = 0;
indiceHifen = 0;
indiceDblPonto = 0;
indiceVirgula = 0;
indiceAbrePar = 0;
indiceFechaPar = 0;

// Varre o campo aplicando a máscara
for (i = 0; i < valor.length; i++) {
if (i == posPonto[indicePonto]) {
if (valor.charAt(i) != '.') {
if (i == 0) {
valor = '.' + valor;
} else if (i == valor.length) {
valor = valor + '.';
} else {
valor = valor.substring(0, i) + '.' + valor.substring(i);
}
indicePonto++;
}
}
if (i == posBarra[indiceBarra]) {
if (valor.charAt(i) != '/') {
if (i == 0) {
valor = '/' + valor;
} else if (i == valor.length) {
valor = valor + '/';
} else {
valor = valor.substring(0, i) + '/' + valor.substring(i);
}
indiceBarra++;
}
}

if (i == posHifen[indiceHifen]) {
if (valor.charAt(i) != '-') {
if (i == 0) {
valor = '-' + valor;
} else if (i == valor.length) {
valor = valor + '-';
} else {
valor = valor.substring(0, i) + '-' + valor.substring(i);
}
indiceHifen++;
}
}

if (i == posDblPonto[indiceDblPonto]) {
if (valor.charAt(i) != ':') {
if (i == 0) {
valor = ':' + valor;
} else if (i == valor.length) {
valor = valor + ':';
} else {
valor = valor.substring(0, i) + ':' + valor.substring(i);
}
indiceDblPonto++;
}
}

if (i == posAbrePar[indiceAbrePar]) {
if (valor.charAt(i) != '(') {
if (i == 0) {
valor = '(' + valor;
} else if (i == valor.length) {
valor = valor + '(';
} else {
valor = valor.substring(0, i) + '(' + valor.substring(i);
}
indiceAbrePar++;
}
}

if (i == posFechaPar[indiceFechaPar]) {
if (valor.charAt(i) != ')') {
if (i == 0) {
valor = ')' + valor;
} else if (i == valor.length) {
valor = valor + ')';
} else {
valor = valor.substring(0, i) + ')' + valor.substring(i);
}
indiceFechaPar++;
}
}

if (i == posVirgula[indiceVirgula]) {
if (valor.charAt(i) != ',') {
if (i == 0) {
valor = ',' + valor;
} else if (i == valor.length) {
valor = valor + ',';
} else {
valor = valor.substring(0, i) + ',' + valor.substring(i);
}
indiceVirgula++;
}
}
}

if (campo.value.length > tamanho) {
campo.value = campo.value.substring(0, tamanho);
}

campo.value = valor;
}


Segue abaixo um exemplo de como utilizar a função:




Para a máscara pode se passar qualquer mascara que possua os caracteres (ponto, barra, menos, dois pontos, virgula, e abre e fecha parenteses. Os valores que são passados entre os caracteres acima serão desconsiderados, então tanto faz colocar 999.999.999-99 quanto 111.111.111-11. Espero que esta máscara ajude vcs assim como vem me ajudando durante este longo período de desenvolvimento.

É isto aí pessoal. Qualquer problema mandem mensagem.

segunda-feira, 15 de dezembro de 2008

Palestra interessante no evento Maratona JBoss

Olá pessoal,

Fui em um evento a algum tempo aqui em SP e queria comentar sobre uma palestra que particularmente me chamou bastante atenção. Esta palestra foi sobre o JBoss Performance e foi ministrada pelo Bill Coutinho, funcionário da empresa Dextra.

Entre as principais lições da palestra posso listar as seguintes:
  • Por não saber aonde está os gargalos, nunca otimize cedo demais (Só a medição mostrará os gargalos).
  • Deve-se sempre fazer um paralelo entre a corretude versus a performance. (A corretude depende da aplicação. Para uma aplicação, ficar com dados antigos, pode ser um erro enquanto para outras pode ser tolerável desde que a aplicação fique mais rápida)
  • Escolhas sempre devem ser feitas. (Objetivos na sua grande maioria das vezes serão conflitantes como por exemplo, performance e segurança)
Foi falado também sobre as principais técnicas para o aumento da performance que podem ser:
  • Pool de recursos, como no exemplo do pool de conexões com o BD.
  • Caching, como no exemplo do cache de segundo nível do Hibernate (Neste caso deve-se analisar a corretude versus a performance)
  • Clustering como o exemplo do cluster do Jboss.
  • Melhorias nas configurações da JVM. (Evolução do Java 5 com o Ergonomics)
O evento em sí foi muito bom, mas gostaria de parabenizar o palestrante pela excelente apresentação. É isto aí pessoal.


sexta-feira, 14 de novembro de 2008

Mapeamento de objetos com JPA e Hibernate

Para auxiliar quem está começando com algum projeto resolvi explicar algumas poucas anotações para realizar o mapeamento de entidades utilizando JPA e Hibernate. Para se realizar a modelagem de classes persistentes e da base de dados pode se utilizar as anotações do JPA e do Hibernate.

Uma das anotações mais utilizadas é a anotação @Entity que define que aquela classe anotada deve ser uma entidade persistida na base de dados. Uma vez utilizada esta anotação, todos os métodos gets e sets e campos (dependendo do seu tipo de acesso) são descobertos e são gerados mapeamento para estas propriedades no banco de dados. Cada uma destas propriedades então gera uma coluna na tabela com o nome da entidade. Para que uma propriedade não seja persistida deve se utilizar a anotação @Transient.

Para se mapear objetos complexos devem ser usadas as anotações @OneToOne, @OneToMany, @ManyToOne e @ManyToMany. Quando mapeamos objetos com o tipo @ManyToMany o próprio JPA é responsável por gerar a tabela entre os dois objetos.

Quando se mapeia objetos deve se definir a política de quando estes objetos devem ser buscados. Normalmente quando se busca um objeto os outros objetos nem sempre precisam ser exibidos e buscá-los no banco causaria uma perda de desempenho indesejável. Um exemplo seria uma busca de pessoas que somente queremos mostrar seus dados e não seu endereço. Para este problema podemos mapear a propriedade como LAZY que indica que o objeto mapeado não é buscado inicialmente e sim somente depois dela ser necessária. Para isto definimos na anotação de mapeamento o trecho fetch = FetchType.LAZY. Para colocar propriedades simples LAZY deve se utilizar a seguinte anotação @Basic(fetch=FetchType.LAZY).

Para constantes únicas como, por exemplo, CPF de uma pessoa é interessante se utilizar a seguinte anotação @Table (uniqueConstraints = @UniqueConstraint ( columnNames = { "cpf" })). Para determinar que uma propriedade não deva ser nula deve-se utilizar a anotação @NotNull. Outra anotação importante é a anotação @NotEmpty que verifica se alguma propriedade do tipo String não é vazia. Para se determinar o tamanho máximo e mínimo de um campo do tipo String pode se utilizar a anotação @Length(min=3, Max=100) e para se determinar qual o intervalo válido de valores de campos inteiros pode se utilizar a anotação @Range(min=12, max=12).

Para propriedades que podem ser validadas com expressões regulares deve se utilizar a seguinte anotação @Pattern(regex="expressão regular") e para validação de email pode se utilizar a anotação @Email. Para outras validações mais complexas podem-se utilizar as anotações @AssertTrue, @AssertFalse, @Valid.

As validações devem ser utilizadas sempre que possível, para que o modelo esteja sempre coerente e correto.

É isto aí pessoal. Existem várias outras anotações de mapeamento que não foram citadas, mas em um outro momento iremos falar delas. Quaisquer problemas enviem mensagem.


quarta-feira, 12 de novembro de 2008

Gerenciamento de Transações com JTA

Segue abaixo algumas considerações acerca da API JTA e de como EJB3 suporta transações declarativamente.

Normalmente as transações em aplicações são controladas em nível de métodos de negócio. Para se declarar que se está sendo utilizada alguma transação deve se utilizar a anotação @TransactionAttribute.

Por padrão a transação utiliza a estratégia REQUIRED, que significa que se o método do serviço não possuir alguma transação, uma será criada, e se já possuir uma transação esta será aproveitada. Esta é a estratégia que deve ser utilizada na maior parte dos casos uma vez que é bem lógica.

O que esta estratégia quer dizer resumidamente? Quando você estiver em um contexto de transação e caso ocorra alguma exceção do tipo Runtime ou exceção com a anotação @ApplicationException(rollback=true), deve ser dado o rollback na base de dados. Com esta estratégia padrão sempre será dado o rollback da transação mais externa, o que na maioria dos casos faz todo sentido.

Outras estratégias são:
  • SUPPORTS - Não cria nenhuma transação, mas se já existir alguma ela será utilizada.
  • MANDATORY - Requer que quem chamou o método tenha criado uma transação.
  • NEVER - Proíbe que quem chamou o método tenha iniciado uma transação.
  • REQUIRESNEW - Sempre começa uma nova transação, suspendendo a antiga existente.
  • NOTSUPPORTED - Suspende qualquer transação ativa.
Como padrão, pode-se anotar toda a classe com a anotação @TransactionAttribute, o que determina que todos os métodos da classe utilizam transação e com a estratégia REQUIRED. Caso exista algum método que deva usar outra estratégia basta sobrescrever a estratégia usando acima do método alguma das anotações abaixo:
  • @TransactionAttribute(TransactionAttributeType.NEVER)
  • @TransactionAttribute(TransactionAttributeType.MANDATORY)
  • @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
  • @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
  • @TransactionAttribute(TransactionAttributeType.SUPPORTS)
Agora é definir para os métodos qual o melhor tipo de estratégia de transação e sobreescrever.É isto aí pessoal. Qualquer problema me avisem.

sexta-feira, 7 de novembro de 2008

DAO Factory Genérico EJB3

Pessoal,

Como dito pelo excelente post do meu amigo Ricardo Ferreira em http://architecture-journal.blogspot.com/2007/07/enterprise-java-beans-30-anti-patterns.html não é uma boa prática termos todos os DAOs controlados pelo EJB apenas para podermos injetar o EntityManager.

Então fiz um DAOFactory bem genérico que serve muito bem aos meus propósitos, mas que pode ajudar quem quer criar uma factory para EJB3. Para se buscar o DAO, deve-se então injetar o DaoFactory e utilizar o método getDAO passando como argumento a classe persistente a ser utilizada.

Segue abaixo a classe:

@Stateless
@SuppressWarnings("unchecked")
public class DAOFactory implements DaoFactoryLocal {

@PersistenceContext(unitName = "BioModel")
private EntityManager em;

private final static String POS_FIXO_DAO = "DAO";
private final static String PACOTE_ENTIDADE = ".entidade.";
private final static String PACOTE_DAO = ".dao.";

private static String getDAOName(Class classeEntidade) {
String resultado = classeEntidade.getName().replace(PACOTE_ENTIDADE,
PACOTE_DAO);
resultado = resultado.concat(POS_FIXO_DAO);


return resultado;
}

@Override
public GenericDAOImpl getDAO(Class classeEntidade) {

GenericDAOImpl result = null;

try {
result = (GenericDAOImpl) Class.forName(getDAOName(classeEntidade))
.newInstance();
result.setEntityManager(em);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}


return result;
}

}



Segue abaixo um exemplo de sua utilização:

@EJB
private DaoFactoryLocal daoFactory;

@SuppressWarnings("unchecked")
protected GenericDAOImpl getDAO() {
return daoFactory.getDAO(classePersistente);

}


Qualquer problema me avisem.