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

Posted

技术标签:

【中文标题】带有 MySQL 的 Spring Boot R2DBC - 异常:找不到表【英文标题】:Spring Boot R2DBC with MySQL - Exception: Table not found 【发布时间】:2020-07-03 01:58:46 【问题描述】:

我对 String Boot 和后端开发(可能三天或更短时间)非常陌生,我希望构建 REST API 以从不同的客户端使用。

所以我从一个简单的演示应用开始,它有一个名为/register 的端点。我们发布一个带有usernamepasswordJSON 字符串以创建一个新用户(如果不存在)。

我将JPAHSQLDB 一起使用,它在内存上运行良好。但是最近我想用RxJava,因为我熟悉android,所以我切换到R2DBCmysql

MySQL 服务器在端口 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"

这是异常的完整 logfile。

我一直在寻找解决方案几个小时,但我似乎在任何地方都找不到它,所以我希望我能在这里找到它。

让我们分解项目以便更容易找到解决方案:

1.数据库:

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.注册控制器:

@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) 

        

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

【问题讨论】:

你手动创建了user_entity表吗? @shazin 是的,正如您在第一个屏幕截图中看到的那样,我使用 IntelliJ 控制台来执行此操作,它会在 MySQL Workbench 和 Terminal 中做出反应。 【参考方案1】:

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

对于初学者来说,问题如此简单却又如此偷偷摸摸:

    我在我的 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)
    
    

现在可以使用了!我希望有人会觉得这很有帮助,祝编程愉快!

【讨论】:

【参考方案2】:

application.properties 中,您需要设置spring.jpa.hibernate.ddl-auto 属性。

选项有:

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

【讨论】:

以上是关于带有 MySQL 的 Spring Boot R2DBC - 异常:找不到表的主要内容,如果未能解决你的问题,请参考以下文章

Spring boot(带有 jpa 的 mysql):没有名为“entityManagerFactory”的 bean 可用

使用带有 LIKE 的 mysql 本机查询的 spring boot 搜索返回空

带有存储过程的 Spring Boot MySQL 数据库初始化错误

尝试使用 mysql 学习带有外键和一对多关系关系的 Spring Boot 但是

使用带有 MySQL 数据库的 Spring Boot Rest API 的一对一映射

带有 mysql 的 Spring Boot 应用程序卡在“Hikari-Pool-1 - 正在启动...”