2025年3月14日 星期五 甲辰(龙)年 月十三 设为首页 加入收藏
rss
您当前的位置:首页 > 计算机 > 编程开发 > Spring Boot

Spring Boot MySQL入门以及Spring Boot对Kotlin支持的一些Bug

时间:02-07来源:作者:点击数:36

Bug重现

本人是做Android开发的,不做后端。目前公司有一个应用,功能界面都做了,但是请求接口还没做好,这时我就想自己做个接口,简单做个接口就行了,当然,市面上有那种可以模拟出后台接口的网站,但是我想自己写会比较好一点,比较灵活。跟公司同事了解到,现在Java搞接口已经不用Servlet了,使用Spring Boot,于是请教了他Spring Boot的使用,确实很简单,不需要学什么东西,真的是看一眼就会了。因为我在Android上已经习惯了Kotlin开发,看到Spring Boot也是支持Android的,于是也使用Kotlin了做Spring Boot开发,结果刚进门就踩坑了,真倒霉,现在记录一下:

  • 使用IntelliJ创建Spring Boot项目,如下:
    在这里插入图片描述
    如上图,选择Kotlin和Gradle,然后点击“下一步”,在出现的依赖项窗口中添加需要的依赖,因为我这里需要用到MySQL,所以选择的依赖包含有Spring Data JPA和MySQL Driver如下:
    在这里插入图片描述
    点击“完成”按钮,项目结构如下:
    在这里插入图片描述
  1. 访问MySQL数据我是参考的官方文档:https://spring.io/guides/gs/accessing-data-mysql/,项目创建好之后,需要先在MySQL中创建好数据库,官方创建数据库的代码如下:
    • mysql> create database db_example; -- 创建新数据库
    • mysql> create user 'springuser'@'%' identified by 'ThePassword'; -- 创建用户
    • mysql> grant all on db_example.* to 'springuser'@'%'; -- Gives all privileges to the new user on the newly created database

    执行上面的MySQL命令,创建了一个数据库,名为:db_example,并且创建了一个用户,用户名为:springuser,密码是:ThePassword,第三个命令是给这个springuser用户授权的。

    如果此时运行Spring Boot项目是会报错的,如下:

    Failed to configure a DataSource: ‘url’ attribute is not specified and no embedded datasource could be configured.

    这是因为我们的项目配置的时候是指定了要使用MySQL的,但是我们并没有配置连接MySQL的相关信息,在application.properties文件中添加如下配置:

    • spring.jpa.hibernate.ddl-auto=update
    • spring.datasource.url=jdbc:mysql://${MYSQL_HOST:localhost}:3306/db_example
    • spring.datasource.username=springuser
    • spring.datasource.password=ThePassword
    • spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
    • spring.jpa.open-in-view=false
    • #spring.jpa.show-sql: true

    此时我们再运行就不会报错了。

  2. 创建Controller

    这里是第一个坑,因为Spring Boot官方文档是Java的,所以我把MainController直接复制了过来,发现访问不了,我修改简化后如下:

    • @RestController
    • @RequestMapping("/demo")
    • public class MainController {
    • @GetMapping("hello")
    • public String hello() {
    • return "Hello Spring Boot!";
    • }
    • }

    运行项目没有报任何错误,然后在浏览器中访问:localhost:8080/demo/hello,如下:
    在这里插入图片描述
    如上图,并没有出现我们期待的"Hello Spring Boot!"字符串,控制台也没有出现错误,见鬼了。如果开始创建Spring Boot项目的时候选择了Java语言,就不会有这个问题,开始我还怀疑是Spring Boot不支持Kotlin语言吗?仔细一想也不对啊,创建的时候明明有Kotlin可以选择就说明了是支持Kotlin语言的,后来是一不小心就解决了这个问题的:当我把MainController转换为Kotlin语言后就好了,具体原因不详,Kotlin和Java是可以同时存在的,只能说这是Spring Boot的坑。MainController转换为Kotlin语言如下:

    • @RestController
    • @RequestMapping("/demo")
    • class MainController {
    • @GetMapping("hello")
    • fun hello(): String {
    • return "Hello Spring Boot!"
    • }
    • }

    此时我们重新运行项目,并再次访问:localhost:8080/demo/hello,如下:
    在这里插入图片描述

  3. Spring Boot的另外一个坑,其实和上面也是同一个坑来的,但是

    数据库访问我是完全参考的官方文档:https://spring.io/guides/gs/accessing-data-mysql/

    在官方教程中,还有一个User和UserRepository,我也是直接复制过来的,是Java代码,后来把MainController转换为了Kotlin,而User和UserRepository并没有转换为Java,运行时报如下错误:

    org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘mainController’

    一开始我还不知道Spring Boot和Kotlin有兼容问题的时候,我就在百度或Google上搜索这个错误信息,文章是有许多的,但是没有一个能解决我的问题的。

    完整异常信息如下:

    • Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
    • 2021-09-18 10:04:20.292 ERROR 8820 --- [ main] o.s.boot.SpringApplication : Application run failed
    • org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'mainController' defined in file [C:\Users\Even\Downloads\gs-accessing-data-mysql-main\HelloB\build\classes\kotlin\main\com\example\hellob\MainController.class]: Post-processing of merged bean definition failed; nested exception is java.lang.IllegalStateException: Failed to introspect Class [com.example.hellob.MainController] from ClassLoader [jdk.internal.loader.ClassLoaders$AppClassLoader@2437c6dc]
    • at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:579) ~[spring-beans-5.3.9.jar:5.3.9]
    • at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:524) ~[spring-beans-5.3.9.jar:5.3.9]
    • at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335) ~[spring-beans-5.3.9.jar:5.3.9]
    • at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.3.9.jar:5.3.9]
    • at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333) ~[spring-beans-5.3.9.jar:5.3.9]
    • at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208) ~[spring-beans-5.3.9.jar:5.3.9]
    • at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:944) ~[spring-beans-5.3.9.jar:5.3.9]
    • at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:918) ~[spring-context-5.3.9.jar:5.3.9]
    • at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583) ~[spring-context-5.3.9.jar:5.3.9]
    • at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:145) ~[spring-boot-2.5.4.jar:2.5.4]
    • at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754) ~[spring-boot-2.5.4.jar:2.5.4]
    • at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:434) ~[spring-boot-2.5.4.jar:2.5.4]
    • at org.springframework.boot.SpringApplication.run(SpringApplication.java:338) ~[spring-boot-2.5.4.jar:2.5.4]
    • at org.springframework.boot.SpringApplication.run(SpringApplication.java:1343) ~[spring-boot-2.5.4.jar:2.5.4]
    • at org.springframework.boot.SpringApplication.run(SpringApplication.java:1332) ~[spring-boot-2.5.4.jar:2.5.4]
    • at com.example.hellob.HelloBApplicationKt.main(HelloBApplication.kt:13) ~[main/:na]
    • Caused by: java.lang.IllegalStateException: Failed to introspect Class [com.example.hellob.MainController] from ClassLoader [jdk.internal.loader.ClassLoaders$AppClassLoader@2437c6dc]
    • at org.springframework.util.ReflectionUtils.getDeclaredFields(ReflectionUtils.java:739) ~[spring-core-5.3.9.jar:5.3.9]
    • at org.springframework.util.ReflectionUtils.doWithLocalFields(ReflectionUtils.java:671) ~[spring-core-5.3.9.jar:5.3.9]
    • at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor.buildPersistenceMetadata(PersistenceAnnotationBeanPostProcessor.java:407) ~[spring-orm-5.3.9.jar:5.3.9]
    • at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor.findPersistenceMetadata(PersistenceAnnotationBeanPostProcessor.java:388) ~[spring-orm-5.3.9.jar:5.3.9]
    • at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor.postProcessMergedBeanDefinition(PersistenceAnnotationBeanPostProcessor.java:335) ~[spring-orm-5.3.9.jar:5.3.9]
    • at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyMergedBeanDefinitionPostProcessors(AbstractAutowireCapableBeanFactory.java:1098) ~[spring-beans-5.3.9.jar:5.3.9]
    • at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:576) ~[spring-beans-5.3.9.jar:5.3.9]
    • ... 15 common frames omitted
    • Caused by: java.lang.NoClassDefFoundError: Lcom/example/hellob/UserRepository;
    • at java.base/java.lang.Class.getDeclaredFields0(Native Method) ~[na:na]
    • at java.base/java.lang.Class.privateGetDeclaredFields(Class.java:3061) ~[na:na]
    • at java.base/java.lang.Class.getDeclaredFields(Class.java:2248) ~[na:na]
    • at org.springframework.util.ReflectionUtils.getDeclaredFields(ReflectionUtils.java:734) ~[spring-core-5.3.9.jar:5.3.9]
    • ... 21 common frames omitted
    • Caused by: java.lang.ClassNotFoundException: com.example.hellob.UserRepository
    • at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:581) ~[na:na]
    • at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178) ~[na:na]
    • at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521) ~[na:na]
    • ... 25 common frames omitted

    后来也是无意中把所有类都转换为Kotlin时发现就正常了,所以,总结就是,在Spring Boot项目中,不要混用Kotlin和Java,不然会出现你意想不到的问题。怪不得我们的后台还没开始使用Kotlin语言来开发,或许这也是原因之一。而在Android上Kotlin和Java就能很好的整合在一起。

