java - jparepository - JPA-FindByExample
native query jpa repository (7)
¿Alguien tiene un buen ejemplo de cómo hacer un findByExample en JPA que funcione dentro de un DAO genérico a través de la reflexión para cualquier tipo de entidad? Sé que puedo hacerlo a través de mi proveedor (Hibernate), pero no quiero romper con la neutralidad ...
Parece que la API de criterios podría ser el camino a seguir ... pero no estoy seguro de cómo manejar la parte de reflexión.
Criteria API es su mejor apuesta. Sin embargo, necesitará un proveedor JPA-2.0 para eso. Entonces, si tienes una entidad como esta:
@Entity
public class Foo {
@Size(max = 20)
private String name;
}
La siguiente prueba unitaria debería tener éxito (la probé con EclipseLink, pero debería funcionar con cualquiera de los proveedores JPA-2.0):
@PersistenceContext
private EntityManager em;
@Test
@Transactional
public void testFoo(){
Foo foo = new Foo();
foo.setName("one");
em.persist(foo);
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Foo> c = cb.createQuery(Foo.class);
Root<Foo> f = c.from(Foo.class);
c.select(f).where(cb.equal(f.get("name"), "one"));
TypedQuery<Foo> query = em.createQuery(c);
Foo bar = query.getSingleResult();
Assert.assertEquals("one", bar.getName());
}
Además, es posible que desee seguir el enlace al tutorial al que se hace referencia here .
Debería verificar la solución propuesta por Springfuse usando Spring Data y JPA 2.
http://www.springfuse.com/2012/01/31/query-by-example-spring-data-jpa.html
Algunos ejemplos de código fuente aquí (bajo el sub paquete de repositorio): https://github.com/jaxio/generated-projects
Encontrado este proyecto: https://github.com/jaxio/jpa-query-by-example
En realidad, se ha considerado la inclusión de Query By Example (QBE) en la especificación JPA 2.0, pero no se incluye, incluso si los principales proveedores lo admiten. Citando a Mike Keith:
Lamento decir que en realidad no pudimos hacer QBE en JPA 2.0. Criteria API no tiene ningún operador especial para él, por lo que la igualdad de la entidad es igual que en JP QL, en función del valor de PK. Lo sentimos, pero esperamos tener más éxito en ese frente en la próxima ronda. Por ahora es una de esas características de proveedor que todos los proveedores admiten, pero aún no está en la especificación.
Por las dudas, he agregado código de ejemplo (no genérico) para los principales proveedores a continuación para fines de documentación.
EclipseLink
Aquí hay una muestra del uso de QBE en la implementación de referencia EclipseLink JPA 2.0:
// Create a native EclipseLink query using QBE policy
QueryByExamplePolicy policy = new QueryByExamplePolicy();
policy.excludeDefaultPrimitiveValues();
ReadObjectQuery q = new ReadObjectQuery(sampleEmployee, policy);
// Wrap the native query in a standard JPA Query and execute it
Query query = JpaHelper.createQuery(q, em);
return query.getSingleResult();
OpenJPA
OpenJPA admite este estilo de consulta a través de su interfaz extendida OpenJPAQueryBuilder
:
CriteriaQuery<Employee> q = cb.createQuery(Employee.class);
Employee example = new Employee();
example.setSalary(10000);
example.setRating(1);
q.where(cb.qbe(q.from(Employee.class), example);
Hibernar
Y con la API de criterios de Hibernate:
// get the native hibernate session
Session session = (Session) getEntityManager().getDelegate();
// create an example from our customer, exclude all zero valued numeric properties
Example customerExample = Example.create(customer).excludeZeroes();
// create criteria based on the customer example
Criteria criteria = session.createCriteria(Customer.class).add(customerExample);
// perform the query
criteria.list();
Ahora, si bien debería ser posible implementar algo que se acercara de forma neutral a los proveedores con JPA 2.0 Criteria API y la reflexión, realmente me pregunto si vale la pena el esfuerzo. Quiero decir, si hace genéricos algunos de los fragmentos anteriores y coloca el código en un método DAO, sería muy fácil cambiar de un proveedor a otro si fuera necesario. Estoy de acuerdo que no es ideal, pero aún así.
Referencias
Esto es bastante crudo y no estoy seguro de que sea una buena idea en primer lugar. Pero de todos modos, intentemos implementar QBE con la API de criterios JPA-2.0.
Comience definiendo una interfaz Persistable:
public interface Persistable {
public <T extends Persistable> Class<T> getPersistableClass();
}
El método getPersistableClass()
está ahí porque el DAO necesitará la clase, y no pude encontrar una manera mejor de decir T.getClass()
más adelante. Sus clases modelo implementarán Persistable
:
public class Foo implements Persistable {
private String name;
private Integer payload;
@SuppressWarnings("unchecked")
@Override
public <T extends Persistable> Class<T> getPersistableClass() {
return (Class<T>) getClass();
}
}
Entonces su DAO puede tener un findByExample(Persistable example)
(EDITED):
public class CustomDao {
@PersistenceContext
private EntityManager em;
public <T extends Persistable> List<T> findByExample(T example) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, SecurityException, NoSuchMethodException {
Class<T> clazz = example.getPersistableClass();
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<T> cq = cb.createQuery(clazz);
Root<T> r = cq.from(clazz);
Predicate p = cb.conjunction();
Metamodel mm = em.getMetamodel();
EntityType<T> et = mm.entity(clazz);
Set<Attribute<? super T, ?>> attrs = et.getAttributes();
for (Attribute<? super T, ?> a: attrs) {
String name = a.getName();
String javaName = a.getJavaMember().getName();
String getter = "get" + javaName.substring(0,1).toUpperCase() + javaName.substring(1);
Method m = cl.getMethod(getter, (Class<?>[]) null);
if (m.invoke(example, (Object[]) null) != null)
p = cb.and(p, cb.equal(r.get(name), m.invoke(example, (Object[]) null)));
}
cq.select(r).where(p);
TypedQuery<T> query = em.createQuery(cq);
return query.getResultList();
}
Esto es bastante feo. Supone que los métodos getter se pueden derivar de los nombres de los campos (esto es probablemente seguro, como por ejemplo un Java Bean), la manipulación de cadenas en el bucle y puede arrojar un montón de excepciones. La mayor parte del clunkiness en este método gira en torno al hecho de que estamos reinventando la rueda. Tal vez haya una mejor manera de reinventar la rueda, pero tal vez sea allí donde debemos reconocer la derrota y recurrir a uno de los métodos enumerados anteriormente por Pascal. Para Hibernate, esto simplificaría la interfaz para:
public interface Persistable {}
y el método DAO pierde casi todo su peso y clunkiness:
@SuppressWarnings("unchecked")
public <T extends Persistable> List<T> findByExample(T example) {
Session session = (Session) em.getDelegate();
Example ex = Example.create(example);
Criteria c = session.createCriteria(example.getClass()).add(ex);
return c.list();
}
EDITAR: Entonces la siguiente prueba debería tener éxito:
@Test
@Transactional
public void testFindFoo() {
em.persist(new Foo("one",1));
em.persist(new Foo("two",2));
Foo foo = new Foo();
foo.setName("one");
List<Foo> l = dao.findByExample(foo);
Assert.assertNotNull(l);
Assert.assertEquals(1, l.size());
Foo bar = l.get(0);
Assert.assertNotNull(bar);
Assert.assertEquals(Integer.valueOf(1), bar.getPayload());
}
Quizás la respuesta sea demasiado tarde. Pero mira esto Puede ser de ayuda.
https://sourceforge.net/projects/simplejpaquery/
Primero, incluye el jar en classpath. Tendrás una clase llamada com.afifi.simpleJPAQuery.entities.utility.JPAUtil
. Esta clase usa la reflexión para deducir la consulta del bean. Supongamos que tiene un bean de entidad de la siguiente manera:
@Entity
public class Person {
@Id
private Integer personNo;
private String personName;
public Integer getPersonNo() {
return personNo;
}
public void setPersonNo(Integer personNo) {
this.personNo = personNo;
}
public String getPersonName() {
return personName;
}
public void setPersonName(String personName) {
this.personName = personName;
}
}
Entonces, si desea consultar por nombre de persona, por ejemplo, debe hacer lo siguiente:
//initiate entity manager (em)
Person p=new Person();
p.setPersonName("John");
String sortString="";
List<Person> result= JPAUtil.findByExample(em,p,sortString);
El resultado obtendrá todos los registros donde el nombre de la persona contenía la palabra "John".
si quiere limitar los resultados, puede hacer algo como:
List<Person> result= JPAUtil.findByExample(em, p, sortString, start, size);
Esta biblioteca tiene otros métodos como:
getResultCount
: para obtener el recuento del resultado
createSqlStatement
: para obtener la sentencia sql que se está utilizando
getSqlWhereString
: para obtener solo la cadena utilizada
Tiene las formas nativas de estas funciones:
findByExampleNative
, getResultCountNative
, createSqlStatementNative
y getSqlWhereStringNative
La biblioteca también tiene la clase QueryAnnotations
que contiene anotaciones que se pueden agregar a las propiedades de bean de la entidad para dar más control sobre cómo desea realizar consultas utilizando el bean.
puedes usar esto https://github.com/xiaod0510/jpa-findbyexample
si su entidad es Contacto:
@Entity
public class Contact {
@Id
@GeneratedValue
private Long id;
@Column
private String name;
@Column
private Date birthday;
//Getter and Setter
}
public interface ContactRepository
extends
JpaSpecificationExecutor<Contact> {
}
solo crea tu propio ejemplo como este:
public class ContactExample extends BaseExample<ContactExample, Contact> {
public final Attr<Long> id = new Attr<Long>("id");
public final Attr<String> name = new Attr<String>("name");
public final Attr<Date> birthday = new Attr<Date>("birthday");
//default builder
public static ContactExample where() {
ContactExample example = new ContactExample();
example.operatorType = OperatorType.and;
return example;
}
}
y ahora puedes consultar por ejemplo:
ContactRepository.findOne(ContactExample
.where()//default is and
.id.eq(1l)
);
el ejemplo implementa la interfaz "Especificación", más información sobre ese github
https://github.com/superbiger/sbiger-jpa-qbe
Creo que la consulta por ejemplo con una sola tabla como mybatis es fácil de usar
base en jpa también podemos apoyar Join / GroupBy así:
/*
SQL:
select * from
user
where
id=1
or id=2
group by
id,
name
order by
id asc,
name asc
limit ?
*/
public List<User> findAll(){
Example<User> example = ExampleBuilder.create();
example.or()
.andEqual("id", 1)
.orEqual("id", 2);
example.groupBy("id","name");
example.asc("id","name");
return userReponsitory.findAll(example, new PageRequest(0, 1));
}
Características ahora:
- Soporte y / o operación lógica
- El soporte es (Vacío / Booleano / Nulo)
- Soporte Equal / NotEqual / In / NotIn / Me gusta / NotLike
- Soporte gt / ge / lt / le / entre
- Soporte unirse consulta
- Grupo de apoyo por
- Soporte de especificación personalizada.
- Paginación de soporte
más características próximamente ...