wake-up-neo.com

Hinzufügen einer benutzerdefinierten Methode zu Spring Data JPA

Ich untersuche Spring Data JPA. Betrachten Sie das Beispiel unten, bei dem ich alle Funktionen von Crud und Finder standardmäßig zum Laufen bekomme. Wenn Sie einen Finder anpassen möchten, können Sie dies auch in der Benutzeroberfläche selbst tun.

@Transactional(readOnly = true)
public interface AccountRepository extends JpaRepository<Account, Long> {

  @Query("<JPQ statement here>")
  List<Account> findByCustomer(Customer customer);
}

Ich möchte wissen, wie ich eine vollständige benutzerdefinierte Methode für die Implementierung des obigen AccountRepository hinzufügen kann. Da es eine Schnittstelle ist, kann ich die Methode dort nicht implementieren.

126
Sharad Yadav

Sie müssen eine eigene Schnittstelle für Ihre benutzerdefinierten Methoden erstellen:

public interface AccountRepository 
    extends JpaRepository<Account, Long>, AccountRepositoryCustom { ... }

public interface AccountRepositoryCustom {
    public void customMethod();
}

und stellen Sie eine Implementierungsklasse für diese Schnittstelle bereit:

public class AccountRepositoryImpl implements AccountRepositoryCustom {

    @Autowired
    AccountRepository accountRepository;  /* Optional - if you need it */

    public void customMethod() { ... }
}

Siehe auch:

224
axtavt

Vergessen Sie nicht, dass Sie neben axtavt answer auch Entity Manager in Ihre benutzerdefinierte Implementierung einfügen können, wenn Sie dies zum Erstellen Ihrer Abfragen benötigen:

public class AccountRepositoryImpl implements AccountRepositoryCustom {

    @PersistenceContext
    private EntityManager em;

    public void customMethod() { 
        ...
        em.createQuery(yourCriteria);
        ...
    }
}
65
jelies

Dies ist in der Verwendung eingeschränkt, für einfache benutzerdefinierte Methoden können Sie jedoch default -Methoden verwenden:

import demo.database.Customer;
import org.springframework.data.repository.CrudRepository;

public interface CustomerService extends CrudRepository<Customer, Long> {


    default void addSomeCustomers() {
        Customer[] customers = {
            new Customer("Józef", "Nowak", "[email protected]", 679856885, "Rzeszów", "Podkarpackie", "35-061", "Zamknięta 12"),
            new Customer("Adrian", "Mularczyk", "[email protected]", 867569344, "Krosno", "Podkarpackie", "32-442", "Hynka 3/16"),
            new Customer("Kazimierz", "Dejna", "[email protected]", 996435876, "Jarosław", "Podkarpackie", "25-122", "Korotyńskiego 11"),
            new Customer("Celina", "Dykiel", "[email protected]", 947845734, "Żywiec", "Śląskie", "54-333", "Polna 29")
        };

        for (Customer customer : customers) {
            save(customer);
        }
    }
}

BEARBEITEN: 

In diesem Frühling Tutorial steht geschrieben:

Mit Spring Data JPA können Sie auch andere Abfragemethoden mithilfe von .__ definieren. einfach ihre Methodensignatur angeben.

So ist es sogar möglich, Methoden wie folgt zu deklarieren:

Customer findByHobby(Hobby personHobby);

wenn das Objekt Hobby eine Eigenschaft des Kunden ist, definiert Spring automatisch die Methode für Sie.

10

Ich verwende den folgenden Code, um auf generierte Suchmethoden aus meiner benutzerdefinierten Implementierung zuzugreifen. Wenn Sie die Implementierung durch die Bean-Factory durchführen, werden Probleme bei der Erstellung von Circular-Beans vermieden.

public class MyRepositoryImpl implements MyRepositoryExtensions, BeanFactoryAware {

    private BrandRepository myRepository;

    public MyBean findOne(int first, int second) {
        return myRepository.findOne(new Id(first, second));
    }

    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        myRepository = beanFactory.getBean(MyRepository.class);
    }
}
5
Peter Rietzler

