Spring boot 随笔 1 DatasourceInitializer

0. 为啥感觉升级了 win11 之后,电脑像是刚买回来的,很快

这篇加餐完全是一个意外:时隔两年半,再看 Springboot-quartz-starter 集成实现的时候,不知道为啥我的h2 在应用启动的时候,不能自动创建quartz相关的schema。后面看了 springboot 的文档,据说是可以做到的,AI也是这么说的。

没办法,只能看 QuartzAutoConfiguration 源码了。于是乎,就有了这么个好活

没办法,就当是一个支线任务了

1. AbstractScriptDatabaseInitializer

请添加图片描述

下面是熟悉的,阉割后的 源码

package org.springframework.boot.sql.init;

/**
 * Base class for an {@link InitializingBean} that performs SQL database initialization
 * using schema (DDL) and data (DML) scripts.
 *
 * @author Andy Wilkinson
 * @since 2.5.0
 */
public abstract class AbstractScriptDatabaseInitializer implements ResourceLoaderAware, InitializingBean {

	// 构造入参配置
	private final DatabaseInitializationSettings settings;
	private volatile ResourceLoader resourceLoader;

	@Override
	public void afterPropertiesSet() throws Exception {
		// 初始化后,就执行逻辑了
		initializeDatabase();
	}

	/**
	 * Initializes the database by applying schema and data scripts.
	 * @return {@code true} if one or more scripts were applied to the database, otherwise
	 * {@code false}
	 */
	public boolean initializeDatabase() {
		ScriptLocationResolver locationResolver = new ScriptLocationResolver(this.resourceLoader);
		// 先后执行 schema, data 的脚本
		boolean initialized = applySchemaScripts(locationResolver);
		return applyDataScripts(locationResolver) || initialized;
	}

	// 真正执行脚本前,会走这个判断,决定是否要执行脚本
	private boolean isEnabled() {
		if (this.settings.getMode() == DatabaseInitializationMode.NEVER) {
			return false;
		}
		return this.settings.getMode() == DatabaseInitializationMode.ALWAYS || isEmbeddedDatabase();
	}

	/**
	 * Returns whether the database that is to be initialized is embedded.
	 * @return {@code true} if the database is embedded, otherwise {@code false}
	 * @since 2.5.1
	 */
	protected boolean isEmbeddedDatabase() {
		throw new IllegalStateException(
				"Database initialization mode is '" + this.settings.getMode() + "' and database type is unknown");
	}

	private boolean applySchemaScripts(ScriptLocationResolver locationResolver) {
		return applyScripts(this.settings.getSchemaLocations(), "schema", locationResolver);
	}

	private boolean applyDataScripts(ScriptLocationResolver locationResolver) {
		return applyScripts(this.settings.getDataLocations(), "data", locationResolver);
	}

	private boolean applyScripts(List<String> locations, String type, ScriptLocationResolver locationResolver) {
		List<Resource> scripts = getScripts(locations, type, locationResolver);
		if (!scripts.isEmpty() && isEnabled()) {
			runScripts(scripts);
			return true;
		}
		return false;
	}

	// 根据配置的 路径的字符串 -> spring.Resource 类型
	private List<Resource> getScripts(List<String> locations, String type, ScriptLocationResolver locationResolver) {
		if (CollectionUtils.isEmpty(locations)) {
			return Collections.emptyList();
		}
		List<Resource> resources = new ArrayList<>();
		for (String location : locations) {
			for (Resource resource : doGetResources(location, locationResolver)) {
				if (resource.exists()) {
					resources.add(resource);
				}
			}
		}
		return resources;
	}

	private List<Resource> doGetResources(String location, ScriptLocationResolver locationResolver) {
		return locationResolver.resolve(location);
	}

	private void runScripts(List<Resource> resources) {
		runScripts(resources, this.settings.isContinueOnError(), this.settings.getSeparator(),
				this.settings.getEncoding());
	}

	protected abstract void runScripts(List<Resource> resources, boolean continueOnError, String separator,
			Charset encoding);

	private static class ScriptLocationResolver {
		private final ResourcePatternResolver resourcePatternResolver;
		private List<Resource> resolve(String location) throws IOException {
			// ...
		}
	}

}

