简体   繁体   English

带有 MySQL 的 Spring Boot R2DBC - 异常:找不到表

[英]Spring Boot R2DBC with MySQL - Exception: Table not found

I'm extremely new to String Boot and backend development (maybe three days or less) and I have the desire to build REST API to consume from different clients.我对String Boot和后端开发(可能三天或更短)非常陌生,我希望构建REST API以从不同的客户端使用。

So I started by a simple demo app that has an endpoint called /register .所以我从一个简单的演示应用程序开始,它有一个名为/register的端点。 We post a JSON string with username and password to create a new user if not exist.我们发布一个带有usernamepasswordJSON字符串以创建一个新用户(如果不存在)。

I was using JPA with HSQLDB and it worked fine persisting on memory.我在HSQLDB中使用了JPA ,它在内存上的持久化工作得很好。 But recently I wanted to use RxJava since I'm familiar with on Android, so I switched to R2DBC with MySQL .但是最近我想使用RxJava因为我在 Android 上很熟悉,所以我切换到R2DBC with MySQL

MySQL server is running fine on port 3306 and the app was tested using PostMan on localhost:8080 MySQL服务器在端口3306上运行良好,应用程序在localhost:8080上使用 PostMan 进行了测试

The problem occurs when I try to query users table or insert entities and it looks like this:当我尝试查询用户表或插入实体时出现问题,它看起来像这样:

{
    "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"
}

Here's the full logfile for the exception.这是异常的完整日志文件

I Have been looking for a solution for hours and I seem like not finding it anywhere, so I hope that I will find it here.我一直在寻找解决方案几个小时,但似乎在任何地方都找不到,所以我希望我能在这里找到它。

Let's break down the project so it's easier to find the solution:让我们分解项目,以便更容易找到解决方案:

1. database: 1. 数据库:

在此处输入图片说明

2. application.properties: 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. DatabaseConfiguration: 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: 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. UserService: 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: 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: 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. And finally, here's my UserEntity 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 and RegistrationRequest are just simple objects with username and password. UserRegistrationRequest只是带有用户名和密码的简单对象。 What I have missed?我错过了什么? Please leave a comment if you need more code.如果您需要更多代码,请发表评论。

I finally managed to solve this mistake!我终于设法解决了这个错误!

The problems were so simple yet so sneaky for a beginner:对于初学者来说,问题是如此简单但又如此狡猾:

  1. I was using JDBC in my URL instead of R2DBC我在 URL 中使用JDBC而不是R2DBC
  2. I was using the H2 runtime implementation so it was expecting an H2 in-memory database我正在使用H2运行时实现,所以它期待一个H2内存数据库
  3. My ConnectionFactory was not very correct我的ConnectionFactory不是很正确

So what I did was the following:所以我所做的是以下内容:

  1. Updated my build.gradle :更新了我的build.gradle
    • Added: implementation("io.r2dbc:r2dbc-pool") , implementation("dev.miku:r2dbc-mysql:0.8.1.RELEASE") and runtimeOnly("mysql:mysql-connector-java")添加: implementation("io.r2dbc:r2dbc-pool")implementation("dev.miku:r2dbc-mysql:0.8.1.RELEASE")runtimeOnly("mysql:mysql-connector-java")
    • Removed: runtimeOnly("io.r2dbc:r2dbc-h2")删除: runtimeOnly("io.r2dbc:r2dbc-h2")

It now looks like this:现在看起来像这样:

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"
    }
}
  1. Updated my application.properties to this:将我的application.properties更新为:

     spring.r2dbc.url=r2dbc:pool:mysql://127.0.0.1:3306/demodb spring.r2dbc.username=root spring.r2dbc.password=root
  2. Updated my DatabaseConfiguration to this (Note that I removed the @EnableR2dbcRepositories because it should be elsewhere) :将我的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() ) }
  3. Updated my Application class (I brought the annotation here):更新了我的Application类(我在这里带了注释):

     @SpringBootApplication @EnableR2dbcRepositories class DemoApplication fun main(args: Array<String>) { runApplication<DemoApplication>(*args) }

IT WORKS NOW!现在可以使用了! I hope someone will find this helpful, Happy Coding!我希望有人会发现这有帮助,快乐编码!

In application.properties you need to set the spring.jpa.hibernate.ddl-auto property.application.properties您需要设置spring.jpa.hibernate.ddl-auto属性。

The options are:选项是:

validate: validate the schema, makes no changes to the database.
update: update the schema.
create: creates the schema, destroying previous data.
create-drop: drop the schema when the SessionFactory is closed explicitly, typically when the application is stopped.
none: does nothing with the schema, makes no changes to the database

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM