Spring Data JPA에 사용자 지정 메서드를 추가하는 방법
Spring Data JPA에 대해 알아보고 있습니다.아래 예에서는 모든 crud 및 finder 기능을 기본적으로 사용할 수 있으며, Finder를 커스터마이즈하려면 인터페이스 자체에서도 쉽게 실행할 수 있습니다.
@Transactional(readOnly = true)
public interface AccountRepository extends JpaRepository<Account, Long> {
@Query("<JPQ statement here>")
List<Account> findByCustomer(Customer customer);
}
위의 Account Repository 구현에 완전한 커스텀 메서드를 추가하는 방법을 알고 싶습니다.인터페이스이기 때문에, 거기에 메서드를 실장할 수 없습니다.
커스텀 메서드용으로 다른 인터페이스를 작성해야 합니다.
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의 답변 외에 쿼리를 빌드하기 위해 필요한 경우 사용자 지정 구현에 엔티티 매니저를 삽입할 수 있습니다.
public class AccountRepositoryImpl implements AccountRepositoryCustom {
@PersistenceContext
private EntityManager em;
public void customMethod() {
...
em.createQuery(yourCriteria);
...
}
}
인터페이스를 추가할 필요가 없는 약간 변경된 솔루션이 있습니다.
문서화된 기능에 기재되어 있는 바와 같이Impl
접미사를 사용하면 다음과 같은 깨끗한 솔루션을 얻을 수 있습니다.
- 인 것을
@Repository
: " " ), " "MyEntityRepository
Spring Data(스프링 데이터(Spring Data) - " " " 를 만듭니다.
MyEntityRepositoryImpl
(the)Impl
suffix는 마법입니다(같은 패키지에 있을 필요도 없습니다). 커스텀 메서드만 구현하고 이러한 클래스에 주석을 붙입니다.@Component
(**)@Repository
동작하지 않습니다).- 는 심지어 도 할 수 있습니다.
MyEntityRepository
★★★★★★★★★★★★★★★★★를 통해@Autowired
이치노
- 는 심지어 도 할 수 있습니다.
예:
엔티티 클래스(완전성을 위해):
package myapp.domain.myentity;
@Entity
public class MyEntity {
@Id private Long id;
@Column private String comment;
}
저장소 인터페이스:
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); // custom method, code at *Impl class below
List<MyEntity> useTheRepo(Long id); // custom method, code at *Impl class below
}
커스텀 메서드 구현 bean:
package myapp.infrastructure.myentity;
@Component // Must be @Component !!
public class MyEntityRepositoryImpl { // must have the exact 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;
}
}
사용방법:
// You just autowire the the MyEntityRepository as usual
// (the Impl class is just impl detail, the clients don't even know about it)
@Service
public class SomeService {
@Autowired
private MyEntityRepository myEntityRepository;
public void someMethod(String x, long y) {
// call any method as usual
myEntityRepository.findByCommentEndsWith(x);
myEntityRepository.doSomeHql(y);
}
}
이상입니다. 이미 가지고 있는 Spring Data repo 이외의 인터페이스는 필요 없습니다.
제가 확인한 단점은 다음과 같습니다.
- 의
Impl
에 class는 class의 로 마크됩니다.@SuppressWarnings("unused")
★★★★★★ 。 - 은 1개입니다.
Impl
class. (일반 fragment인터페이스 실장에서는 다수의 fragment인터페이스가 실장되어 있는 경우) - 「 」를 ,
Impl
, 테스트에서는 'Da'만 되고 있습니다.@DataJpaTest
, , , , , , , , , , , , , , , , .@ComponentScan("package.of.the.impl.clazz")
스프링이 장전하는 거야
승인된 답변은 기능하지만 다음 세 가지 문제가 있습니다.
- 을 「스프링 데이터」라고 , 되어 있지 않은 합니다.
AccountRepositoryImpl
문서에는 호출해야 한다고 명시되어 있습니다.AccountRepositoryCustomImpl
플러스, 「」Impl
- 할 수 없습니다.단, 컨스트럭터 주입은 사용할 수 없습니다.
@Autowired
- 사용자 정의 구현 내부에 순환 종속성이 있으므로 생성자 주입을 사용할 수 없습니다.
문서화되어 있지 않은 다른 Spring Data 기능을 사용하지 않고서는 완벽할 수 없는 방법을 찾았습니다.
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
}
}
이 방법은 사용이 제한되지만 간단한 커스텀 방식에서는 다음과 같은 기본 인터페이스 방식을 사용할 수 있습니다.
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
고객님의 자산입니다.봄
커스텀 구현에서 생성된 검색 메서드에 액세스하기 위해 다음 코드를 사용합니다.콩 팩토리를 통해 구현함으로써 원두 생성 문제를 방지할 수 있습니다.
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);
}
}
코드 스니펫을 고려하면 네이티브 오브젝트는 findBy### 메서드에만 전달할 수 있습니다.예를 들어 특정 costumer에 속하는 계정 목록을 로드하는 경우, 이를 수행하는 방법이 있습니다.
@Query("Select a from Account a where a."#nameoffield"=?1")
List<Account> findByCustomer(String "#nameoffield");
Make sue 쿼리할 테이블의 이름이 엔티티 클래스와 동일합니다.추가 실장에 대해서는, 여기를 봐 주세요.
좀 더 복잡한 작업을 수행하려면 Spring Data 내부 액세스 권한이 필요할 수 있습니다. 이 경우 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);
}
}
}
여기서 고려해야 할 다른 문제가 있습니다.일부 사용자는 저장소에 사용자 지정 메서드를 추가하면 자동으로 '/search' 링크의 REST 서비스로 노출될 것으로 예상합니다.불행하게도 그렇지 않다.현재 봄은 그것을 지원하지 않는다.
이는 '설계상' 기능으로, 스프링 데이터 레스트는 메서드가 사용자 지정 메서드인지 여부를 명시적으로 확인하고 REST 검색 링크로 표시하지 않습니다.
private boolean isQueryMethodCandidate(Method method) {
return isQueryAnnotationPresentOn(method) || !isCustomMethod(method) && !isBaseClassMethod(method);
}
다음은 올리버 Gierke의 질문입니다.
이것은 고의로 만든 것이다.사용자 지정 리포지토리 메서드는 모든 동작을 효과적으로 구현할 수 있으므로 쿼리 메서드가 아닙니다.따라서 현재 HTTP 방식을 공개하는 것은 불가능합니다.POST가 가장 안전한 옵션이지만 GET를 수신하는 일반적인 쿼리 방법과는 일치하지 않습니다.
상세한 것에 대하여는, 다음의 문제를 참조해 주세요.https://jira.spring.io/browse/DATAREST-206
모든 저장소에 사용자 지정 동작을 추가하는 중:
모든 리포지토리에 사용자 지정 동작을 추가하려면 먼저 중간 인터페이스를 추가하여 공유 동작을 선언합니다.
public interface MyRepository <T, ID extends Serializable> extends JpaRepository<T, ID>
{
void sharedCustomMethod( ID id );
}
이제 개별 저장소 인터페이스가 Repository 인터페이스 대신 이 중간 인터페이스를 확장하여 선언된 기능을 포함합니다.
다음으로 퍼시스텐스 테크놀로지 고유의 저장소 기본 클래스를 확장하는 중간 인터페이스의 구현을 만듭니다.그런 다음 이 클래스는 저장소 프록시의 사용자 지정 기본 클래스로 작동합니다.
public class MyRepositoryImpl <T, ID extends Serializable> extends SimpleJpaRepository<T, ID> implements MyRepository<T, ID>
{
private EntityManager entityManager;
// There are two constructors to choose from, either can be used.
public MyRepositoryImpl(Class<T> domainClass, EntityManager entityManager)
{
super( domainClass, entityManager );
// This is the recommended method for accessing inherited class dependencies.
this.entityManager = entityManager;
}
public void sharedCustomMethod( ID id )
{
// implementation goes here
}
}
Danila의 솔루션이 마음에 들어 사용하기 시작했지만 팀 내 다른 누구도 각 저장소에 대해 4개의 클래스를 만들어야 하는 것을 좋아하지 않았습니다.Danila의 솔루션은 임팩트 클래스에서 스프링 데이터 방법을 사용할 수 있는 유일한 솔루션입니다.하지만, 저는 하나의 수업으로 그것을 할 수 있는 방법을 찾았습니다.
public interface UserRepository extends MongoAccess, PagingAndSortingRepository<User> {
List<User> getByUsername(String username);
default List<User> getByUsernameCustom(String username) {
// Can call Spring Data methods!
findAll();
// Can write your own!
MongoOperations operations = getMongoOperations();
return operations.find(new Query(Criteria.where("username").is(username)), User.class);
}
}
dbean(이 예에서는 Mongo Operations)에 액세스할 수 있는 방법만 있으면 됩니다.Mongo Access는 bean을 직접 취득함으로써 모든 저장소에 대한 액세스를 제공합니다.
public interface MongoAccess {
default MongoOperations getMongoOperations() {
return BeanAccessor.getSingleton(MongoOperations.class);
}
}
여기서 Bean Accessor는 다음과 같습니다.
@Component
public class BeanAccessor implements ApplicationContextAware {
private static ApplicationContext applicationContext;
public static <T> T getSingleton(Class<T> clazz){
return applicationContext.getBean(clazz);
}
public static <T> T getSingleton(String beanName, Class<T> clazz){
return applicationContext.getBean(beanName, clazz);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
BeanAccessor.applicationContext = applicationContext;
}
}
유감스럽게도 인터페이스에서는 @Autowire는 할 수 없습니다.콩을 Mongo AccessImpl에 자동 배선하여 인터페이스에 액세스하기 위한 메서드를 제공할 수 있지만 Spring Data는 폭발합니다.PagingAndSortingRepository와 간접적으로 관련지어도 Impact가 발생할 것으로는 생각하지 않습니다.
나는 몽고와 봄을 이용하여 이것을 직면했다.기본 crud 연산을 제공하기 위해 MongoRepository를 사용하고 mongoTemplate를 사용하여 커스텀 기준 쿼리 연산을 구현해야 한다고 가정합니다.crud 및 custom용 저장소를 주입하는 인터페이스를 하나 얻으려면 다음을 지정해야 합니다.
커스텀 인터페이스:
public interface UserCustomRepository {
List<User> findAllUsersBySomeCriteria(UserCriteriaRequest criteriaRequest);
}
User Repository 인터페이스는 먼저 User Custom Repository를 확장한 후 Mongo Repository를 확장합니다.
@Repository
public interface UserRepository extends UserCustomRepository, MongoRepository<User, ObjectId> {
}
UserRepositoryImpl은 *Impl 서픽스를 가진 crud 인터페이스의 이름과 같아야 합니다.
@Component
@NoArgsConstructor
@AllArgsConstructor(onConstructor = @__(@Autowired))
public class UserRepositoryImpl implements UserCustomRepository {
private MongoTemplate mongoTemplate;
@Override
public List<User> findAllUsersBySomeCriteria(UserCriteriaRequest criteriaRequest){
//some impl
}
}
서비스에 대해 설명하겠습니다.여기에서는 UserRepository 인터페이스만 주입하고 crud 저장소 및 커스텀 클래스 includ 메서드를 사용합니다.
@Service
@NoArgsConstructor
@AllArgsConstructor(onConstructor = @__(@Autowired))
public class UserService {
private UserRepository userReposityry;
public List<User> getUserByCriteria(UserCriteriaRequest request) {
userRepository.findById(request.getUserId); // Crud repository method
userRepository.findAllUsersBySomeCriteria(request); // custom method.
}
}
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;
}
}
이 클래스를 @EnableJpaRepositoryries repositoryBaseClass에 추가합니다.
SimpleJpaRepository를 저장소 구현의 기본 클래스로 사용하고 인터페이스에 다음과 같은 커스텀 메서드를 추가합니다.
public interface UserRepository {
User FindOrInsert(int userId);
}
@Repository
public class UserRepositoryImpl extends SimpleJpaRepository implements UserRepository {
private RedisClient redisClient;
public UserRepositoryImpl(RedisClient redisClient, EntityManager em) {
super(User.class, em);
this.redisClient = redisClient;
}
@Override
public User FindOrInsert(int userId) {
User u = redisClient.getOrSet("test key.. User.class, () -> {
Optional<User> ou = this.findById(Integer.valueOf(userId));
return ou.get();
});
…………
return u;
}
언급URL : https://stackoverflow.com/questions/11880924/how-to-add-custom-method-to-spring-data-jpa
'programing' 카테고리의 다른 글
요소를 효율적으로 검색하는 방법 (0) | 2022.07.11 |
---|---|
텍스트 영역의 스크롤 하이트의 올바른 값을 얻을 수 없습니다(VueJs, Vuetify). (0) | 2022.07.11 |
프로젝트 패싯 Dynamic Web Module 버전을 3.0으로 변경할 수 없습니다. (0) | 2022.07.10 |
C/C++에서 0 사이즈의 어레이를 정의하면 어떻게 됩니까? (0) | 2022.07.10 |
_vuex.default.store가 생성자가 아닙니다. (0) | 2022.07.10 |