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