A um certo tempo atrás eu estava em busca de uma ferramenta que me ajudasse a fazer os mapeamentos Objeto-Relacional para minhas aplicações. Testei algumas das alternativas usadas atualmente porém parecia que elas não serviam para o que eu precisava. Fiz vários testes com o Hibernate porém ele não se mostrou uma solução viável, visto que o projeto já estava em andamento e seria loucura de uma hora pra outra anotar todas as beans ou largar XMLs de configuração pelo projeto, sem falar da brusca mudança que ocorreria nas minhas DAOs se eu quisesse adotar a filosofia do Hibernate e ter que aprender HQL ou Criteria o que pra mim não faz sentido uma vez que eu já sei SQL. Procurando mais um pouco eu me deparei com o framework MentaBean idealizado pelo Sérgio (saoj aqui do GUJ) e resolvi estudá-lo mais a fundo. Trocando e-mails, estudando o código-fonte e propondo ideias acabei me tornando membro (committer) do projeto, implementando o suporte do MentaBean para o PostgreSQL e sugerindo funcionalidades que acabaram sendo implementadas em seguida. Depois disso comecei a ver a possibilidade enorme de adotá-lo nos meus projetos devido à sua filosofia focada na simplicidade.
A ideia do projeto é simples: automatizar as operações básicas de CRUD e facilitar bastante a construção de queries, sem mágica, sem códigos intrusivos, sem XMLs e sem Annotations (sim, elas são interessantes em muitos casos, mas o uso abusivo pode acabar em perda de performance e sujeira de código nas camadas básicas da aplicação).
O legal disso é que não precisamos mudar a lógica do sistema para utilizarmos o framework, ou seja, nada impede que por ora você deixe de usá-lo e faça suas operações de CRUD da forma que achar conveniente, pois quem deve decidir como, quando e de que forma alguma operação com o banco de dados deve ocorrer é você, não?
O MentaBean está na versão 1.3.0 e agora possui um site documentado que explica as funcionalidades da API de forma bem objetiva… E em breve estará também em português.
As features do projeto (tais como suporte à nested properties, mapeamento automático, etc.) estão detalhadas no site http://mentabean.soliveirajr.com. Talvez ele seja uma boa dica pra quem precisa uma forma simples de aumentar a produtividade no back-end das aplicações sem a complexidade desnecessária do Hibernate.
Ah, o projeto é open source, portanto os fontes estão disponíveis no site ou a partir do SVN. Se alguém resolver experimentar e quiser compartilhar a experiência, é só postar aqui…
[quote=saoj][quote=Hebert Coelho]
Uma coisa que não gostei muito foi: .field(“username”, DBTypes.STRING). Se o nome do atributo mudar tem que mudar aí também. =/
[/quote]
[/quote]Legal, mas é sempre programático a configuração? ou tem um modo automático de se fazer? Por exemplo, o JPA já varre todo o código e localizar quem é “entity” no caso. Como ficaria nesse caso? (só a URL mesmo de como fazer já basta )
O MentaBean é contra anotação pois entende que elas se tornam facilmente uma zona para entender e manter. Como vc lida com o código abaixo:
@Entity
public class User {
@ElementCollection
@CollectionTable(name="Addresses", joinColumns=@JoinColumn(name="user_id"))
@AttributeOverrides({ @AttributeOverride(name="street1",
column=@Column(name="fld_street"))})
public Set<Address> getAddresses() {
// ...
}
// (...)
}
@Embeddable
public class Address {
// (...)
public String getStreet1() {
// (...)
}
}
Com configuração programática as suas entidades ficam limpinhas, sem qualquer anotação ou dependência do framework. E o mapeamento via código (fluent API ou DSL) se torna milhares de vezes mais fácil de entender e manter.
Ficar brigando com essa zona de anotação para fazer a coisa funcionar é como ficar batendo naquelas TVs antigas para ver se a imagem melhora.
[quote=saoj][quote=Hebert Coelho]
Legal, mas é sempre programático a configuração? ou tem um modo automático de se fazer? Por exemplo, o JPA já varre todo o código e localizar quem é “entity” no caso. Como ficaria nesse caso? (só a URL mesmo de como fazer já basta )
[/quote]
O MentaBean é contra anotação pois entende que elas se tornam facilmente uma zona para entender e manter. Como vc lida com o código abaixo:
@Entity
public class User {
@ElementCollection
@CollectionTable(name="Addresses", joinColumns=@JoinColumn(name="user_id"))
@AttributeOverrides({ @AttributeOverride(name="street1",
column=@Column(name="fld_street"))})
public Set<Address> getAddresses() {
// ...
}
// (...)
}
@Embeddable
public class Address {
// (...)
public String getStreet1() {
// (...)
}
}
Com configuração programática as suas entidades ficam limpinhas, sem qualquer anotação ou dependência do framework. E o mapeamento via código (fluent API ou DSL) se tornam milhares de vezes mais fácil de entender e manter.[/quote]Questão de gosto né?
Beleza, parabéns pela iniciativa. [=
Com certeza. Mas meu ponto é que se vc não tomar cuidado, as anotações vão virar uma zona. E elas geralmente viram, porque anotação não é código, é gambiarra.
[quote=AbelBueno]
Eu gosto do mapeamento em código para manter type-safe.
Se você usa strings acaba perdendo isso.
Mas também não tenho idéia de como resolver isso em java.[/quote]
No VRaptor eles fazem umas mágicas com reflection e manipulação de bytecode. Se usassem a mesma técnica poderiam ter construções do tipo:
User userAttributes = createProxy(User.class);
//...
.field(userAttributes.getUsername(), DBTypes.STRING)
.field(userAttributes.getBirthdate(), "bd", DBTypes.DATE)
O método createProxy retorna uma instância modificada, cujos getters quando chamados guardam em algum lugar o nome do atributo associado ao getter.
O método .field() (que teria o primeiro parâmetro alterado para Object, permitindo qualquer tipo) busca esse nome armazenado anteriormente pelo proxy.
Achei muito criativo, e elimina o problema de ter os nomes dos atributos como literal, sem verificação do compilador.
Aliás, a meu ver essa dificuldade de refatorar é o que falta para o esquema de configuração programática ficar perfeito.
[quote=gomesrod]
No VRaptor eles fazem umas mágicas com reflection e manipulação de bytecode. Se usassem a mesma técnica poderiam ter construções do tipo:
User userAttributes = createProxy(User.class);
//...
.field(userAttributes.getUsername(), DBTypes.STRING)
.field(userAttributes.getBirthdate(), "bd", DBTypes.DATE)
O método createProxy retorna uma instância modificada, cujos getters quando chamados guardam em algum lugar o nome do atributo associado ao getter.
O método .field() (que teria o primeiro parâmetro alterado para Object, permitindo qualquer tipo) busca esse nome armazenado anteriormente pelo proxy.
Achei muito criativo, e elimina o problema de ter os nomes dos atributos como literal, sem verificação do compilador.
Aliás, a meu ver essa dificuldade de refatorar é o que falta para o esquema de configuração programática ficar perfeito.[/quote]
Pois é, cheguei a pensar nisso.
Mas não acho que com proxy dinâmico você consiga alterar o tipo de retorno de um método (eu acho).
Daí, imagino, que o seu getUserName e getBirthdate teria que continuar retornando seus tipos originais.
Se você fosse adicionar meta informação ao método, teria que obter de outra forma.
Pelo menos é isso que eu acho.
BeanConfig config = new AutoBeanConfig(User.class, "Users");
// agora sobre-escreva as propriedades que vc quer definir manualmente, pois a configuração padrão não lhe atende...
config.pk("id", DBTypes.AUTOINCREMENT);
config.field("birthdate", "bd", DBTypes.DATE);
config.field("status", DBTypes.ENUMVALUE.from(User.Status.class));
config.field("deleted", DBTypes.BOOLEANINT);
config.field("insertTime", "insert_time", DBTypes.NOW_ON_INSERT_TIMESTAMP);
Tb não dá para refatorar strings dentro de XML ou Anotações. Vai chegar uma hora que vc vai ter que usar Strings, não tem jeito. Por exemplo: se o nome da propriedade do bean for diferente do nome da coluna no banco, vc vai ter que chapar o nome da coluna no banco somewhere.
[quote=AbelBueno][quote=gomesrod]
No VRaptor eles fazem umas mágicas com reflection e manipulação de bytecode. Se usassem a mesma técnica poderiam ter construções do tipo:
User userAttributes = createProxy(User.class);
//...
.field(userAttributes.getUsername(), DBTypes.STRING)
.field(userAttributes.getBirthdate(), "bd", DBTypes.DATE)
O método createProxy retorna uma instância modificada, cujos getters quando chamados guardam em algum lugar o nome do atributo associado ao getter.
O método .field() (que teria o primeiro parâmetro alterado para Object, permitindo qualquer tipo) busca esse nome armazenado anteriormente pelo proxy.
Achei muito criativo, e elimina o problema de ter os nomes dos atributos como literal, sem verificação do compilador.
Aliás, a meu ver essa dificuldade de refatorar é o que falta para o esquema de configuração programática ficar perfeito.[/quote]
Pois é, cheguei a pensar nisso.
Mas não acho que com proxy dinâmico você consiga alterar o tipo de retorno de um método (eu acho).
Daí, imagino, que o seu getUserName e getBirthdate teria que continuar retornando seus tipos originais.
Se você fosse adicionar meta informação ao método, teria que obter de outra forma.
Pelo menos é isso que eu acho.[/quote]
O que você pode é receber Object no método .field() e lá de dentro pegar o método chamado (semelhante ao que o EasyMock faz pra configurar os mocks). O problema dessa solução é que ela faz você expor atributos que podem ser desnecessários pois precisaria chamar o getter.
Isso seria algo para facilitar o refactoring, mas, como a configuração é centralizada, refatorar a classe de configuração após a troca da propriedade seria o menor dos problemas. Dependendo do caso, teríamos que procurar nas páginas referências à propriedade também (caso o IDE não faça a refatoração nelas).
O Érico levantou essa bola, por isso adicionamos o método remove(propName) no AutoBeanConfig pois ele inicialmente vai ter que mapear todas as propriedades. Aí depois se vc quiser excluir alguma propriedade que não deve ir para o banco vc usa esse método.
[quote=saoj][quote=Ataxexe]
O problema dessa solução é que ela faz você expor atributos que podem ser desnecessários pois precisaria chamar o getter.
[/quote]
O Érico levantou essa bola, por isso adicionamos o método remove(propName) no AutoBeanConfig pois ele inicialmente vai ter que mapear todas as propriedades. Aí depois se vc quiser excluir alguma propriedade que não deve ir para o banco vc usa esse método.[/quote]
Na verdade eu me referia à solução de proxy para configuração dos mapeamentos
Se você tiver que mapear as coisas com .field(entity.getSeiLaOQue()) vai precisar criar getters pra coisas que não deveriam ser expostas, por isso sou mais a favor de configurar na mão mesmo. É sempre bom prover automatizações, mas acho que nunca se deve tirar o desenvolvedor do controle da situação nesses casos.
Ah! E meus parabéns pelo projeto. Conheço a família Menta há um tempo e acho muito legal a filosofia de configuração programática. Também detesto XML e não curto anotações de infraestrutura, acho que o domínio da aplicação deveria conter anotações de domínio e não de infra.
Não entendi o que um proxy vai ajudar para configuração, uma vez que via reflection é possível pegar o nome e o tipo de todas as propriedades. Pensando melhor acho que entendi:
O AutoBeanConfig faz o mapeamento automático de forma implícita (obviamente vai pegar o refactory dos nomes das propriedades mas não para os campos que vc configurou na mão / sobreescreveu). E o proxy sugerido faz o mapeamento não-automático de forma explícita com suporte a refactoring do nome das propriedades. Tem seu uso sim, pois eu muitas vezes prefiro VER o que está sendo configurado do que confiar que foi configurado automaticamente. O único drawback que eu vejo é quanto tivermos beans com 100 propriedades, terão que haver 100 linhas para configurá-las via o proxy e com o AutoBeanConfig apenas uma + as propriedades que vc quer sobre-escrever.
Sim! Eu só usuaria o mapeamento automático para um bean com muitas propriedades e eu fiquei com preguiça de escrever um caminhão de linhas para configurar as Strings e Integers que são triviais e tem o mesmo nome no banco de dados.
Na prática, não vejo tantos casos de atributos persistidos sem um get, mas não é papel do framework limitar isso.
Só ainda não entendi como um proxy atuaria ali, como você comentou no caso do EasyMock.
Tem uma idéia de como seria o código do método field() para isso?