Spring Data JPA에 커스텀 메소드를 추가하는 방법
Spring Data JPA를 찾고 있습니다. 기본적으로 모든 크루 드 및 파인더 기능이 작동하는 아래 예제를 고려하고 파인더를 사용자 정의하려면 인터페이스 자체에서 쉽게 수행 할 수 있습니다.
@Transactional(readOnly = true)
public interface AccountRepository extends JpaRepository<Account, Long> {
@Query("<JPQ statement here>")
List<Account> findByCustomer(Customer customer);
}
위의 AccountRepository에 대한 구현으로 완전한 사용자 정의 메소드를 추가하는 방법을 알고 싶습니다. 인터페이스 때문에 메소드를 구현할 수 없습니다.
사용자 정의 메소드에 대해 별도의 인터페이스를 작성해야합니다.
public interface AccountRepository
extends JpaRepository<Account, Long>, AccountRepositoryCustom { ... }
public interface AccountRepositoryCustom {
public void customMethod();
}
해당 인터페이스에 대한 구현 클래스를 제공하십시오.
public class AccountRepositoryImpl implements AccountRepositoryCustom {
@Autowired
@Lazy
AccountRepository accountRepository; /* Optional - if you need it */
public void customMethod() { ... }
}
또한보십시오:
이름 지정 체계는 버전간에 변경되었습니다. 자세한 내용은 https://stackoverflow.com/a/52624752/66686 을 참조하십시오.
axtavt의 답변 외에도 쿼리를 작성하는 데 필요한 경우 사용자 정의 구현에 Entity Manager를 삽입 할 수 있음을 잊지 마십시오.
public class AccountRepositoryImpl implements AccountRepositoryCustom {
@PersistenceContext
private EntityManager em;
public void customMethod() {
...
em.createQuery(yourCriteria);
...
}
}
사용에는 제한이 있지만 간단한 사용자 정의 메소드의 경우 다음 과 같은 기본 인터페이스 메소드를 사용할 수 있습니다 .
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", "nowakJ@o2.pl", 679856885, "Rzeszów", "Podkarpackie", "35-061", "Zamknięta 12"),
new Customer("Adrian", "Mularczyk", "adii333@wp.pl", 867569344, "Krosno", "Podkarpackie", "32-442", "Hynka 3/16"),
new Customer("Kazimierz", "Dejna", "sobieski22@weebly.com", 996435876, "Jarosław", "Podkarpackie", "25-122", "Korotyńskiego 11"),
new Customer("Celina", "Dykiel", "celina.dykiel39@yahoo.org", 947845734, "Żywiec", "Śląskie", "54-333", "Polna 29")
};
for (Customer customer : customers) {
save(customer);
}
}
}
편집하다:
Spring Data JPA를 사용하면 메소드 서명을 선언하여 다른 쿼리 메소드를 정의 할 수도 있습니다.
따라서 다음과 같은 메소드를 선언하는 것조차 가능합니다.
Customer findByHobby(Hobby personHobby);
객체 Hobby
가 Customer의 속성이라면 Spring은 자동으로 메소드를 정의합니다.
수락 된 답변은 효과가 있지만 세 가지 문제가 있습니다.
- 커스텀 구현을로 명명 할 때 문서화되지 않은 스프링 데이터 기능을 사용합니다
AccountRepositoryImpl
. 문서는 명확하게 호출 할 것을 명시AccountRepositoryCustomImpl
, 사용자 정의 인터페이스 이름과Impl
@Autowired
나쁜 습관으로 간주되는 생성자 주입 만 사용할 수 없습니다.- 사용자 정의 구현 내부에 순환 종속성이 있습니다 (따라서 생성자 삽입을 사용할 수 없습니다).
문서화되지 않은 다른 스프링 데이터 기능을 사용하지 않고도 완벽하게 만드는 방법을 찾았습니다.
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
}
}
내 사용자 정의 구현에서 생성 된 찾기 메소드에 액세스하기 위해 다음 코드를 사용하고 있습니다. Bean Factory를 통해 구현하면 순환 Bean 작성 문제가 방지됩니다.
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);
}
}
Considering your code snippet, please note that you can only pass Native objects to the findBy### method, lets say you want to load a list of accounts that belongs certain costumers, one solution is to do this,
@Query("Select a from Account a where a."#nameoffield"=?1")
List<Account> findByCustomer(String "#nameoffield");
Make sue the name of the table to be queried is thesame as the Entity class. For further implementations please take a look at this
If you want to be able to do more sophisticated operations you might need access to Spring Data's internals, in which case the following works (as my interim solution to 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);
}
}
}
There is another issue to be considered here. Some people expect that adding custom method to your repository will automatically expose them as REST services under '/search' link. This is unfortunately not the case. Spring doesn't support that currently.
This is 'by design' feature, spring data rest explicitly checks if method is a custom method and doesn't expose it as a REST search link:
private boolean isQueryMethodCandidate(Method method) {
return isQueryAnnotationPresentOn(method) || !isCustomMethod(method) && !isBaseClassMethod(method);
}
This is a qoute of Oliver Gierke:
This is by design. Custom repository methods are no query methods as they can effectively implement any behavior. Thus, it's currently impossible for us to decide about the HTTP method to expose the method under. POST would be the safest option but that's not in line with the generic query methods (which receive GET).
For more details see this issue: https://jira.spring.io/browse/DATAREST-206
As specificed in the documented functionality, using the Impl
prefix allows us to have a quite clean solution:
- Define in the
@Repository
interface, sayMyEntityRepository
, either Spring Data methods or custom methods - Create a class
MyEntityRepositoryImpl
(theImpl
suffix is the magic) anywhere (doesn't even need to be in the same package) that implements the custom methods only and annotate such class with@Component
** (@Repository
will not work).- This class can even inject
MyEntityRepository
via@Autowired
for use in the custom methods.
- This class can even inject
Example:
Entity class:
package myapp.domain.myentity;
@Entity
public class MyEntity {
@Id
private Long id;
@Column
private String comment;
}
Repository interface:
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);
}
Custom methods implementation bean:
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;
}
}
The small drawbacks I identified are:
- The custom methods in the
Impl
class are marked as unused by the compiler, thus the@SuppressWarnings("unused")
suggestion. - You have a limit of one
Impl
class. (Whereas in the regular fragment interfaces implementation the docs suggest you could have many.)
I extends the 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;
}
}
and adds this class to @EnableJpaRepositoryries repositoryBaseClass.
Side note:
When creating Custom Implementations for Spring Data Repositories:
The most important part of the class name that corresponds to the fragment interface is the Impl postfix.
참고URL : https://stackoverflow.com/questions/11880924/how-to-add-custom-method-to-spring-data-jpa
'Programing' 카테고리의 다른 글
정규식을 사용하여 줄이 비어 있는지 확인하는 방법 (0) | 2020.06.20 |
---|---|
UnicodeEncodeError : 'charmap'코덱이 문자를 인코딩 할 수 없습니다 (0) | 2020.06.20 |
안드로이드 이미지 캐싱 (0) | 2020.06.20 |
스칼라에서 환경 변수를 읽는 방법 (0) | 2020.06.20 |
두 개의 두 숫자 사이의 난수 (0) | 2020.06.20 |