完整Spring Boot MySQL项目创建流程

  1. 创建Spring Boot项目,在选择依赖项的时候添加:Spring Web、Spring Data JPA、MySQL Driver
  2. 在MySQL中创建好数据库(注:只需要创建好数据库,表可以不建,因为在Spring Boot中可以用代码来创建表)
  3. 在application.properties中添加数据库连接配置:
    • spring.jpa.hibernate.ddl-auto=update
    • spring.datasource.url=jdbc:mysql://${MYSQL_HOST:localhost}:3306/db_example
    • spring.datasource.username=springuser
    • spring.datasource.password=ThePassword
    • spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
    • spring.jpa.open-in-view=true
    • #spring.jpa.show-sql: true
  • 创建对应数据库表的JavaBean:User
    • import javax.persistence.Entity
    • import javax.persistence.GeneratedValue
    • import javax.persistence.GenerationType
    • import javax.persistence.Id
    • @Entity
    • class User {
    • @Id
    • @GeneratedValue(strategy = GenerationType.AUTO)
    • var id: Int? = null
    • var name: String? = null
    • var email: String? = null
    • }
  1. 创建用于操作User表的接口
    • import org.springframework.data.repository.CrudRepository
    • interface UserRepository : CrudRepository<User?, Int?>
  2. 创建Controller并使用UserRepository来操作User表的增删改查
    • import org.springframework.web.bind.annotation.RestController
    • import org.springframework.web.bind.annotation.RequestMapping
    • import org.springframework.web.bind.annotation.GetMapping
    • @RestController
    • @RequestMapping("/demo")
    • class MainController(private val userRepository: UserRepository) {
    • @GetMapping("/add")
    • fun addNewUser(name: String?, email: String?): String {
    • val n = User()
    • n.name = name
    • n.email = email
    • userRepository.save(n)
    • return "Saved"
    • }
    • @get:GetMapping("/all")
    • val allUsers: Iterable<User?>
    • get() = userRepository.findAll()
    • }

    注:这里的userRepository在官方教程中是使用注解的方式赋值的,如下:

    • @Autowired
    • private val userRepository: UserRepository? = null

    我为什么知道可以使用构造函数的方式呢?在Kotlin代码中是无法知道的,但是在Java代码中,IDE会有个警告,提示使用构造函数的方式,或者说推荐使用构造函数的方式,所以我就使用构造函数了,这在Kotlin中也是有好处的,使用构造函数可以声明为非空属性,否则只能声明为可空属性,因为如果使用注解来赋值的话,在声明的时候语法上又必须要求赋值,那就只能声明为可空属性了。

  3. 运行项目。然后在浏览器调用add接口来向数据库添加一个用户,如下:
    在这里插入图片描述
    再调用all接口查询出所有的用户,如下:
    在这里插入图片描述
    可以看到,Spring Boot访问数据库还是非常方便的,UserRepository的实例框架会给我们注入,而且这个接口已经包含了一些常用的增删改查的方法,如果需要更详细的增删改查操作,可以查看官方教程:
方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门