当前位置: 首页 > news >正文

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;@Overridepublic 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@AutowiredAlbumRepo repo;// @Disabled@DisplayName("execute DDL, DML automatically, as App startup")@Testpublic 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 {@Overrideprotected 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 embeddedreturn false;}}----------// org.springframework.boot.jdbc.EmbeddedDatabaseConnection.IsEmbedded@Overridepublic 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),运行在严肃的生产环境

相关文章:

Spring boot 随笔 1 DatasourceInitializer

0. 为啥感觉升级了 win11 之后&#xff0c;电脑像是刚买回来的&#xff0c;很快 这篇加餐完全是一个意外&#xff1a;时隔两年半&#xff0c;再看 Springboot-quartz-starter 集成实现的时候&#xff0c;不知道为啥我的h2 在应用启动的时候&#xff0c;不能自动创建quartz相关…...

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;全局锁使用场景&#xff1a;全局锁备份案例&#xff1a; mysql按照锁的粒度分类 全局锁 概念&#xff1a; 全局锁就是对整个数据库实例加锁。MySQL 提供了一个加全局读锁的方法&#xff0c;命令是: Flush tables with…...

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

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

使用 Docker 和 Docker Compose 部署 Vue

使用 Docker 和 Docker Compose 部署 Vue 项目有两种方式&#xff1a;直接使用 Docker 和使用 Docker Compose。 创建 Dockerfile 在Vue.js项目根目录下创建一个 Dockerfile 的文件 # 使用最新的官方 Node.js 镜像作为基础镜像&#xff0c;并命名为 builder 阶段 FROM node:…...

力扣linkedlist

