Spring Boot实现第一次启动时自动初始化数据库

这篇具有很好参考价值的文章主要介绍了Spring Boot实现第一次启动时自动初始化数据库。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Spring Boot实现第一次启动时自动初始化数据库

在现在的后端开发中,只要是运用联系型数据库,信任SSM架构(Spring Boot + MyBatis)已经成为首选。 不过在咱们第一次运转或许布置项目的时分,一般要先手动衔接数据库,履行一个SQL文件以创立数据库以及数据库表格完结数据库的初始化作业,这样咱们的SSM应用程序才能够正常作业。 这样也对实际布置或许是容器化造成了一些麻烦,必须先手动初始化数据库再发动应用程序。 那能不能让咱们的SSM应用程序第一次发动时,主动地帮咱们履行SQL文件以完结数据库初始化作业呢? 这样事实上是没问题的,今日就以Spring Boot + MyBatis为例,运用MySQL作为数据库,完结上述的数据库初始化功用。

1,全体思路

咱们能够编写一个装备类,在一个标示了@PostConstruct注解的办法中编写初始化数据库的逻辑,这样应用程序发动时,就会履行该办法协助咱们完结数据库的初始化作业。 那么这个初始化数据库的逻辑大概是什么呢?能够总结为如下过程:

  1. 首要测验衔接用户装备的地址,若衔接抛出反常阐明地址中指定的数据库不存在,需求创立数据库并初始化数据,不然就不需求初始化,直接退出初始化逻辑
  2. 若要履行初始化,首要从头拼装用户装备的衔接地址,使得本次衔接不再是衔接至详细的数据库,并履行create database句子完结数据库创立
  3. 创立完结数据库后,再次运用用户装备的衔接地址,这时数据库创立完结就能够成功衔接上了!这时再履行SQL文件初始化表格即可

上述逻辑中咱们能够会有下列的疑问:

  • 第一步中,为什么衔接抛出反常阐明地址中指定的数据库不存在
  • 第二步中,什么是 “使得本次衔接不再是衔接至详细的数据库”

假定用户装备的衔接地址是jdbc:mysql://127.0.0.1:3306/init_demo,信任这个咱们十分熟悉了,它表明:衔接的MySQL地址是127.0.0.1,端口是3306,并且衔接到该MySQL中名为init_demo的数据库中。 那么假如MySQL中init_demo的库并不存在,Spring Boot还测验衔接上述地址的话,就会抛出SQLException反常:

Spring Boot实现第一次启动时自动初始化数据库,spring boot,数据库,后端

所以在这儿能够将是否抛出SQLException反常作为判别应用程序是否是第一次布置发动的条件。 好的,已然数据库不存在,咱们就要创立数据库,但是上述地址衔接不上啊!怎样创立呢? 正是由于上述地址中指定了要衔接的详细数据库,而数据库又不存在,才会衔接失利,那能不能衔接时不指定数据库,仅仅是衔接到MySQL上就行呢?当然能够,咱们将上述的衔接地址改成:jdbc:mysql://127.0.0.1:3306/,就能够衔接成功了! 不过一般SSM应用程序中,装备数据库地址都是要指定库名的,因而咱们待会在装备类编写初始化数据库逻辑时,从头拼装一下用户给的装备衔接地址即可,即把jdbc:mysql://127.0.0.1:3306/init_demo经过代码处理成jdbc:mysql://127.0.0.1:3306/并主张衔接即可,这便是上述说的第二步。 第二步完结了数据库的创立,第三步便是完结表格创立了!表格创立就写在SQL文件里即可,由于数据库创立好了,咱们在第三步中又能够从头运用用户给的装备地址jdbc:mysql://127.0.0.1:3306/init_demo再次衔接并履行SQL文件完结初始化了! 上述过程中,咱们将运用JDBC自带的接口完结数据库衔接等等,而不是运用MyBatis的SqlSessionFactory,由于咱们第二步需求改动衔接地址。 下面,咱们就来完结一下。

2,详细完结

首要是在本地或许其它地方树立好MySQL服务器,这儿就不再赘述怎样去树立MySQL了。 我这儿在本地树立了MySQL服务器,下面经过Spring Boot进行衔接。

(1) 创立应用程序并装备

首要创立一个Spring Boot应用程序,并集成好MySQL驱动和MyBatis支持,我这儿的依靠如下:

<!-- Spring Web -->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- MyBatis -->
<dependency>
	<groupId>org.mybatis.spring.boot</groupId>
	<artifactId>mybatis-spring-boot-starter</artifactId>
	<version>3.0.2</version>
</dependency>
<!-- MySQL衔接支持 -->
<dependency>
	<groupId>com.mysql</groupId>
	<artifactId>mysql-connector-j</artifactId>
	<scope>runtime</scope>