再看几个它的实现类,加载上配置类,基本上,可以知道它的使用方法了

2. 吾のDemo

始于测试类

package org.pajamas.spring.boot;

import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.pajamas.example.starter.core.entity.AlbumEntity;
import org.pajamas.example.starter.core.repo.AlbumRepo;
import org.pajamas.example.test.AbstractApplicationTest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.TestPropertySource;

import java.util.List;

/**
 * @author william
 * @since 2024/5/30
 */
@DisplayName("what the interesting component")
@TestPropertySource(properties = {
        "spring.application.name=service-example-test",
        // 屏蔽 liquibase 的干扰 
        "spring.liquibase.enabled=false"
})
@Import(ExampleDatabaseInitializer.class)
public class DatabaseInitializerTest extends AbstractApplicationTest {
	// 其实就,一个 jpa 实体类的 repository
    @Autowired
    AlbumRepo repo;

    // @Disabled
    @DisplayName("execute DDL, DML automatically, as App startup")
    @Test
    public void t0() throws Exception {
    	// 预期的结果:启动启动时,自动创建表,并插入一条记录
        List<AlbumEntity> all = this.repo.findAll();
        printErr(all);
    }
}

既然是测试,就走简单的方式,注册这个bean

package org.pajamas.spring.boot;

import org.springframework.boot.autoconfigure.sql.init.SqlDataSourceScriptDatabaseInitializer;
import org.springframework.boot.autoconfigure.sql.init.SqlInitializationProperties;
import org.springframework.boot.sql.init.DatabaseInitializationMode;

import java.util.Collections;

import javax.sql.DataSource;

/**
 * @author william
 * @since 2024/5/30
 */
public class ExampleDatabaseInitializer extends SqlDataSourceScriptDatabaseInitializer {
    public ExampleDatabaseInitializer(DataSource dataSource) {
        super(dataSource, getProperty());
    }

    private static SqlInitializationProperties getProperty() {
        SqlInitializationProperties properties = new SqlInitializationProperties();
        properties.setSchemaLocations(Collections.singletonList("classpath:sql/schema.sql"));
        properties.setDataLocations(Collections.singletonList("classpath:sql/data.sql"));
        properties.setMode(DatabaseInitializationMode.ALWAYS);
        properties.setContinueOnError(false);
        return properties;
    }
}

schema.sql

CREATE TABLE IF NOT EXISTS `t_album`
(
    `id`             bigint NOT NULL AUTO_INCREMENT,
    `album_name`     varchar(32)                                                  DEFAULT NULL COMMENT 'album name',
    `album_year`     int                                                          DEFAULT NULL COMMENT 'album publish year',
    `create_date`    timestamp NULL DEFAULT NULL,
    `create_user_id` bigint                                                       DEFAULT NULL,
    `update_date`    timestamp NULL DEFAULT NULL,
    `update_user_id` bigint                                                       DEFAULT NULL,
    `ver`            int    NOT NULL                                              DEFAULT '0',
    `del`            bigint NOT NULL                                              DEFAULT '0',
    PRIMARY KEY (`id`),
    UNIQUE KEY `uni_album_id_del` (`id`,`del`)
) COMMENT='album table';

CREATE TABLE IF NOT EXISTS `t_artist`
(
    `id`             bigint NOT NULL AUTO_INCREMENT,
    `artist_name`    varchar(32)                                                  DEFAULT NULL COMMENT 'artist name',
    `artist_from`    varchar(32)                                                  DEFAULT NULL COMMENT 'shorten of country name',
    `create_date`    timestamp NULL DEFAULT NULL,
    `create_user_id` bigint                                                       DEFAULT NULL,
    `update_date`    timestamp NULL DEFAULT NULL,
    `update_user_id` bigint                                                       DEFAULT NULL,
    `ver`            int    NOT NULL                                              DEFAULT '0',
    `del`            bigint NOT NULL                                              DEFAULT '0',
    PRIMARY KEY (`id`),
    UNIQUE KEY `uni_artist_id_del` (`id`,`del`)
) COMMENT='artist table';