反转链表、 public class reverseList { // 1->2->3->o 、 o<-1<-2<-3public ListNode reverseList(ListNode head){//反转链表ListNode prevnull;ListNode currhead;while(curr!null){ListNode nextcurr.next;curr.nextprev;prevcurr;currnext;}retu…...

springboot 启动原理、启动过程、启动机制的介绍

Spring Boot 是一种基于 Java 的框架,用于创建独立的、生产级别的 Spring 应用程序。它的主要目标是简化 Spring 应用的初始搭建和开发过程,同时提供一系列大型项目常见的非功能性特征(如嵌入式服务器、安全性、度量、健康检查和外部化配置)。以下是 Spring Boot 的一些核心…...

大模型ChatGLM的部署与微调

前言&#xff1a;最近大模型太火了&#xff0c;导师让我看看能不能用到自己的实验中&#xff0c;就想着先微调一个chatGLM试试水&#xff0c;微调的过程并不难&#xff0c;难的的硬件条件跟不上&#xff0c;我试了一下lora微调&#xff0c;也算跑通了吧&#xff0c;虽然最后评估…...

全球七家半导体工厂建设受阻:英特尔、三星、台积电等面临延期挑战

过去两年间&#xff0c;半导体行业经历了市场衰退、复苏慢于预期以及资金紧缩等问题&#xff0c;英特尔、台积电和三星等主要企业虽然继续推进扩张计划&#xff0c;但不断调整和放缓工厂建设的步伐与时间表&#xff0c;以更好地服务于长期发展目标。据统计&#xff0c;全球范围…...

JavaScript错误;调试;“=”,“==”,“===”的区别

try...catch语句 try..catch语句是JavaScript中用来处理异常的一种方式。它允许我们在代码块中尝试执行可能会引发错误的代码&#xff0c;并在发生错误时捕获并处理异常。 下面是try..catch语句的基本语法&#xff1a; try {// 可能会引发错误的代码 } catch (error) {// 处理…...

thinkphp6的请求

由于笔者是刚入门thinkphp&#xff0c;所以学习时对照thinkphp的官网&#xff0c;各位读者也可以对照官网学习。还麻烦各位笔者一键三连&#xff0c;谢谢。 1.请求对象 当前的请求对象由think\Request类负责&#xff0c;该类不需要单独实例化调用&#xff0c;通常使用依赖注入…...

ant design vue 表格错位,表头错位

ant design vue 表格错位,表头错位 在官网中,我们可以看到下面图片的描述: 好的,我们按照官网来一波,前面都设置了固定宽度,娃哈哈就不设置了.会出现下面效果 为啥会多了一个竖线(因为按照官网来一波x:1300,这个1300太小的原因) 3.那我们把1300改成1600,1700试试,结果也不是…...

【小白向】微信小程序解密反编译教程

# 前言 最近笔者有做到微信小程序的渗透测试&#xff0c;其中有一个环节就是对微信小程序的反编译进行源码分析&#xff0c;所谓微信小程序反编译&#xff0c;就是将访问的小程序进行反向编译拿到部分源码&#xff0c;然后对源码进行安全审计&#xff0c;分析出其中可能存在的…...

Flutter基础 -- Dart 语言 -- 类抽象接口继承函数库

目录 1. 类 class 1.1 定义、使用类 1.2 构造函数 1.3 初始化列表 1.4 命名构造函数 1.5 重定向构造函数 1.6 callable 2. 类 get set 2.1 定义、使用 get set 2.2 简化 get set 2.3 业务场景 3. 静态 static 3.1 static 定义 3.2 函数内部访问 3.3 静态方法 3…...

【TB作品】msp430单片机,播放蜂鸣器音乐,天空之城

功能 msp430单片机&#xff0c;连接一个无源蜂鸣器&#xff0c;播放蜂鸣器音乐&#xff0c;天空之城。 适用于所有msp430单片机。 硬件 无源蜂鸣器&#xff0c;接单片机P1.5&#xff0c;使用vcc3.3v供电。 如果根据简谱修改音乐? //第一步 //首先修改music0 的变量&…...

C语言(数据存储)

Hi~&#xff01;这里是奋斗的小羊&#xff0c;很荣幸各位能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎~~ &#x1f4a5;个人主页&#xff1a;小羊在奋斗 &#x1f4a5;所属专栏&#xff1a;C语言 本系列文章为个人学习笔记&#xff0c;在这里撰写成文一…...

Linux shell编程学习笔记56:date命令——显示或设置系统时间与日期

0 前言 2024年的网络安全检查又开始了&#xff0c;对于使用基于Linux的国产电脑&#xff0c;我们可以编写一个脚本来收集系统的有关信息。在收集的信息中&#xff0c;应该有一条是搜索信息的时间。 1. date命令 的功能、格式和选项说明 我们可以使用命令 date --help 来查看 d…...

Realsense的一些事情

Realsense的一些事情 librealsense的安装 官网教程&#xff1a; apt 安装教程&#xff1a; https://github.com/IntelRealSense/librealsense/blob/master/doc/distribution_linux.md自行clone并编译教程&#xff1a; https://github.com/IntelRealSense/librealsense/blo…...

CISCN 2023 初赛 被加密的生产流量

题目附件给了 modbus.pcap 存在多个协议 但是这道题多半是 考 modbus 会发现 每次的 Query 末尾的两个字符 存在规律 猜测是base家族 可以尝试提取流量中的数据 其中Word Count字段中的22871 是10进制转16进制在转ascii字符串 先提取 过滤器判断字段 tshark -r modbus.pcap …...

初识C语言第三十天——设计三子棋游戏

目录 一.设计游戏框架 1.打印游戏菜单 2.输入选择判断&#xff08;玩游戏/游戏结束/输入错误重新输入&#xff09; 二、玩游戏过程设计 1.设计棋格存放棋子——二维数组 2.初始化棋盘——初始化为空格 3.打印棋盘——本质上就是打印数组 4.游戏过程——1.玩家走棋 2.…...

ehcache3多级缓存应用

项目中如果有使用大量的本地缓存场景&#xff0c;可以使用redisehcache组合缓存&#xff0c;优先使用ehcache本地缓存&#xff0c;本地缓存没有查询到再使用redis缓存 可看前文中如何集成 本地缓存使用存在的问题 1、本地缓存如何保证缓存的是最新值 可定义版本号、自增id或者…...

C# WinForm —— 24 Threading.Timer 组件介绍与使用

1. 简介 System.Threading.Timer 多线程 轻量级 精度高 提供以指定的时间间隔对线程池线程执行方法的机制 和System.Timers.Timer 类似&#xff0c;每隔一段时间触发事件&#xff0c;执行操作(不是由UI线程执行的)&#xff0c;即使事件中执行了比较耗时的操作&#xff0c;也…...

03-07Java自动化之JAVA基础之循环

JAVA基础之循环 一、for循环 1.1for循环的含义 for&#xff08;初始化语句;条件判断;条件控制或–&#xff09;{ ​ //代码语句 } 1、首先执行初始话语句&#xff0c;给变量一个起始的值 2、条件判断进行判断&#xff0c;为true&#xff0c;执行循环体中的代码语句 ​ …...

【人工智能Ⅱ】实验8:生成对抗网络

实验8&#xff1a;生成对抗网络 一&#xff1a;实验目的 1&#xff1a;理解生成对抗网络的基本原理。 2&#xff1a;学会构建改进的生成对抗网络&#xff0c;如DCGAN、WGAN、WGAN-GP等。 3&#xff1a;学习在更为真实的数据集上应用生成对抗网络的方法。 二&#xff1a;实验…...

vmware将物理机|虚拟机转化为vmware虚机

有时&#xff0c;我们需要从不同的云平台迁移虚拟机、上下云、或者需要将不再受支持的老旧的物理服务器转化为虚拟机&#xff0c;这时&#xff0c;我们可以用一款虚拟机转化工具&#xff1a;vmware vcenter converter standalone&#xff0c;我用的是6.6的版本&#xff0c;当然…...

redis 高可用及哨兵模式 @by_TWJ

目录 1. 高可用2. redis 哨兵模式3. 图文的方式让我们读懂这几个算法3.1. Raft算法 - 图文3.2. Paxos算法 - 图文3.3. 区别&#xff1a; 1. 高可用 在 Redis 中&#xff0c;实现 高可用 的技术主要包括 持久化、复制、哨兵 和 集群&#xff0c;下面简单说明它们的作用&#xf…...

封装tab栏,tab切换可刷新页面

dom结构 <template><div class"container"><!-- tab栏 --><div class"border-b"><tabs:tabsList"tabsList":selectTabsIndex"selectTabsIndex"tabsEven"tabsEven"></tabs></div>…...

JavaScript第八讲:日期,Math,自定义对象

目录 前言 日期 1. 创建日期对象 2. 年/月/日 3. 时:分:秒:毫秒 4. 一周的第几天 5. 经历的毫秒数 6. 修改日期和时间 Math 1. 自然对数和圆周率 2. 绝对值 3. 最小最大 4. 求幂 5. 四舍五入 6. 随机数 自定义对象 1. 通过 new Object 创建对象 2. 通过 funct…...

php质量工具系列之phploc

phploc是一个快速测量PHP项目大小的工具 结果支持raw csv xml json格式&#xff0c;可输出文件或者打印到控制台 安装 PHAR方式 wget https://phar.phpunit.de/phploc.pharphp phploc.phar -vComposer方式(推荐) composer global require --dev phploc/phplocphploc -v使用 …...

创建模拟器

修改模拟器默认路径 由于模拟器文件比较大&#xff0c;默认路径在C:\Users\用户名.android\avd&#xff0c;可修改默认路径 创建修改后的路径文件 D:\A-software\Android\AVD添加系统变量ANDROID_SDK_HOME&#xff1a;D:\A-software\Android\AVD重启Android Studio 创建模拟…...