</dependency>
<!-- Hutool有用工具 -->
<dependency>
	<groupId>cn.hutool</groupId>
	<artifactId>hutool-all</artifactId>
	<version>5.8.16</version>
</dependency>
<!-- Lombok注解 -->
<dependency>
	<groupId>org.projectlombok</groupId>
	<artifactId>lombok</artifactId>
	<scope>provided</scope>
</dependency>
<!-- Spring Boot测试 -->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-test</artifactId>
	<scope>test</scope>
</dependency>

然后在装备文件application.yml中加入下列装备:

# 数据库装备
spring:
  datasource:
    url: "jdbc:mysql://127.0.0.1:3306/init_demo?serverTimezone=GMT%2B8"
    username: "swsk33"
    password: "dev-2333"

这便是正常的数据库衔接装备,不再过多讲述。我这儿运用yaml格局装备文件,咱们也能够运用properties格局的装备文件。

(2) 编写装备类完结数据库的检测和初始化逻辑

这儿先给出这个装备类的代码:

package com.gitee.swsk33.sqlinitdemo.config;
import cn.hutool.core.io.resource.ClassPathResource;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.jdbc.ScriptRunner;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
/**
 * 用于第一次发动时,初始化数据库的装备类
 */
@Slf4j
@Configuration
public class DatabaseInitialize {
	/**
	 * 读取衔接地址
	 */
	@Value("${spring.datasource.url}")
	private String url;
	/**
	 * 读取用户名
	 */
	@Value("${spring.datasource.username}")
	private String username;
	/**
	 * 读取暗码
	 */
	@Value("${spring.datasource.password}")
	private String password;
	/**
	 * 检测当前衔接的库是否存在(衔接URL中的数据库)
	 *
	 * @return 当前衔接的库是否存在
	 */
	private boolean currentDatabaseExists() {
		// 测验以装备文件中的URL树立衔接
		try {
			Connection connection = DriverManager.getConnection(url, username, password);
			connection.close();
		} catch (SQLException e) {
			// 若衔接抛出反常则阐明衔接URL中指定数据库不存在
			return false;
		}
		// 正常情况下阐明衔接URL中数据库存在
		return true;
	}
	/**
	 * 履行SQL脚本
	 *
	 * @param path        SQL脚本文件的路径
	 * @param isClasspath SQL脚本路径是否是classpath路径
	 * @param connection  数据库衔接目标,经过这个衔接履行脚本
	 */
	private void runSQLScript(String path, boolean isClasspath, Connection connection) {
		try (InputStream sqlFileStream = isClasspath ? new ClassPathResource(path).getStream() : new FileInputStream(path)) {
			BufferedReader sqlFileStreamReader = new BufferedReader(new InputStreamReader(sqlFileStream, StandardCharsets.UTF_8));
			// 创立SQL脚本履行器目标
			ScriptRunner scriptRunner = new ScriptRunner(connection);
			// 运用SQL脚本履行器目标履行脚本
			scriptRunner.runScript(sqlFileStreamReader);
			// 最后关闭文件读取器
			sqlFileStreamReader.close();
		} catch (Exception e) {
			log.error("读取文件或许履行脚本失利!");
			e.printStackTrace();
		}
	}
	/**
	 * 履行SQL脚本以创立数据库
	 */
	private void createDatabase() {
		try {
			// 修正衔接句子,从头树立衔接
			// 从头树立的衔接不再衔接到指定库,而是直接衔接到整个MySQL
			// 运用URI类解析并拆解衔接地址,从头拼装
			URI databaseURI = new URI(url.replace("jdbc:", ""));
			// 得到衔接地址中的数据库平台名(例如mysql)
			String databasePlatform = databaseURI.getScheme();
			// 得到衔接地址和端口
			String hostAndPort = databaseURI.getAuthority();
			// 得到衔接地址中的库名
			String databaseName = databaseURI.getPath().substring(1);
			// 拼装新的衔接URL,不衔接至指定库
			String newURL = "jdbc:" + databasePlatform + "://" + hostAndPort + "/";
			// 从头树立衔接
			Connection connection = DriverManager.getConnection(newURL, username, password);
			Statement statement = connection.createStatement();
			// 履行SQL句子创立数据库
			statement.execute("create database if not exists `" + databaseName + "`");
			// 关闭会话和衔接
			statement.close();
			connection.close();
			log.info("创立数据库完结!");
		} catch (URISyntaxException e) {
			log.error("数据库衔接URL格局过错!");
			throw new RuntimeException(e);
		} catch (SQLException e) {
			log.error("衔接失利!");
			throw new RuntimeException(e);
		}
	}
	/**
	 * 该办法用于检测数据库是否需求初始化,假如是则履行SQL脚本进行初始化操作
	 */
	@PostConstruct
	private void initDatabase() {
		log.info("开端查看数据库是否需求初始化...");
		// 检测当前衔接数据库是否存在
		if (currentDatabaseExists()) {
			log.info("数据库存在,不需求初始化!");
			return;
		}
		log.warn("数据库不存在!预备履行初始化过程...");
		// 先创立数据库
		createDatabase();
		// 然后再次衔接,履行脚本初始化库中的表格
		try (Connection connection = DriverManager.getConnection(url, username, password)) {
			runSQLScript("/create-table.sql", true, connection);
			log.info("初始化表格完结!");
		} catch (Exception e) {
			log.error("初始化表格时,衔接数据库失利!");
			e.printStackTrace();
		}
	}
}