data.sql

insert into
    `t_album`
(
    `album_name`,
    `album_year`,
    `create_user_id`,
    `update_user_id`
)
values
(
    'Boomerang',
    2023,
    1023,
    1023
);

3. 话说回来:为甚么,我的h2没有自动创建quartz的schema

这是springboot.Quartz的实现
在这里插入图片描述

接下来,源码启动…

package org.springframework.boot.jdbc.init;

/**
 * {@link InitializingBean} that performs {@link DataSource} initialization using schema
 * (DDL) and data (DML) scripts.
 *
 * @author Andy Wilkinson
 * @since 2.5.0
 */
public class DataSourceScriptDatabaseInitializer extends AbstractScriptDatabaseInitializer {
	@Override
	protected boolean isEmbeddedDatabase() {
		try {
			// step into ..
			return EmbeddedDatabaseConnection.isEmbedded(this.dataSource);
		}
		catch (Exception ex) {
			logger.debug("Could not determine if datasource is embedded", ex);
			return false;
		}
	}
}

----------

	// org.springframework.boot.jdbc.EmbeddedDatabaseConnection
	/**
	 * Convenience method to determine if a given data source represents an embedded
	 * database type.
	 * @param dataSource the data source to interrogate
	 * @return true if the data source is one of the embedded types
	 */
	public static boolean isEmbedded(DataSource dataSource) {
		try {
		
			return new JdbcTemplate(dataSource)
				// step into ...
				.execute(new IsEmbedded());
		}
		catch (DataAccessException ex) {
			// Could not connect, which means it's not embedded
			return false;
		}
	}

----------

	// org.springframework.boot.jdbc.EmbeddedDatabaseConnection.IsEmbedded
	@Override
	public Boolean doInConnection(Connection connection) throws SQLException, DataAccessException {
		DatabaseMetaData metaData = connection.getMetaData();
		String productName = metaData.getDatabaseProductName();
		if (productName == null) {
			return false;
		}
		productName = productName.toUpperCase(Locale.ENGLISH);
		// step into ...
		EmbeddedDatabaseConnection[] candidates = EmbeddedDatabaseConnection.values();
		for (EmbeddedDatabaseConnection candidate : candidates) {
			if (candidate != NONE && productName.contains(candidate.getType().name())) {
				// 根据jdbc.url判断是不是一个 嵌入式数据库
				String url = metaData.getURL();
				return (url == null || candidate.isEmbeddedUrl(url));
			}
		}
		return false;
	}

------------

public enum EmbeddedDatabaseConnection {

	// H2 判断是否为嵌入式数据的依据
	/**
	 * H2 Database Connection.
	 */
	H2(EmbeddedDatabaseType.H2, DatabaseDriver.H2.getDriverClassName(),
			"jdbc:h2:mem:%s;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE", (url) -> url.contains(":h2:mem")),

}

破案:我的h2 使用默认的 file(xxx.mv.db) 存储,默认配置下(DatabaseInitializationMode.EMBEDDED), 只有内存(嵌入式)的数据库会开启这个特性。

  • 要么配置 DatabaseInitializationMode.ALWAYS
  • 要么使用内存数据库

Anyway, h2支持好多种连接方式,新版本h2, 默认的file模式,采用mv的storeEngine 支持MVCC。所以说,对于quartz这种依赖行锁的要求,也是支持的。

4. 话又说回去… 这个东西对项目的意义是什么

  • 可以试下这个:如果你有一个连接数据库的测试环境,或者你的程序很简单,又或者 有特殊的xp(内存数据库)
  • 专门数据库的版本控制工具:你的程序比较复杂,或者 本身就需要数据库的版本控制工具(如 Liquibase),运行在严肃的生产环境

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/676272.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

FL Studio怎么给钢琴加延音 FL Studio怎么用钢琴做伴奏

在使用钢琴音色进行音乐创作的时候&#xff0c;可以对钢琴进行延音处理&#xff0c;这样处理的音色给人的感觉会更加的饱满丰富&#xff0c;同时&#xff0c;给钢琴加了延音之后&#xff0c;钢琴的声音时值也会相应的变长&#xff0c;听起来更加的柔和。今天就和大家讲一讲&…

