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.

27
demandé sur Manuel Drieschmanns 2010-05-21 11:17:06

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

37
répondu Pascal Thivent 2010-05-21 22:42:33

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());      
}
11
répondu wallenborn 2010-05-26 15:20:06

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

3
répondu Nicolas Romanetti 2012-12-23 10:11:52

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......
1
répondu superbiger 2016-12-27 10:22:25

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 .

0
répondu wallenborn 2017-05-23 11:46:45

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

0
répondu xiaod0510 2016-12-15 10:10:00

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.

0
répondu Moody Ibrahim Moody 2017-04-02 10:07:57