上述代码中,有下列要点:

  • 咱们运用@Value注解读取了装备文件中数据库的衔接信息,包括衔接地址、用户名和暗码
  • 上述currentDatabaseExists办法用于测验运用装备的地址进行衔接,假如抛出SQLException反常则判别装备的地址中,指定的数据库是不存在的,这儿的代码主要是完结了上述初始化逻辑中的第一步
  • 上述createDatabase办法用于从头拼装用户的衔接地址,使其不再是衔接到指定数据库,然后履行SQL句子完结数据库的创立,咱们运用Java的URI类解析用户装备的衔接地址,便于咱们拆分然后拼装衔接地址,并获取用户要运用的数据库名,对其进行创立,这儿的代码完结了上述初始化逻辑中的第二步
  • 上述initDatabase办法是会被主动履行的,它调用了currentDatabaseExistscreateDatabase办法,组合起来一切的过程,在其间完结了第一步和第二步后,从头运用用户装备的地址主张衔接并履行SQL脚本以初始化表,这个办法包括了上述初始化逻辑中的第三步
  • 上述runSQLScript办法用于衔接数据库后履行SQL脚本,其间ScriptRunner类是由MyBatis供给的运转SQL脚本的有用类,其结构函数需求传入JDBC的数据库衔接目标Connection目标,然后上述我还设定了形参isClasspath,能够让用户自定义是读取文件体系中的SQL脚本还是classpath中的SQL脚本

上述的初始化表格脚本坐落工程目录的src/main/resources/create-table.sql,即classpath中,内容如下:

-- 初始化表格前先删去
drop table if exists `user`;
-- 创立表格
create table `user`
(
	`id`       int unsigned auto_increment,
	`username` varchar(16) not null,
	`password` varchar(32) not null,
	primary key (`id`)
) engine = InnoDB
  default charset = utf8mb4;

好的,现在先保证MySQL数据库中不存在init_demo的库,发动程序试试:

Spring Boot实现第一次启动时自动初始化数据库,spring boot,数据库,后端

可见成功地完结了数据库的检测、初始化作业,也可见ScriptRunner在履行SQL的时分会在操控台输出履行的句子。 现在再从头发动一下程序试试:

Spring Boot实现第一次启动时自动初始化数据库,spring boot,数据库,后端

可见第2次发动时,名为init_demo的数据库已经存在了,这时就不需求履行初始化逻辑了!

(3) 假如有的Bean初始化时需求拜访数据库

假定现在有一个类,在初始化为Bean的时分需求拜访数据库,例如:

// 省略package和import
/**
 * 发动时需求查询数据库的Beans
 */
@Slf4j
@Component
public class UserServiceDemo {
	@Autowired
	private UserDAO userDAO;
	@PostConstruct
	private void init() {
		log.info("履行数据库测试拜访...");
		userDAO.add(new User(0, "用户名", "暗码"));
		List<User> users = userDAO.getAll();
		for (User user : users) {
			System.out.println(user);
		}
	}
}

这个类在被初始化为Bean的时分,就需求拜访数据库进行读写操作,那问题来了,假如这个类UserServiceDemo在上述数据库初始化类DatabaseInitialize之前被初始化了怎样办呢?这会导致数据库还没有被初始化时,UserServiceDemo就去拜访数据库,导致初始化失利。 这时,咱们能够运用@DependsOn注解,这个注解能够操控UserServiceDemoDatabaseInitialize初始化之后再进行初始化:

@Slf4j
@Component
// 运用@DependsOn注解表明当前类依靠于名为databaseInitialize的Bean
// 这样能够使得databaseInitialize这个Bean(咱们的数据库查看类)先被初始化,并履行完结数据库初始化后再初始化本类,以顺利拜访数据库
@DependsOn("databaseInitialize")
public class UserServiceDemo {
	// 省略这个类的内容
}

在这儿咱们在UserServiceDemo上标示了注解@DependsOn,并传入databaseInitialize作为参数,表明UserServiceDemo这个类是依靠于名(id)为databaseInitialize的Bean的,这样Spring Boot就会在DatabaseInitialize初始化之后再初始化UserServiceDemo