STM32使用HAL库UART接收不定长数据-1

使用STM32的HAL库实现UART串口不定长数据的接收 使用STM32的UART接收数据的时候&#xff0c;经常会遇到接收长度不固定的数据&#xff0c;比如一帧数据可能是10个字节&#xff0c;也可能是12个字节。这种数据称为不定长数据。 现有的很多通信协议是不定长的&#xff0c;比如mo…

vue3_组件间通信方式

目录 一、父子通信 1.父传子&#xff08; defineProps&#xff09; 2.父传子&#xff08;useAttrs&#xff09; 3.子传父&#xff08;ref&#xff0c;defineExpose &#xff09; 4.子传父&#xff08;defineEmits&#xff09; 5.子传父&#xff08;v-model&#xff09; …

数据库 mysql 的彻底卸载

MySQL卸载步骤如下&#xff1a; &#xff08;1&#xff09;按 winr 快捷键&#xff0c;在弹出的窗口输入 services.msc&#xff0c;打开服务列表。 &#xff08;2&#xff09;在服务列表中&#xff0c; 找到 mysql 开头的所有服务&#xff0c; 右键停止&#xff0c;终止对应的…

【问题随记】tightvnc 连接后灰屏

问题描述 刚刚入手了官方发的 OrangePi AI Pro&#xff0c;想用 tight vnc 来连接开发板&#xff0c;就不用连接屏幕那么麻烦了。结果连接后&#xff0c;没能显示 OrangePi AI Pro 桌面。 问题解决 看一下现有的桌面环境。 apt list --installed | grep desktop从中可以看到…

游戏找不到d3dcompiler43.dll怎么办,分享5种有效的解决方法

在计算机使用过程中&#xff0c;我们经常会遇到一些错误提示&#xff0c;其中之一就是找不到某个文件。其中&#xff0c;找不到d3dcompiler43.dll是一个常见的问题。这个问题通常出现在运行某些游戏或应用程序时&#xff0c;由于缺少了d3dcompiler43.dll文件&#xff0c;导致程…

【PTA】7-3 拯救007(C++)代码实现 易错点反思

题目见下: 输入样例 14 20 25 -15 -25 28 8 49 29 15 -35 -2 5 28 27 -29 -8 -28 -20 -35 -25 -20 -13 29 -30 15 -35 40 12 12 //输入上述数据后输出“Yes” AC代码如下: #include<bits/stdc++.h> using namespace std; #define sz 100 typedef struct node{int …

基于javacv ffmpeg 使用原生ffmpeg命令

基于javacv ffmpeg 使用原生ffmpeg命令 1. ffmpeg2. ffprobe 相关阅读&#xff1a; javacv ffmpeg使用笔记 测试过程中&#xff0c;发现ffmpeg-6.0-1.5.9-linux-x86_64.jar 存在问题&#xff08;ffmpeg原生命令执行失败&#xff09;&#xff0c;降级到ffmpeg-5.1.2-1.5.8-linux…

Mixly 开启WIFI AP UDP收发数据

一、开发环境 软件&#xff1a;Mixly 2.0在线版 硬件&#xff1a;ESP32-C3&#xff08;立创实战派&#xff09; 固件&#xff1a;ESP32C3 Generic(UART) 测试工工具&#xff1a;NetAssist V5.0.1 二、实现功能 ESP32开启WIFI AP&#xff0c;打印接入点IP地址&#xff0c;允许…

直播预告|手把手教你玩转 Milvus Lite !

Milvus Lite&#xff08;https://milvus.io/docs/milvus_lite.md&#xff09;是一个轻量级向量数据库&#xff0c;支持本地运行&#xff0c;可用于搭建 Python 应用&#xff0c;由 Zilliz 基于全球最受欢迎的开源向量数据库 Milvus&#xff08;https://milvus.io/intro&#xf…

Pandas读取文本文件为多列

