저는 String Boot 및 백엔드 개발(아마도 3일 이하)을 처음 접했고 다른 클라이언트에서 사용할 REST API 를 구축하고 싶습니다.
그래서 저는 이라는 엔드포인트가 있는 간단한 데모 앱으로 시작했습니다 /register
. 존재하지 않는 경우 새 사용자를 생성하기 위해 JSON
문자열을 username
게시 합니다 .password
나는 함께 사용 JPA
하고 HSQLDB
있었고 메모리에 잘 작동했습니다. RxJava
그런데 최근 안드로이드에 익숙해져서 사용하고 싶어서 R2DBC
with 로 바꿨습니다 MySQL
.
MySQL
서버가 포트에서 제대로 실행 중이고 3306
앱이 PostMan을 사용하여 테스트되었습니다.localhost:8080
사용자 테이블을 쿼리하거나 엔터티를 삽입하려고 하면 문제가 발생하며 다음과 같습니다.
{
"timestamp": "2020-03-22T11:54:43.466+0000",
"status": 500,
"error": "Internal Server Error",
"message": "execute; bad SQL grammar [UPDATE user_entity SET username = $1, password = $2 WHERE user_entity.id = $3]; nested exception is io.r2dbc.spi.R2dbcBadGrammarException: [42102] [42S02] Table \"USER_ENTITY\" not found; SQL statement:\nUPDATE user_entity SET username = $1, password = $2 WHERE user_entity.id = $3 [42102-200]",
"path": "/register"
}
다음 은 예외에 대한 전체 로그 파일 입니다.
나는 몇 시간 동안 해결책을 찾고 있었지만 어디에서도 찾을 수 없는 것 같아서 여기에서 찾을 수 있기를 바랍니다.
솔루션을 더 쉽게 찾을 수 있도록 프로젝트를 세분화해 보겠습니다.
1. 데이터베이스:
2. 애플리케이션.속성:
logging.level.org.springframework.data.r2dbc=DEBUG
spring.datasource.url=jdbc:mysql://localhost:3306/demodb
spring.datasource.username=root
spring.datasource.password=root
3. 데이터베이스 구성:
@Configuration
@EnableR2dbcRepositories
class DatabaseConfiguration : AbstractR2dbcConfiguration() {
override fun connectionFactory(): ConnectionFactory
= ConnectionFactories.get(
builder().option(DRIVER, "mysql")
.option(HOST, "localhost")
.option(USER, "root")
.option(PASSWORD, "root")
.option(DATABASE, "demodb")
.build()
)
}
4. 등록 컨트롤러:
@RequestMapping("/register")
@RestController
class RegistrationController @Autowired constructor(private val userService: UserService) {
@PostMapping
fun login(@RequestBody registrationRequest: RegistrationRequest): Single<ResponseEntity<String>>
= userService.userExists(registrationRequest.username)
.flatMap { exists -> handleUserExistance(exists, registrationRequest) }
private fun handleUserExistance(exists: Boolean, registrationRequest: RegistrationRequest): Single<ResponseEntity<String>>
= if (exists) Single.just(ResponseEntity("Username already exists. Please try an other one", HttpStatus.CONFLICT))
else userService.insert(User(registrationRequest.username, registrationRequest.password)).map { user ->
ResponseEntity("User was successfully created with the id: ${user.id}", HttpStatus.CREATED)
}
}
5. 사용자 서비스:
@Service
class UserService @Autowired constructor(override val repository: IRxUserRepository) : RxSimpleService<User, UserEntity>(repository) {
override val converter: EntityConverter<User, UserEntity> = UserEntity.Converter
fun userExists(username: String): Single<Boolean>
= repository.existsByUsername(username)
}
6. RxSimpleService:
abstract class RxSimpleService<T, E>(protected open val repository: RxJava2CrudRepository<E, Long>) {
protected abstract val converter: EntityConverter<T, E>
open fun insert(model: T): Single<T>
= repository.save(converter.fromModel(model))
.map(converter::toModel)
open fun get(id: Long): Maybe<T>
= repository.findById(id)
.map(converter::toModel)
open fun getAll(): Single<ArrayList<T>>
= repository.findAll()
.toList()
.map(converter::toModels)
open fun delete(model: T): Completable
= repository.delete(converter.fromModel(model))
}
7. RxUserRepository:
@Repository
interface IRxUserRepository : RxJava2CrudRepository<UserEntity, Long> {
@Query("SELECT CASE WHEN EXISTS ( SELECT * FROM ${UserEntity.TABLE_NAME} WHERE username = :username) THEN CAST(1 AS BIT) ELSE CAST(0 AS BIT) END")
fun existsByUsername(username: String): Single<Boolean>
}
8. 마지막으로 여기 내 UserEntity가 있습니다.
@Table(TABLE_NAME)
data class UserEntity(
@Id
val id: Long,
val username: String,
val password: String
) {
companion object {
const val TABLE_NAME = "user_entity"
}
object Converter : EntityConverter<User, UserEntity> {
override fun fromModel(model: User): UserEntity
= with(model) { UserEntity(id, username, password) }
override fun toModel(entity: UserEntity): User
= with(entity) { User(id, username, password) }
}
}
User
사용자 이름 과 RegistrationRequest
암호가 있는 단순한 개체입니다. 내가 무엇을 놓쳤습니까? 더 많은 코드가 필요하면 댓글을 남겨주세요.