我对 String Boot 和后端开发(可能需要三天或更短的时间)非常陌生,我希望构建 REST API 以便从不同的客户端使用。
因此,我从一个简单的演示应用程序开始,该应用程序具有一个名为/register的终结点。如果不存在,我们将JSON字符串与usernamepassword一起发布,以创建一个新用户。
我在将JPAHSQLDB一起使用,并且可以在内存上正常工作。但是最近我想使用RxJava,因为我在Android上很熟悉,所以我使用R2DBC切换到MySQLMySQL服务器在3306端口上运行良好,并且该应用程序已在localhost:8080上使用PostMan进行了测试
当我尝试查询用户表或插入实体时出现问题,它看起来像这样:

{
    "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"
}
这是异常(exception)情况的完整logfile
我一直在寻找解决方案达数小时之久,但似乎找不到任何解决方案,因此希望在这里找到它。
让我们分解项目,以便更轻松地找到解决方案:
1.数据库:
java - 带有MySQL的Spring Boot R2DBC-异常(exception):找不到表-LMLPHP
2. application.properties:
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. RegistrationController:
@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. UserService:
@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) }

        }
}
UserRegistrationRequest只是带有用户名和密码的简单对象。
我错过了什么?
如果您需要更多代码,请发表评论。

最佳答案

我终于设法解决了这个错误!

这些问题非常简单,但是对于初学者来说却是一个 secret :

  • 我在URL中使用的是JDBC,而不是R2DBC
  • 我正在使用H2运行时实现,因此
    期待一个H2内存数据库
  • 我的ConnectionFactory不太正确

  • 所以我做了以下事情:
  • 更新了我的build.gradle:
  • 添加:implementation("io.r2dbc:r2dbc-pool")implementation("dev.miku:r2dbc-mysql:0.8.1.RELEASE")runtimeOnly("mysql:mysql-connector-java")
  • 删除:runtimeOnly("io.r2dbc:r2dbc-h2")

  • 现在看起来像这样:
    import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
    
    plugins {
        id("org.springframework.boot") version "2.2.5.RELEASE"
        id("io.spring.dependency-management") version "1.0.9.RELEASE"
        kotlin("jvm") version "1.3.61"
        kotlin("plugin.spring") version "1.3.61"
    }
    
    group = "com.tamimattafi.backend"
    version = "0.0.1-SNAPSHOT"
    java.sourceCompatibility = JavaVersion.VERSION_11
    
    repositories {
        mavenCentral()
        maven(url = "https://repo.spring.io/milestone")
    }
    
    dependencies {
        //SPRING BOOT
        implementation("org.springframework.boot:spring-boot-starter-security")
        implementation("org.springframework.boot:spring-boot-starter-web")
        implementation("org.springframework.boot.experimental:spring-boot-starter-data-r2dbc")
    
        //KOTLIN
        implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
        implementation("org.jetbrains.kotlin:kotlin-reflect")
        implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
    
        //RX JAVA
        implementation("io.reactivex.rxjava2:rxjava:2.2.0")
        implementation("io.reactivex:rxjava-reactive-streams:1.2.1")
    
        //MYSQL
        implementation("dev.miku:r2dbc-mysql:0.8.1.RELEASE")
        implementation("io.r2dbc:r2dbc-pool")
        runtimeOnly("mysql:mysql-connector-java")
    
        //TEST
        testImplementation("org.springframework.boot:spring-boot-starter-test") {
            exclude(group = "org.junit.vintage", module = "junit-vintage-engine")
        }
        testImplementation("org.springframework.security:spring-security-test")
        testImplementation("io.projectreactor:reactor-test")
        testImplementation("org.springframework.boot.experimental:spring-boot-test-autoconfigure-r2dbc")
    }
    
    dependencyManagement {
        imports {
            mavenBom("org.springframework.boot.experimental:spring-boot-bom-r2dbc:0.1.0.M3")
        }
    }
    
    tasks.withType<Test> {
        useJUnitPlatform()
    }
    
    tasks.withType<KotlinCompile> {
        kotlinOptions {
            freeCompilerArgs = listOf("-Xjsr305=strict")
            jvmTarget = "1.8"
        }
    }
    
  • 将我的application.properties更新为:
    spring.r2dbc.url=r2dbc:pool:mysql://127.0.0.1:3306/demodb
    spring.r2dbc.username=root
    spring.r2dbc.password=root
    
  • 将我的DatabaseConfiguration更新为此(请注意,我删除了@EnableR2dbcRepositories,因为它应该在其他位置):
    @Configuration
    class DatabaseConfiguration : AbstractR2dbcConfiguration() {
    
    override fun connectionFactory(): ConnectionFactory
        = MySqlConnectionFactory.from(
            MySqlConnectionConfiguration.builder()
                    .host("127.0.0.1")
                    .username("root")
                    .port(3306)
                    .password("root")
                    .database("demodb")
                    .connectTimeout(Duration.ofSeconds(3))
                    .useServerPrepareStatement()
                    .build()
        )
    
    }
    
  • 更新了我的Application类(我在此处添加了注释):
    @SpringBootApplication
    @EnableR2dbcRepositories
    class DemoApplication
    
    fun main(args: Array<String>) {
        runApplication<DemoApplication>(*args)
    }
    

  • 现在工作!我希望有人会觉得这个有用,快乐编码!

    07-24 09:19