Die akzeptierte Antwort funktioniert, hat jedoch drei Probleme:

  • Bei der Benennung der benutzerdefinierten Implementierung als AccountRepositoryImpl wird eine undokumentierte Spring Data-Funktion verwendet. Die Dokumentation gibt eindeutig an, dass sie AccountRepositoryCustomImpl, den Namen der benutzerdefinierten Schnittstelle plus Impl heißen muss.
  • Sie können keine Konstruktorinjektion verwenden, nur @Autowired, die als schlechte Praxis betrachtet wird
  • Sie haben innerhalb der benutzerdefinierten Implementierung eine zirkulare Abhängigkeit (deshalb können Sie keine Konstruktorinjektion verwenden). 

Ich habe einen Weg gefunden, es perfekt zu machen, allerdings nicht ohne ein anderes undokumentiertes Spring Data-Feature:

public interface AccountRepository extends AccountRepositoryBasic,
                                           AccountRepositoryCustom 
{ 
}

public interface AccountRepositoryBasic extends JpaRepository<Account, Long>
{
    // standard Spring Data methods, like findByLogin
}

public interface AccountRepositoryCustom 
{
    public void customMethod();
}

public class AccountRepositoryCustomImpl implements AccountRepositoryCustom 
{
    private final AccountRepositoryBasic accountRepositoryBasic;

    // constructor-based injection
    public AccountRepositoryCustomImpl(
        AccountRepositoryBasic accountRepositoryBasic)
    {
        this.accountRepositoryBasic = accountRepositoryBasic;
    }

    public void customMethod() 
    {
        // we can call all basic Spring Data methods using
        // accountRepositoryBasic
    }
}
4
Danila Piatov