要使用Pandas将文本文件读取为多列数据&#xff0c;你可以使用pandas.read_csv()函数&#xff0c;并通过指定适当的分隔符来确保正确解析文件中的数据并将其分隔到多个列中。 假设你有一个以逗号分隔的文本文件&#xff08;CSV格式&#xff09;&#xff0c;每一行包含多个值&a…

二分查找算法介绍(边界值、循环条件、值的变化、二分查找的原理、异常处理)

一、二分查找法原理介绍 二分查找是经典的查找算法之一&#xff0c;其原理也非常简单。 对于已排序的数组&#xff08;假设是整型&#xff0c;如果非整型&#xff0c;如果有排序和大小比较的定义&#xff0c;也可以使用二分查找&#xff09;&#xff0c;我们每次判断中间值与目…

单位企业是如何禁用USB接口的(公司禁止USB接口的三大方法)

在当前信息化时代&#xff0c;单位企业对于数据安全的重视程度日益增加&#xff0c;尤其是防止通过USB接口导致的数据泄露和恶意软件传播。 为了构建一个更加安全的办公环境&#xff0c;许多企业采取措施禁用USB接口。 以下是公司禁止USB接口的三大常用方法&#xff1a; 1. 部…

AI绘画Stable Diffusion最新整合包,开源免费 AI 绘图工具神器,解压即用!

写在前面 众所周知现在的AI绘画可谓是热火朝天&#xff0c;前有国外的Midjourney&#xff0c;后有国内各大平台推出的 各种AI工具等&#xff0c;但是目前的这些线上的AI绘画都会有生成次数、时长等限制&#xff0c;有时候还得排队等待出图&#xff0c;所以免费开源的 Stable D…

大话设计模式解读01-简单工厂模式

本系列的文章&#xff0c;来介绍编程中的设计模式&#xff0c;介绍的内容主要为《大话设计模式》的读书笔记&#xff0c;并改用C语言来实现&#xff08;书中使用的是.NET中的C#&#xff09;,本篇来学习第一章&#xff0c;介绍的设计模式是——简单工厂模式。 1 面向对象编程 …

989.数组形式的整数加法

对于非负整数 X而言&#xff0c;x的数组形式是每位数字按从左到右的顺序形成 的数组。例如&#xff0c;如果 X1231&#xff0c;那么其数组形式为[1,2,3,1]。 给定非负整数 X的数组形式 A&#xff0c;返回整数 X 的数组形式, #include <stdio.h> #include <stdlib.h>…

【面试经典150题】删除有序数组中的重复项||

目录 一.题目解析二.解法一三.解法二 一.题目解析 首先我们先看一下题目描述&#xff1a; 删除数组中的重复项的升级版要求&#xff0c;一个升序数组序列中&#xff0c;相同的元素最多出现两次。 二.解法一 首项我们先来看一种比较繁琐坑比较多的解法&#xff1a; class S…

JCR一区级 | Matlab实现TCN-BiLSTM-MATT时间卷积双向长短期记忆神经网络多特征分类预测

JCR一区级 | Matlab实现TCN-BiLSTM-MATT时间卷积双向长短期记忆神经网络多特征分类预测 目录 JCR一区级 | Matlab实现TCN-BiLSTM-MATT时间卷积双向长短期记忆神经网络多特征分类预测分类效果基本介绍程序设计参考资料 分类效果 基本介绍 1.JMatlab实现TCN-BiLSTM-MATT时间卷积双…

Spring Boot 整合开源 Tess4J库 实现OCR图片文字识别

&#x1f604; 19年之后由于某些原因断更了三年&#xff0c;23年重新扬帆起航&#xff0c;推出更多优质博文&#xff0c;希望大家多多支持&#xff5e; &#x1f337; 古之立大事者&#xff0c;不惟有超世之才&#xff0c;亦必有坚忍不拔之志 &#x1f390; 个人CSND主页——Mi…

Python | 试卷刷题and基础笔记

1.下列转义字符中&#xff0c; 属于“回车”含义的是 \n 换行 \r 回车 2.for循环遍历字典 在Python中&#xff0c;你可以使用for循环来遍历字典的键&#xff08;keys&#xff09;、值&#xff08;values&#xff09;或者键-值对&#xff08;items&#xff09;。下面是三种遍历…