사용자 정의 Spring 데이터 저장소
이 길은 좀 좁을 수 있다.나는 Spring 생태계의 충실한 팬이다. 왜냐하면 그의 디자인은 서로 다른 추상적인 차원에서 확장 가능하고 맞춤형이기 때문이다. 따라서 이 경로는 네가 필요로 하는 크기이다.
함수식 프로그래밍이 갈수록 유행하고 있다.Spring은 Kotlin 언어에 몇 개의 DSL을 제공합니다.예를 들어 Beans DSL와 Routes DSL는 용수철 배치에 대해 더욱 기능적인 방법을 사용할 수 있도록 허용한다.유형별로는 Vavr(예전에는 자바슬램)이 자바에서 유행했고, 코틀린Arrow이 자바에서 유행했다.
이 글에서 Arrow의 유형 시스템을 Spring 데이터와 어떻게 결합시켜 사용하는지 설명하고 싶습니다.최종적으로 이러한 해석을 통해 맞춤형 Spring 데이터 저장소를 구축할 수 있습니다.
기점건물
내 애플리케이션의 초기 아키텍처는 매우 표준적입니다.
GET
매핑된 REST 컨트롤러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
ref()
Spring 응용 프로그램 상하문beans()
함수, 더 이상 마력이 없습니다!아로를 소개합니다.
Functional companion to Kotlin's Standard Library
-- Arrow
Arrow에는
Either<E,T>
유형.Arrow는 선택적 값에 대한 모델링을 위해 Either<Unit,T>
을 사용하는 것이 좋습니다.한편, 스프링 데이터 JDBCfindById()
는 하나java.util.Optional<T>
를 되돌려준다.격차를 줄이다
우리는
Optional
와Either
사이의 차이를 어떻게 메웁니까?여기는 첫 번째 시도입니다.
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 데이터는 JdbcRepositoryFactoryBean
bean을 통해 클래스를 찾아 새 인스턴스를 만들고 컨텍스트에 인스턴스를 등록합니다.기본 저장소
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 에서 찾을 수 있습니다.
한 걸음 더 나아가 말하면:
Reference
이 문제에 관하여(사용자 정의 Spring 데이터 저장소), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/nfrankel/your-own-custom-spring-data-repository-140g텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)