标示了@Component等等的类,默认情况下被初始化为Bean的时分,其名称是其类名的小驼峰方式,例如上述的DatabaseInitialize类,初始化为Bean时姓名默认为databaseInitialize,因而上述@DependsOn注解就传入databaseInitialize

现在删去init_demo库,再次发动应用程序:
Spring Boot实现第一次启动时自动初始化数据库,spring boot,数据库,后端

可见在初始化数据库后,又成功地在发动时拜访了数据库。

3,总结

本文以Spring Boot + Mybatis为例,运用MySQL数据库,完结了SSM应用程序第一次发动时主动检测并完结数据库初始化的功用,理论上上述方式适用于一切的联系型数据库,咱们稍作修正即可。文章来源地址https://www.toymoban.com/news/detail-772820.html

到了这里,关于Spring Boot实现第一次启动时自动初始化数据库的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【2023新教程】树莓派4B开机启动-树莓派第一次启动-树莓派不使用显示器启动-树莓派从购买到启动一步一步完全版!

    闲来无事,在咸鱼上买了一个树莓派4B。买来配件都十分齐全,于是就想着启动来测试一下。下面是树莓派无显示器第一次启动的全过程,包含安装系统。 网上的教程大多需要额外使用显示器、鼠标、键盘之类的外设。然而,树莓派本身就是便捷灵活开发的代表,在真实开发

    2024年02月13日
    浏览(61)
  • Spring Boot实现在启动时执行一次的功能

    此方法可能是最常用的 可以使用Spring Boot的@PostConstruct注解来实现在启动时执行一次的功能。@PostConstruct注解标记的方法会在Bean初始化完成后自动调用,可以在该方法中执行只需要在启动时执行一次的操作。 如果想在生成对象时完成某些初始化操作,而偏偏这些初始化操作又

    2024年02月06日
    浏览(40)
  • 第一次使用ThreadPoolTaskExecutor实现线程池的经历,反复修改了多次代码才正常使用

    1、前言   在一个向第三方平台推送消息的场景中,为了提高程序的执行效率,每次发送消息,都创建一个新的线程来完成发送消息的任务,为了提供线程的使用性能,我选择了ThreadPoolTaskExecutor线程池,结果在使用的过程中,出现了较多的问题,这里记录一下避免以后再出

    2024年02月08日
    浏览(58)
  • 当我第一次通过Kotlin和Compose来实现一个Canvas时, 我收获了什么?

    自从2019年Google推荐Kotlin为Android开发的首选语言以来已经经历了将近四年的时间, Compose的1.0版本也发布了将近2年的时间, Kotlin+Compose在现阶段的Android开发过程中还远远达不到主流的程度. 我们是否应该开始尝试这个组合? 这个组合有会给我们带来什么? 对于我来说, 我是个守旧又

    2023年04月27日
    浏览(39)
  • 第一次作业

    作业内容:1,atd和crond的区别                   2,指定在2023/08/26 09:00将时间写入testmail.txt文件中                   3,指定在每天凌晨4:00将该时间点之前的系统日志信息备份到个目录下(/var/log/messages ),备份后日志文件名显示格式logfileYY-MM-DD HH-MM 1、运行方式不同

    2023年04月20日
    浏览(45)
  • python 第一次作业

    因为笔者有一些 c/c++ 语言的基础,所以应该学 python 会稍微简单一些 输入的时候所有的输入都是字符串类型,我们需要进行类型转换 参见资源里面的第三题和第四题,为了方便起见,直接把代码贴在下面

    2024年03月25日
    浏览(54)
  • shell第一次作业

    1、判断当前磁盘剩余空间是否有20G,如果小于20G,则将报警邮件发送给管理员,每天检查次磁盘剩余空间。 2、判断web服务是否运行    1、查看进程的方式判断该程序是否运行,    2、通过查看端口的方式判断该程序是否运行,如果没有运行,则启动该服务并配置防火墙规

    2024年02月09日
    浏览(45)
  • 第一次PR经历

         

    2024年02月13日
    浏览(45)
  • 第一次博客作业

    这学期才开始接触Java,之前只学了C语言,所以一开始写题目的代码的时候对Java的众多函数和语法不是太熟悉,一开始就上手写代码有点不适应。 ​  关于类: 1、类似C中的struct,构造函数、内置方法(函数 )都比较相似 2、尽量避免代码的重复,把private和public的方法搞清晰。

    2024年02月08日
    浏览(61)
  • jQuery第一次接触

    jQuery是一个轻量级js库 1.下载jquery库,网址Download jQuery | jQuery npm i jquery 2.还可以从cdn中载入jquery script src=\\\"https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js\\\" 3.j代表js,query代表查询,jQuery可以进行查询的js语言,主要用来查询html元素 4.基础语法$(selector).action(),其中selector代表要进行操

    2024年02月12日
    浏览(46)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包