사용자 정의 Spring 데이터 저장소

주류 경로만 따르면 개발 속도를 높일 수 있다는 게 틀의 약속이다.
이 길은 좀 좁을 수 있다.나는 Spring 생태계의 충실한 팬이다. 왜냐하면 그의 디자인은 서로 다른 추상적인 차원에서 확장 가능하고 맞춤형이기 때문이다. 따라서 이 경로는 네가 필요로 하는 크기이다.
함수식 프로그래밍이 갈수록 유행하고 있다.Spring은 Kotlin 언어에 몇 개의 DSL을 제공합니다.예를 들어 Beans DSLRoutes DSL는 용수철 배치에 대해 더욱 기능적인 방법을 사용할 수 있도록 허용한다.유형별로는 Vavr(예전에는 자바슬램)이 자바에서 유행했고, 코틀린Arrow이 자바에서 유행했다.
이 글에서 Arrow의 유형 시스템을 Spring 데이터와 어떻게 결합시켜 사용하는지 설명하고 싶습니다.최종적으로 이러한 해석을 통해 맞춤형 Spring 데이터 저장소를 구축할 수 있습니다.

기점건물


내 애플리케이션의 초기 아키텍처는 매우 표준적입니다.
  • 2개GET 매핑된 REST 컨트롤러
  • Spring 데이터 JDBC 저장소 커넥터
  • 표준이기 때문에 스프링은 많은 파이프를 처리하기 때문에 우리는 많은 코드를 작성할 필요가 없다.Kotlin이 있으면 더 깔끔해집니다.
    class Person(@Id val id: Long, var name: String, var birthdate: LocalDate?)
    
    interface PersonRepository : CrudRepository<Person, Long>
    
    @RestController
    class PersonController(private val repository: PersonRepository) {
    
        @GetMapping
        fun getAll(): Iterable<Person> = repository.findAll()
    
        @GetMapping("/{id}")
        fun getOne(@PathVariable id: Long) = repository.findById(id)
    }
    
    @SpringBootApplication
    class SpringDataArrowApplication
    
    fun main(args: Array<String>) {
        runApplication<SpringDataArrowApplication>(*args)
    }
    

    더욱 실용적인 방법으로 나아가다


    이 단계는 Spring 데이터와 무관하며 필요하지도 않지만 함수 방법에 더 적합합니다.위에서 설명한 바와 같이 라우팅 및 bean DSL 사용의 이점을 누릴 수 있습니다.가능한 한 많은 주석을 삭제하기 위해 상술한 코드를 재구성합시다.
    class PersonHandler(private val repository: PersonRepository) {                   // 1
    
      fun getAll(req: ServerRequest) = ServerResponse.ok().body(repository.findAll()) // 2
      fun getOne(req: ServerRequest): ServerResponse = repository
        .findById(req.pathVariable("id").toLong())
        .map { ServerResponse.ok().body(it) }
        .orElse(ServerResponse.notFound().build())                                    // 3
    }
    
    fun beans() = beans {                                                             // 4
      bean<PersonHandler>()
      bean {
        val handler = ref<PersonHandler>()                                           // 5
        router {
          GET("/", handler::getAll)
          GET("/{id}", handler::getOne)
        }
      }
    }
    
    fun main(args: Array<String>) {
      runApplication<SpringDataArrowApplication>(*args) {
        addInitializers(beans())                                                      // 6
      }
    }
    
  • 루트 함수를 구성하는 프로세스 클래스를 만듭니다
  • 모든 루트 함수는 ServerRequest 매개 변수를 받아들여 되돌아와야 한다ServerResponse
  • 추가 기능 추가: 엔티티를 찾을 수 없으면 404
  • 로 돌아가기
  • 라우팅 DSL을 사용하여 HTTP 술어와 경로를 라우팅 함수에 매핑
  • ref() Spring 응용 프로그램 상하문
  • 에서 설정 형식의 bean을 검색합니다
  • 현식호출beans()함수, 더 이상 마력이 없습니다!
  • 아로를 소개합니다.


    Functional companion to Kotlin's Standard Library

    -- Arrow


    Arrow에는
  • 코어
  • FX: 기능 효과 프레임워크와 KotlinX 협동 프로그램
  • 광학: 변하지 않는 데이터에 대한 깊이 있는 접근과 변환
  • Meta: Kotlin 컴파일러 플러그인의 모듈 라이브러리 핵심 라이브러리 제공Either<E,T> 유형.Arrow는 선택적 값에 대한 모델링을 위해 Either<Unit,T>을 사용하는 것이 좋습니다.한편, 스프링 데이터 JDBCfindById()는 하나java.util.Optional<T>를 되돌려준다.

    격차를 줄이다


    우리는 OptionalEither 사이의 차이를 어떻게 메웁니까?
    여기는 첫 번째 시도입니다.
    repository
        .findById(req.pathVariable("id").toLong())      // 1
        .map { Either.fromNullable(it) }                // 2
        .map { either ->
            either.fold(
                { ServerResponse.notFound().build() },  // 3
                { ServerResponse.ok().body(it) }        // 3
            )
        }.get()                                         // 4
    
  • Optional<Person>
  • Optional<Either<Unit, Person>>
  • Optional<ServerResponse>
  • 이 때 우리는 안전하게 get()로 전화를 걸어 ServerResponse를 얻을 수 있다Optional<Either<Unit,Person>>
  • 나는 Optional<Person>의 용법이 그리 좋지 않다고 믿는다.그러나 Kotlin은 다음과 같은 기능을 확장할 수 있습니다.
    private fun <T> Optional<T>.toEither() =
        if (isPresent) Either.right(get())
        else Unit.left()
    
    이 기능을 통해 기존 코드를 개선할 수 있습니다.
    repository
        .findById(req.pathVariable("id").toLong())    // 1
        .toEither()                                   // 2
        .fold(
            { ServerResponse.notFound().build() },    // 3
            { ServerResponse.ok().body(it) }          // 3
        )
    
  • Either<Unit, Person>
  • ServerResponse
  • Either<Unit,Person>
  • 이렇게 하면 더 보기 좋지만, 저장소를 직접 되돌려 주는 것이 더 좋다.

    Spring 데이터 사용자 정의


    이를 위해 Spring 데이터를 사용자 정의하는 방법을 살펴보겠습니다.
    기본적으로 Spring 데이터 저장소는 사용자가 예상할 수 있는 모든 공통 기능을 제공합니다.예를 들면 다음과 같습니다.

    Spring 데이터는 사용하기 쉽도록 설계되었으나 확장성은 그대로 유지됩니다.
    기본 단계에서 특정 명명 모드를 따르는 함수를 추가할 수 있습니다. 예를 들어 findByFirstNameAndLastNameOrderByLastName().Spring 데이터는 SQL 행을 작성하지 않고도 구현 코드를 생성합니다.이 메서드의 한계에 도달하면 실행하려는 SQL 주석 함수를 사용할 수 있습니다.
    이 두 가지 상황에서 되돌아오는 형식을 설정해야 한다.number of possible return types는 상당히 크지만 여전히 한계가 있다.이 프레임워크는 모든 가능한 유형을 고려할 수 없습니다. 구체적으로 말하면 목록에 포함되지 않습니다. Either다음 확장 단계는 사용자 정의를 통해 서명이 필요한 함수를 추가하는 것입니다.이를 위해서는 다음이 필요합니다.
  • 필요한 함수를 설명하는 인터페이스
  • 인터페이스를 실현하는 클래스
  • interface CustomPersonRepository {                                         // 1
        fun arrowFindById(id: Long): Either<Unit, Person>                      // 2
    }
    
    class CustomPersonRepositoryImpl(private val ops: JdbcAggregateOperations) // 3
        : CustomPersonRepository {                                             // 4
    
        override fun arrowFindById(id: Long) =
            Either.fromNullable(ops.findById(id, Person::class.java))           // 5
    }
    
    interface PersonRepository
        : CrudRepository<Person, Long>, CustomPersonRepository                 // 6
    
  • 새로운 사용자 지정 인터페이스
  • 원하는 함수 성명
  • 새로운 실현류...
  • ... 부모 인터페이스 구현
  • 실행 기능
  • 사용자 정의 인터페이스만 확장
  • 지금 우리는 다음과 같이 할 수 있다.
    repository.arrowFindById(req.pathVariable("id").toLong())
        .fold(
            { ServerResponse.notFound().build() },
            { ServerResponse.ok().body(it) }
        )
    
    이런 방법은 효과가 있지만 주요한 결함이 하나 있다.함수 서명 충돌을 피하기 위해서, 우리는 되돌아오는 Either 함수에 원시 이름 arrowFindById() 을 만들어야 한다.

    기본 저장소 변경


    이 제한을 극복하기 위해서 우리는 기본 저장소를 바꾸는 또 다른 확장점을 이용할 수 있다.
    Spring 데이터 응용 프로그램은 인터페이스를 정의하지만 구현을 위해 필요한 위치가 있습니다.기본적으로 프레임워크는 하나를 제공하지만, 우리만의 프레임워크로 전환할 수도 있습니다.
    다음은 클래스 맵의 개요입니다.

    상세한 절차는 상당히 복잡하다. 중요한 부분은 SimpleJdbcRepository류다.Spring 데이터는 JdbcRepositoryFactoryBeanbean을 통해 클래스를 찾아 새 인스턴스를 만들고 컨텍스트에 인스턴스를 등록합니다.
    기본 저장소Either를 만들겠습니다.
    @NoRepositoryBean
    interface ArrowRepository<T, ID> : Repository<T, ID> {         // 1
        fun findById(id: Long): Either<Unit, T>                    // 2
        fun findAll(): Iterable<T>                                 // 3
    }
    
    class SimpleArrowRepository<T, ID>(                            // 4
        private val ops: JdbcAggregateOperations,
        private val entity: PersistentEntity<T, *>
    ) : ArrowRepository<T, ID> {
    
        override fun findById(id: Long) = Either.fromNullable(
            ops.findById(id, entity.type)                          // 5
        )
    
        override fun findAll(): Iterable<T> = ops.findAll(entity.type)
    }
    
  • 우리의 새로운 인터페이스 저장소...
  • ...우리가 선택한 서명은 어떠한 충돌 위험도 없다.
  • 나는 너무 게을러서 모든 것을 실현할 수 없다.
  • 메모리 라이브러리 인터페이스의 기본적인 실현.구조 함수는 이 두 매개 변수를 받아들여야 한다.
  • 바퀴를 재발명하지 말기;기존JdbcAggregateOperations의 실례를 사용하다.
  • 우리는 @EnableJdbcRepositories로 메인 응용 프로그램 클래스를 설명하고 후자를 이 기본 클래스로 전환해야 한다.
    @SpringBootApplication
    @EnableJdbcRepositories(repositoryBaseClass = SimpleArrowRepository::class)
    class SpringDataArrowApplication
    
    클라이언트 코드 사용을 단순화하기 위해 기본값을 덮어쓰는 주석을 만들 수 있습니다.
    @EnableJdbcRepositories(repositoryBaseClass = SimpleArrowRepository::class)
    annotation class EnableArrowRepositories
    
    현재 사용법은 매우 간단하다.
    @SpringBootApplication
    @EnableArrowRepositories
    class SpringDataArrowApplication
    
    이 때 Arrow 저장소 코드를 프로젝트로 이동하여 다른 클라이언트 프로젝트에 나누어 사용할 수 있습니다.Spring 데이터가 공장 bean 전환과 같은 더 많은 기능을 제공하지만 더 이상 확장할 필요가 없습니다.

    결론


    Spring Data는 기존 저장소를 제공합니다.이것이 부족할 때, 그 유연한 디자인은 서로 다른 추상적인 단계에서 코드를 확장하는 것을 가능하게 한다.
    이 글은 함수 서명에 화살표 형식을 사용하는 기본 라이브러리를 우리의 라이브러리로 바꾸는 방법을 보여 줍니다.
    그의 평론에 감사하다.
    이 글의 전체 소스 코드는 마븐 형식의 Github 에서 찾을 수 있습니다.
    한 걸음 더 나아가 말하면:
  • Working with Spring Data repositories
  • Custom Implementations for Spring Data Repositories
  • Customize the Base Repository
  • 최초 발표는 2021년 4월 11일A Java Geek

    좋은 웹페이지 즐겨찾기