JPA-FindByExample
est-ce que quelqu'un a un bon exemple pour faire un findByExample dans JPA qui fonctionnera dans un DAO Générique via la réflexion pour n'importe quel type d'entité? Je sais que je peux le faire via mon fournisseur (hibernant), mais je ne veux pas rompre avec la neutralité...
semble comme L'API critères pourrait être la voie à suivre....mais je ne suis pas sûr de savoir comment gérer la partie de réflexion de celui-ci.
7 réponses
en fait, Query By Example (QBE) a été considéré pour l'inclusion dans la spécification JPA 2.0, mais n'est pas inclus, même si les principaux fournisseurs l'appuient. Citant Mike Keith:
je suis désolé de dire que nous n'avons pas vraiment pu faire de QBE dans JPA 2.0. L'API critères n'a pas d'opérateurs spéciaux pour elle, donc l'égalité d'entité est exactement comme dans JP QL, basée sur la valeur PK. Désolé, mais j'espère que nous aurons plus de succès sur ce front lors du prochain tour. Pour maintenant, c'est une de ces fonctionnalités de fournisseur que chaque fournisseur prend en charge, mais qui n'est pas encore dans les spécifications.
juste au cas où, j'ai ajouté un code d'exemple (non générique) pour les principaux fournisseurs ci-dessous à des fins de documentation.
EclipseLink
voici un exemple d'utilisation de QBE dans L'implémentation de référence JPA 2.0 D'EclipseLink:
// 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 soutient ce style de requête à travers son étendue OpenJPAQueryBuilder
interface:
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);
hibernation
et avec API critères D'hibernation:
// 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();
maintenant, alors qu'il devrait être possible d'implémenter quelque chose approchant de manière neutre avec L'API critères JPA 2.0 et la réflexion, je me demande vraiment si cela en vaut la peine. Je veux dire, si vous faites l'un des snippets ci-dessus générique et mettez le code dans une méthode DAO, il serait assez facile de passer d'un fournisseur à l'autre si besoin est. Je suis d'accord que ce n'est pas idéal, mais quand même.
Références
c'est assez grossier et je ne suis pas convaincu que ce soit une bonne idée en premier lieu. Mais de toute façon, essayons d'implémenter QBE avec L'API critères JPA-2.0.
commencer par définir une interface Persistable:
public interface Persistable {
public <T extends Persistable> Class<T> getPersistableClass();
}
la méthode getPersistableClass()
est là parce que le DAO aura besoin de la classe, et je n'ai pas pu trouver une meilleure façon de dire T.getClass()
plus tard. Vos classes de modèles implémenteront 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();
}
}
alors votre DAO peut avoir une méthode findByExample(Persistable example)
(édité):
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();
}
c'est assez laid. Il suppose que les méthodes getter peuvent être dérivées des noms de champ (ce qui est probablement sûr, comme exemple devrait être un haricot Java), fait la manipulation de chaîne dans la boucle, et pourrait jeter un tas d'exceptions. La plupart du temps, cette méthode tourne autour du fait que nous réinventons la roue. Peut-être il ya une meilleure façon de réinventer la roue, mais c'est peut-être là où nous devrions concéder la défaite et recourir à l'une des méthodes énumérées par Pascal ci-dessus. Pour L'hibernation, cela simplifierait L'Interface à:
public interface Persistable {}
et la méthode DAO perd presque tout son poids et son élégance:
@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();
}
modifier: alors le test suivant devrait réussir:
@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());
}
vous devez vérifier la solution proposée par Springfuse en utilisant Spring Data & JPA 2.
http://www.springfuse.com/2012/01/31/query-by-example-spring-data-jpa.html
quelques exemples de code source ici (sous le sous-paquet de dépôt)): https://github.com/jaxio/generated-projects
trouvé ce projet: https://github.com/jaxio/jpa-query-by-example
https://github.com/superbiger/sbiger-jpa-qbe
l'think requête par exemple avec une table simple comme mybatis est facile à utiliser
base sur jpa nous pouvons également soutenir Join / GroupBy comme ceci:
/*
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));
}
Dispose désormais:
- Support et/ou fonctionnement logique
- Soutien est(Vide/Boolean/Null)
- Support L'Égalité/NotEqual//NotIn/Comme/NotLike
- Soutien gt/ge/lt/le/entre
- Soutenir la requête de jointure
- groupe de Soutien par les
- Soutien personnalisé de la spécification.
- pagination de soutien
d'autres fonctionnalités à venir......
critères API est votre meilleur pari. Vous aurez besoin d'un fournisseur JPA-2.0 pour cela, cependant. Donc, si vous avez une entité comme ceci:
@Entity
public class Foo {
@Size(max = 20)
private String name;
}
le test unitaire suivant devrait réussir (je l'ai testé avec EclipseLink, mais il devrait fonctionner avec n'importe lequel des fournisseurs 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());
}
Aussi, vous pouvez suivre le lien du tutoriel référencé ici .
vous pouvez utiliser ce https://github.com/xiaod0510/jpa-findbyexample
si votre entité est Contact:
@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> {
}
créez simplement votre propre exemple comme ceci:
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;
}
}
et maintenant, vous pouvez interroger par exemple :
ContactRepository.findOne(ContactExample
.where()//default is and
.id.eq(1l)
);
l'exemple implémente l'interface "spécification", plus d'informations sur ce github
Peut-être que la réponse est trop tard. Mais vérifier ce point. Il peut être de l'aide.
https://sourceforge.net/projects/simplejpaquery/
tout d'abord, incluez le bocal dans le chemin de catégorie. Vous aurez une classe appelée com.afifi.simpleJPAQuery.entities.utility.JPAUtil
.
Cette classe utilise la réflexion pour déduire la requête de la fève.
Supposons que vous ayez un haricot d'entité comme suit:
@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;
}
}
puis si vous voulez demander par nom de personne par exemple, vous devez faire comme suit:
//initiate entity manager (em)
Person p=new Person();
p.setPersonName("John");
String sortString="";
List<Person> result= JPAUtil.findByExample(em,p,sortString);
le résultat obtiendra tous les enregistrements où le nom de la personne contenait le mot"John".
si vous voulez limiter les résultats, vous pouvez faire quelque chose comme:
List<Person> result= JPAUtil.findByExample(em, p, sortString, start, size);
Cette bibliothèque a d'autres méthodes comme:
getResultCount
: pour obtenir le compte du résultat
createSqlStatement
: pour obtenir la déclaration sql qui est
getSqlWhereString
: pour obtenir la chaîne utilisée
il a les formes natives de ces fonctions:
findByExampleNative
, getResultCountNative
, createSqlStatementNative
et getSqlWhereStringNative
la bibliothèque a aussi la classe QueryAnnotations
qui contient des annotations qui peuvent être ajoutées aux propriétés de la fève entité pour donner plus de contrôle sur la façon dont vous voulez interroger en utilisant la fève.