In Anbetracht Ihres Code-Snippets beachten Sie bitte, dass Sie Native-Objekte nur an die findBy ### -Methode übergeben können. Angenommen, Sie möchten eine Liste von Konten laden, die zu bestimmten Kunden gehören. Eine Lösung besteht darin, 

 @Query("Select a from Account a where a."#nameoffield"=?1")
      List<Account> findByCustomer(String "#nameoffield");

Geben Sie sue an, dass der Name der Tabelle, die abgefragt werden soll, die Entity-Klasse ist. Für weitere Implementierungen lesen Sie bitte this .

4
samba

Hier ist noch ein weiterer Punkt zu prüfen. Einige Leute erwarten, dass das Hinzufügen einer benutzerdefinierten Methode zu Ihrem Repository sie automatisch als REST - Dienste unter dem Link "/ search" verfügbar macht. Dies ist leider nicht der Fall. Spring unterstützt das derzeit nicht. 

Hierbei handelt es sich um eine Funktion "von Design". Spring Data Rest prüft explizit, ob die Methode eine benutzerdefinierte Methode ist und stellt sie nicht als REST - Suchlink zur Verfügung:

private boolean isQueryMethodCandidate(Method method) {    
  return isQueryAnnotationPresentOn(method) || !isCustomMethod(method) && !isBaseClassMethod(method);
}

Dies ist eine Kurzbeschreibung von Oliver Gierke:

Dies ist beabsichtigt. Benutzerdefinierte Repository-Methoden sind keine Abfragemethoden wie Sie können jedes Verhalten effektiv umsetzen. So ist es derzeit Es ist uns nicht möglich, über die HTTP-Methode zu entscheiden, um die Methode verfügbar zu machen unter. POST wäre die sicherste Option, aber das stimmt nicht mit der .__ überein. generische Abfragemethoden (die GET erhalten).

Weitere Informationen finden Sie in dieser Ausgabe: https://jira.spring.io/browse/DATAREST-206

3
Lukasz Magiera

Wenn Sie komplexere Operationen ausführen möchten, benötigen Sie möglicherweise Zugriff auf die internen Daten von Spring Data. In diesem Fall funktionieren die folgenden (als meine vorläufige Lösung für DATAJPA-422 ):

public class AccountRepositoryImpl implements AccountRepositoryCustom {

    @PersistenceContext
    private EntityManager entityManager;

    private JpaEntityInformation<Account, ?> entityInformation;

    @PostConstruct
    public void postConstruct() {
        this.entityInformation = JpaEntityInformationSupport.getMetadata(Account.class, entityManager);
    }

    @Override
    @Transactional
    public Account saveWithReferenceToOrganisation(Account entity, long referralId) {
        entity.setOrganisation(entityManager.getReference(Organisation.class, organisationId));
        return save(entity);
    }

    private Account save(Account entity) {
        // save in same way as SimpleJpaRepository
        if (entityInformation.isNew(entity)) {
            entityManager.persist(entity);
            return entity;
        } else {
            return entityManager.merge(entity);
        }
    }

}
3
NealeU

Randnotiz:

Beim Erstellen benutzerdefinierter Implementierungen für Spring-Datenrepositorys:

Der wichtigste Teil des Klassennamens, der der Fragmentschnittstelle entspricht, ist das Impl Postfix.

0
Serhat Oz

Wie in der dokumentierten Funktionalität angegeben, können wir mit dem Präfix Impl eine recht saubere Lösung finden:

  • Definieren Sie in der Schnittstelle @Repository beispielsweise MyEntityRepository entweder Spring Data-Methoden oder benutzerdefinierte Methoden
  • Erstellen Sie eine Klasse MyEntityRepositoryImpl (das Suffix Impl ist die Magie) an einer beliebigen Stelle (muss sich nicht einmal im selben Paket befinden), die die benutzerdefinierten Methoden implementiert only und annotieren solche Klasse mit @Component ** (@Repository wird nicht funktionieren).
    • Diese Klasse kann sogar MyEntityRepository über @Autowired zur Verwendung in den benutzerdefinierten Methoden einschleusen.


Beispiel:

Entitätsklasse:

package myapp.domain.myentity;

@Entity
public class MyEntity {

    @Id
    private Long id;

    @Column
    private String comment;

}

Repository-Schnittstelle:

package myapp.domain.myentity;

@Repository
public interface MyEntityRepository extends JpaRepository<MyEntity, Long> {

    // EXAMPLE SPRING DATA METHOD
    List<MyEntity> findByCommentEndsWith(String x);

    List<MyEntity> doSomeHql(Long id);

    List<MyEntity> useTheRepo(Long id);

}

Implementierungs-Bean für benutzerdefinierte Methoden:

package myapp.infrastructure.myentity;

@Component // Must be @Component !!
public class MyEntityRepositoryImpl { // must have the repo name + Impl !!

    @PersistenceContext
    private EntityManager entityManager;

    @Autowired
    private MyEntityRepository myEntityRepository;

    @SuppressWarnings("unused")
    public List<MyEntity> doSomeHql(Long id) {
        String hql = "SELECT eFROM MyEntity e WHERE e.id = :id";
        TypedQuery<MyEntity> query = entityManager.createQuery(hql, MyEntity.class);
        query.setParameter("id", id);
        return query.getResultList();
    }

    @SuppressWarnings("unused")
    public List<MyEntity> useTheRepo(Long id) {
        List<MyEntity> es = doSomeHql(id);
        es.addAll(myEntityRepository.findByCommentEndsWith("DO"));
        es.add(myEntityRepository.findById(2L).get());
        return es;
    }

}

Die kleinen Nachteile, die ich festgestellt habe, sind:

  • Die benutzerdefinierten Methoden in der Klasse Impl werden vom Compiler als nicht verwendet markiert, daher der Vorschlag @SuppressWarnings("unused").
  • Sie können maximal eine Impl Klasse verwenden. (Während bei der Implementierung der regulären Fragmentschnittstellen wie in den Dokumenten vorgeschlagen viele möglich sind.)
0
acdcjunior

Ich erweitere das SimpleJpaRepository:

public class ExtendedRepositoryImpl<T extends EntityBean> extends SimpleJpaRepository<T, Long>
    implements ExtendedRepository<T> {

    private final JpaEntityInformation<T, ?> entityInformation;

    private final EntityManager em;

    public ExtendedRepositoryImpl(final JpaEntityInformation<T, ?> entityInformation,
                                                      final EntityManager entityManager) {
       super(entityInformation, entityManager);
       this.entityInformation = entityInformation;
       this.em = entityManager;
    }
}

und fügt diese Klasse zu @EnableJpaRepositoryries repositoryBaseClass hinzu.

0
Devilluminati