Esfinge Query Builder - Persistência simples e rápida


Funcionalidades Básicas do Query Builder
Query Builder em 1 minuto
public interface PersonDAO extends Repository<Person>{
public List<Person> getPersonByLastName(String lastname);
public List<Person> getPersonByAddressCity(String city);
public List<Person> getPersonByAgeGreater(Integer age);
}
PersonDAO dao = QueryBuilder.create(PersonDAO.class);
Como funciona?
Achou fácil? Saiba mais sobre os detalhes e as funcionalidades do QueryBuilder nas seções seguintes.
Configurando o Query Builder
Além disso ainda são necessárias outros JAR básicos para o acesso ao banco de dados, como o driver de conexão e a implementação JPA.
public class TestEntityManagerProvider implements EntityManagerProvider {
@Override
public EntityManager getEntityManager() {
return getEntityManagerFactory().createEntityManager();
}
@Override
public EntityManagerFactory getEntityManagerFactory() {
return Persistence.createEntityManagerFactory("database_test");
}
}
Para configurar essa nova classe criada, é preciso criar um arquivo chamado org.esfinge.querybuilder.jpa1.EntityManagerProvider no diretório META-INF/services de algum dos arquivos JAR da aplicação. Esse arquivo deve conter apenas o nome completo da classe criada.
Repositório
Abaixo seguem os métodos disponibilizados por essa interface com as respectivas descrições:
| Método | Descrição |
| E save(E obj) | Grava no banco de dados o objeto passado como parêmetro. O objeto é inserido caso não exista ou atualizado caso já exista. |
| void delete(Object id) | O método exclui da base de dados a entidade cujo id foi passado como parâmetro. |
| List<E> list() | Retorna uma lista com todas as entidades do banco de dados. |
| E getById(Object id) | Retorna uma instância de acordo com o id passado como parâmetro. |
| List<E> queryByExample(E obj) | Faz uma query que faz a busca de acordo com as propriedades populadas do objeto. |
Nomeandos os Métodos
- Os métodos devem começar com get e serem seguidos do nome da entidade. O nome utilizado deve ser o mesmo utilizado pelo JPA. Exemplo: List<Person> getPerson()
- Para passar parâmetros para a consulta, o nome da entidade deve ser seguido por by e por nomes de propriedades da classe. O parâmetro deve ser do mesmo tipo da propriedade. Exemplo: Person getPersonByName(String name) e Person getPersonByLastName(String name)
- Os métodos podem retornar uma instância da entidade ou uma lista de instâncias da entidade , como os dois exemplos anteriores apresentados.
- O parâmetro passado pode navegar entre as dependências da classe e acessar propriedades delas. Exemplo: List<Person> getPersonByAddressCity(String city) , que filtraria a propriedade city na propriedade address.
- Para passar mais de um parâmetro pode-se utilizar and ou or entre as propriedades. Os parâmetros serão considerados na mesma ordem definida no nome. Exemplo: Person getPersonByNameAndLastName(String name, String lastname) e List<Person> getPersonByNameOrLastName(String name, String lastname)
Outros Tipos de Comparação
List<Person> getPersonByAge(@Greater Integer age); List<Person> getPersonByName(@Starts String name);
public List<Person> getPersonByAgeLesser(Integer age); public List<Person> getPersonByLastNameNotEquals(String name); public List<Person> getPersonByNameStartsAndAgeGreater(String name, Integer age);
Ordenação de Consultas
public List<Person> getPersonOrderByName(); public List<Person> getPersonByAgeOrderByNameDesc(@Greater Integer age); public List<Person> getPersonOrderByNameAndLastName();
Query Objects
public class ExampleQueryObject {
private Integer ageGreater;
private Integer ageLesser;
@Contains
private String name;
private String lastName;
public Integer getAgeGreater() {
return ageGreater;
}
public void setAgeGreater(Integer ageGreater) {
this.ageGreater = ageGreater;
}
public Integer getAgeLesser() {
return ageLesser;
}
public void setAgeLesser(Integer ageLesser) {
this.ageLesser = ageLesser;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Contains
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
Abaixo segue um exemplo de um método que utiliza a classe definida acima:
public List getPerson(@QueryObject ExampleQueryObject obj);
Termos de Domínio com Query Builder
Definindo Termos de Domínio
@DomainTerm(term="major", conditions=@Condition(property="age",comparison=ComparisonType.GREATER_OR_EQUALS,value="18"))
public interface PersonQuery{
//methods ommited
}
@DomainTerm(term="teenager",
conditions={@Condition(property="age",comparison=ComparisonType.GREATER_OR_EQUALS,value="13"),
@Condition(property="age",comparison=ComparisonType.LESSER_OR_EQUALS,value="19")})
public interface PersonQuery{
//methods ommited
}
@DomainTerms({
@DomainTerm(term="elder", conditions=@Condition(property="age",comparison=ComparisonType.GREATER_OR_EQUALS,value="65")),
@DomainTerm(term="paulista", conditions=@Condition(property="address.state",value="SP"))
})
public interface PersonQuery{
//methods ommited
}
Usando os Termos de Domínio nos Métodos
@DomainTerms({
@DomainTerm(term="old guys", conditions=@Condition(property="age",comparison=ComparisonType.GREATER_OR_EQUALS,value="65")),
@DomainTerm(term="paulista", conditions=@Condition(property="address.state",value="SP")),
@DomainTerm(term="teenager",conditions={@Condition(property="age",comparison=ComparisonType.GREATER_OR_EQUALS,value="13"),
@Condition(property="age",comparison=ComparisonType.LESSER_OR_EQUALS,value="19")})
})
public interface PersonQueries{
public List<Person> getPersonTeenager();
public List<Person> getPersonPaulista();
public List<Person> getPersonTeenagerPaulista();
public List<Person> getPersonOldGuys();
public List<Person> getPersonPaulistaByAge(@Greater Integer age);
}
Trabalhando com Parâmetros NULL
Comparando com NULL
public List<Person> getPersonByCompany(@CompareToNull String company); public List<Person> getPersonByNameAndLastName(@Starts String name, @CompareToNull String lastname);
Ignorando Parâmetros que Receberem NULL
public List<Person> getPersonByNameStartsAndLastNameStarts(@IgnoreWhenNull String name, @IgnoreWhenNull String lastname);
Lidando com NULL em Query Objects
Métodos Customizados
Adicionando um método customizado
O primeiro passo para criar um método customizado é definir uma interface apenas com esse método:
public interface CustomMethodInterface {
public void customMethod();
}
Em seguida, define-se uma classe que implementa essa interface:
public class CustomMethodImpl implements CustomMethodInterface{
@Override
public void customMethod() {
//faz alguma coisa
}
}
Parametrizando a interface
public interface GenericMethodInterface extends NeedClassConfiguration {
public E createNewInstance();
}
public class GenericMethodImpl implements GenericMethodInterface {
private Class clazz;
@Override
public void configureClass(Class c) {
clazz = c;
}
@Override
public E createNewInstance() {
try {
return clazz.newInstance();
} catch (Exception e) {
throw new RuntimeException("Not possible to instantiate "+clazz.getName(),e);
}
}
}
public interface GenericInterface extends GenericMethodInterface{
//outros métodos
}
Recuperando implementações
EntityManagerProvider emp = ServiceLocator.getServiceImplementation(EntityManagerProvider.class)
Sobrepondo implementações
Integrando o Query Builder com Spring
Agradecimento a contribuição de Leonardo Machado Moreira que escreveu boa parte e desenvolveu o exemplo desse tutorial.
Configuração do Spring
Abaixo segue o arquivo que mostra como configurar os beans relativos ao acesso ao banco de dados no Spring.
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd" default-autowire="byName"> <bean id="dataSource"> <property name="driverClass" value="com.mysql.jdbc.Driver"/> <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/erh"/> <property name="user" value="root"/> <property name="password" value="303016"/> <property name="initialPoolSize" value="5"/> <property name="minPoolSize" value="5"/> <property name="maxPoolSize" value="50"/> <property name="autoCommitOnClose" value="false"/> <property name="checkoutTimeout" value="30000"/> <property name="maxStatements" value="50"/> </bean> <bean id="entityManagerFactory"> <property name="dataSource" ref="dataSource" /> <property name="persistenceUnitName" value="persistence-unit" /> </bean> <bean id="entityManager"> <property name="entityManagerFactory" ref="entityManagerFactory"/> </bean> <bean id="transactionManager"> <property name="entityManagerFactory" ref="entityManagerFactory" /> </bean> <tx:annotation-driven transaction-manager="transactionManager" /> <bean id="springApplicationContext"/> </beans>
public class SpringApplicationContext implements ApplicationContextAware {
private static ApplicationContext CONTEXT;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
CONTEXT = applicationContext;
}
public static Object getBean(String beanName) {
return CONTEXT.getBean(beanName);
}
public static Object getBean(Class<?>classType) {
return CONTEXT.getBean(classType);
}
}
Configuração do Query Builder
public class JPAEntityManagerProvider implements EntityManagerProvider {
@Override
public EntityManager getEntityManager() {
return (EntityManager) SpringApplicationContext.getBean("entityManager");
}
@Override
public EntityManagerFactory getEntityManagerFactory() {
return (EntityManagerFactory) SpringApplicationContext.getBean("entityManagerFactory");
}
}
Agora é só criar as instâncias a partir do QueryBuilder baseadas nas suas interfaces!
Plugin Para Eclipse
Instalação
- Baixe o Esfinge Query Builder Plugin (EsfingePlugin_1.0.0.jar)
- Copie o plugin para o diretório plugins dentro da raiz do Eclipse
- Reinicie o Eclipse
Uso
Refatoração
Query Builder com MongoDB
O MongoDB é um banco de dados NoSQL, opensource, de alta performance, escrito em C++ e orientado a documentos, sendo que seus documentos seguem o formato JSON. Este módulo tem seu desenvolvimento com o apoio do projeto Morphia, pois providencia a tradução de objetos Java para o banco.
Para utilizá-lo com o Query Builder siga os seguintes passos:
Primeiramente crie um novo projeto Java e adicione os seguintes JARs nele:
- mongo-2.7.3.jar
- morphia-0.99.1-SNAPSHOT.jar
- QueryBuilder_jar.jar
- QueryBuilder_MongoDB_jar.jar
- QueryBuilderParser_jar.jar
Os JARs podem ser encontrados no seguinte link https://github.com/rmmariano/jars_for_query_builder_mongodb ou caso queira o código-fonte, estão disponíveis em https://github.com/EsfingeFramework/querybuilder
Crie um arquivo com o nome "org.esfinge.querybuilder.mongodb.DatastoreProvider" em uma pasta META-INF/services no código-fonte do projeto. Neste arquivo coloque somente uma linha com o nome da classe que herda da DatastoreProvider.
org.esfinge.querybuilder.mongodb.MongoDBDatastoreProvider
Crie uma classe para ser persistida no Java/MongoDB. Utilize a anotação @Id para indicar quem é o indicador único do objeto/documento (ex.: classe Cliente)
Caso essa classe contenha referências, crie-as também (ex.: classe Cachorro e Pagamento
Crie uma classe que herde a DatastoreProvider, colocando a conexão do MongoDB em seu construtor. Lembrando de utilizar o IP/porta do teu banco, que por padrão é 127.0.0.1/27017. Sobreescreva o método getDatastore(), criando um Datastore passando a conexão do banco com o nome do DB utilizado. Utilize o método getMorphia().map() passando como parâmetro quais as classes que devem ser persistidas. A anotação @ServicePriority(1) serve para dar prioridade ao serviço, no caso maior prioridade (ex.: classe MongoDBDatastoreProvider).
Salvando um objeto
Crie um objeto da classe que herda da DatastoreProvider e a partir dele pegue o Datastore, com este objeto obtido, utilize o método save para salvar os objetos no banco.
Consultando dados
Crie um objeto da classe que herda da DatastoreProvider e a partir dele pegue o Datastore, com este objeto obtido, utilize o método find para retornar os objetos no banco, converta-o para uma List e itere-o.
Apoio

