- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我们有一个 Spring 测试执行监听器,它通过 Spring 的 ResourceDatabasePopulator
导入转储的 MySQL 表定义:
public class DBSetupExecutionListener extends AbstractTestExecutionListener {
...
@Override
public synchronized void beforeTestClass(TestContext testContext) {
...
// import the table definitions from a previously dumped file
ResourceDatabasePopulator populator = new ResourceDatabasePopulator();
populator.addScript(new ClassPathResource("some-table-definitions.sql"));
populator.execute(dataSource);
...
}
...
}
导入的 MySQL 转储文件确实如下所示(简化):
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
DROP TABLE IF EXISTS `someTable`;
CREATE TABLE `someTable`(
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`other_table_id` bigint(20) NOT NULL,
...
PRIMARY KEY (`id`)
KEY `FK_OTHERTABLE_ID`(`other_table_id`),
CONSTRAINT `FK_OTHERTABLE_ID` FOREIGN KEY (`other_table_id`) REFERENCES `otherTable`(`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `otherTable`;
CREATE TABLE `otherTable`(
`id` bigint(20) NOT NULL AUTO_INCREMENT,
...
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
...
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
通过 command line 导入转储时工作没有任何问题,ResourceDatabasePopulator 忽略预处理器指令,因此在创建实际引用表之前无法首先创建引用表。更改表定义的顺序确实可以解决问题,但对于多个表来说有点乏味 - 特别是当您将当前表定义转储到文件时。
由于导入转储的 SQL 表定义是一项非常常见的任务,我想我在这里做错了什么。有没有办法告诉ResourceDatabasePopulator遵守预处理指令,从而防止在执行脚本时进行外键检查?
最佳答案
我听从了 @M.Deinum 的建议,实现了我自己版本的 ResourceDatabasePopulator
。由于成员范围有限并且没有任何 getter 来检索这些字段,不幸的是,原始 ResourceDatabasePopulator
实现中存在一些复制和粘贴。这个类绝对不是为支持子类化而设计的......
下面是在我的用例中适用的当前解决方案。它并不完美,可能是针对 MySQL 语句量身定制的,但至少它完成了工作。
import java.io.BufferedReader;
import java.io.IOException;
import java.lang.invoke.MethodHandles;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.EncodedResource;
import org.springframework.jdbc.datasource.init.DatabasePopulatorUtils;
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
import org.springframework.jdbc.datasource.init.ScriptUtils;
import org.springframework.jdbc.datasource.init.UncategorizedScriptException;
import org.springframework.util.StringUtils;
/**
* Populates, initializes, or cleans up a database using SQL scripts defined in
* external resources.
* <p>
* This implementation will clean up a given script by removing comments and processing instructions
* contained in the script and perform a table reordering based on defined foreign keys in a
* best-effort attempt.
*
* <ul>
* <li>Call {@link #addScript} to add a single SQL script location.
* <li>Call {@link #addScripts} to add multiple SQL script locations.
* <li>Consult the setter methods in this class for further configuration options.
* <li>Call {@link #populate} or {@link #execute} to initialize or clean up the
* database using the configured scripts.
* </ul>
*
* @author Keith Donald
* @author Dave Syer
* @author Juergen Hoeller
* @author Chris Beams
* @author Oliver Gierke
* @author Sam Brannen
* @author Chris Baldwin
* @author Roman Vottner
*
* @since 3.0
* @see DatabasePopulatorUtils
* @see ScriptUtils
*/
public class OrderedResourceDatabasePopulator extends ResourceDatabasePopulator {
private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
protected String sqlScriptEncoding;
/**
* Construct a new {@code OrderedResourceDatabasePopulator} with default settings.
*/
public OrderedResourceDatabasePopulator() {
/* no-op */
}
/**
* Construct a new {@code OrderedResourceDatabasePopulator} with default settings
* for the supplied scripts.
*
* @param scripts the scripts to execute to initialize or clean up the database
* (never {@code null})
*/
public OrderedResourceDatabasePopulator(Resource... scripts) {
this();
setScripts(cleanResource(scripts));
}
/**
* Construct a new {@code OrderedResourceDatabasePopulator} with the supplied values.
*
* @param continueOnError flag to indicate that all failures in SQL should be logged but not cause
* a failure
* @param ignoreFailedDrops flag to indicate that a failed SQL {@code DROP} statement can be
* ignored
* @param sqlScriptEncoding the encoding for the supplied SQL scripts; may be {@code null} or
* <em>empty</em> to indicate platform encoding
* @param scripts the scripts to execute to initialize or clean up the database (never {@code
* null})
*/
public OrderedResourceDatabasePopulator(boolean continueOnError, boolean ignoreFailedDrops,
String sqlScriptEncoding, Resource... scripts) {
super(continueOnError, ignoreFailedDrops, sqlScriptEncoding, scripts);
}
/**
* Add a script to execute to initialize or clean up the database.
*
* @param script the path to an SQL script (never {@code null})
*/
@Override
public void addScript(Resource script) {
super.addScript(cleanResource(script)[0]);
}
/**
* Add multiple scripts to execute to initialize or clean up the database.
*
* @param scripts the scripts to execute (never {@code null})
*/
@Override
public void addScripts(Resource... scripts) {
super.addScripts(cleanResource(scripts));
}
/**
* Set the scripts to execute to initialize or clean up the database, replacing any previously
* added scripts.
*
* @param scripts the scripts to execute (never {@code null})
*/
@Override
public void setScripts(Resource... scripts) {
super.setScripts(cleanResource(scripts));
}
/**
* Specify the encoding for the configured SQL scripts, if different from the platform encoding.
*
* @param sqlScriptEncoding the encoding used in scripts; may be {@code null} or empty to indicate
* platform encoding
*
* @see #addScript(Resource)
*/
@Override
public void setSqlScriptEncoding(String sqlScriptEncoding) {
super.setSqlScriptEncoding(sqlScriptEncoding);
this.sqlScriptEncoding = StringUtils.hasText(sqlScriptEncoding) ? sqlScriptEncoding : null;
}
private Resource[] cleanResource(Resource... scripts) {
List<Resource> cleaned = new ArrayList<>();
for (Resource script : scripts) {
EncodedResource encodedScript = new EncodedResource(script, this.sqlScriptEncoding);
StringBuilder sb = new StringBuilder();
try (BufferedReader in = new BufferedReader(encodedScript.getReader())) {
String line;
// parse script
Map<String, Table> tables = new LinkedHashMap<>();
Table curTable = null;
while ((line = in.readLine()) != null) {
if (!line.startsWith("#") && !line.startsWith("/*")) {
line = line.replaceAll("`", "");
String lowerCaseLine = line.toLowerCase();
// parse table definitions
if (lowerCaseLine.startsWith("drop table") || "".equals(line.trim())) {
if (curTable != null) {
curTable = null;
}
continue;
}
curTable = parseTable(line, tables, curTable);
}
}
// check the order of table definitions and reorder them to ensure referenced tables are
// defined before table specifying the foreign key
Map<String, Table> orderedTables = new LinkedHashMap<>();
List<Table> backlog = new ArrayList<>();
for (String tableName : tables.keySet()) {
// check for available foreign key definitions
List<String> referencedTables = tables.get(tableName).getReferencedTables();
// if no foreign keys are defined in the table we can add the table to the current set
if (referencedTables.isEmpty()) {
orderedTables.put(tableName, tables.get(tableName));
processBacklog(backlog, orderedTables);
continue;
}
// foreign keys are defined. If any of the referenced tables is not yet in the set move it
// to the backlog table and try it later
boolean dependenciesAvailable = true;
for (String dependency : referencedTables) {
if (!orderedTables.containsKey(dependency)) {
backlog.add(tables.get(tableName));
dependenciesAvailable = false;
break;
}
}
// if all referred tables are available in the set add the current table also to the
// ordered set
if (dependenciesAvailable) {
orderedTables.put(tableName, tables.get(tableName));
processBacklog(backlog, orderedTables);
continue;
}
// probe tables kept in the backlog again
processBacklog(backlog, orderedTables);
}
while (!backlog.isEmpty()) {
processBacklog(backlog, orderedTables);
}
ArrayList<Table> reverse = new ArrayList<>(orderedTables.values());
ListIterator<Table> reverseIter = reverse.listIterator(reverse.size());
sb.append("DROP TABLE IF EXISTS ");
int originalSize = sb.length();
while (reverseIter.hasPrevious()) {
if (sb.length() > originalSize) {
sb.append(", ");
}
sb.append(reverseIter.previous().getName());
}
sb.append(";\n\n");
for (Table table : orderedTables.values()) {
sb.append(table.getCreateStatement()).append("\n");
}
} catch (IOException ex) {
throw new UncategorizedScriptException(ex.getMessage(), ex);
}
if (LOG.isTraceEnabled()) {
LOG.trace("SQL script after cleaning: \n{}", sb.toString());
}
Resource cleanedResource = new ByteArrayResource(sb.toString().getBytes());
cleaned.add(cleanedResource);
}
return cleaned.toArray(new Resource[cleaned.size()]);
}
private Table parseTable(String line, Map<String, Table> tables, Table curTable) {
String lowerCaseLine = line.toLowerCase();
if (lowerCaseLine.startsWith("create table")) {
String name = line.substring("create table ".length(), line.indexOf("(")).trim();
curTable = new Table(name);
tables.put(name, curTable);
} else if (curTable != null) {
if (lowerCaseLine.contains("primary key")) {
curTable.setPrimaryKey(line);
} else if (lowerCaseLine.contains("unique key")) {
curTable.addUniqueKey(line);
} else if (lowerCaseLine.contains("foreign key")) {
curTable.addForeignKey(line);
} else if (lowerCaseLine.contains(" key ")) {
curTable.addIndex(line);
} else if (lowerCaseLine.contains(" charset=") || lowerCaseLine.contains("engine=")) {
curTable.setMetaData(line);
} else {
curTable.addColumn(line);
}
}
return curTable;
}
private void processBacklog(List<Table> backlog, Map<String, Table> orderedTables) {
Iterator<Table> iter = backlog.iterator();
while (iter.hasNext()) {
Table table = iter.next();
boolean allDependenciesAvailable = true;
for (String dependency : table.getReferencedTables()) {
if (!orderedTables.containsKey(dependency)) {
allDependenciesAvailable = false;
}
if (allDependenciesAvailable) {
orderedTables.put(table.getName(), table);
iter.remove();
}
}
}
}
private class Table {
private final String name;
private List<String> columns = new ArrayList<>();
private String primaryKey;
private List<String> uniqueKeys = new ArrayList<>();
private List<String> indices = new ArrayList<>();
private List<String> foreignKeys = new ArrayList<>();
private String metaData;
public Table(String name) {
this.name = name;
}
String getName() {
return this.name;
}
void addColumn(String column) {
this.columns.add(column);
}
void setPrimaryKey(String primaryKey) {
this.primaryKey = primaryKey;
}
void addUniqueKey(String uniqueKey) {
this.uniqueKeys.add(uniqueKey);
}
void addIndex(String index) {
this.indices.add(index);
}
void addForeignKey(String foreignKey) {
this.foreignKeys.add(foreignKey);
}
List<String> getReferencedTables() {
List<String> referencedTables = new ArrayList<>();
for (String foreignKey : foreignKeys) {
int start = foreignKey.toLowerCase().indexOf("references ") + "references ".length();
String table = foreignKey.substring(start, foreignKey.indexOf(" ", start));
referencedTables.add(table);
}
return referencedTables;
}
void setMetaData(String metaData) {
this.metaData = metaData;
}
String getCreateStatement() {
StringBuilder sb = new StringBuilder();
sb.append("CREATE TABLE ").append(this.name).append(" (");
for (String column : this.columns) {
sb.append("\n");
sb.append(" ").append(column);
}
if (null != primaryKey) {
sb.append("\n");
sb.append(" ").append(this.primaryKey);
}
if (!uniqueKeys.isEmpty()) {
for (String uniqueKey : uniqueKeys) {
sb.append("\n");
sb.append(" ").append(uniqueKey);
}
}
if (!indices.isEmpty()) {
for (String index : indices) {
sb.append("\n");
sb.append(" ").append(index);
}
}
if (!foreignKeys.isEmpty()) {
for (String foreignKey : foreignKeys) {
sb.append("\n");
sb.append(" ").append(foreignKey);
}
}
sb.append("\n");
if (metaData == null || !metaData.contains(")")) {
sb.append(") ");
}
if (metaData != null) {
sb.append(metaData);
}
sb.append("\n");
return sb.toString();
}
}
}
此填充器将首先清除给定脚本中任何不需要的字符、注释或处理指令,然后构建虚拟表结构,以便在声明外部引用的表之前对作为外部引用目标的表进行排序。表定义虚拟化后,填充器将重新创建脚本,方法是首先按虚拟模型的相反顺序为每个表添加 DROP TABLE 语句,然后添加 CREATE TABLE 语句按计算顺序排列的语句。
请注意,此版本尚不支持脚本中包含的其他 SQL 命令(例如 INSERT,...)。如果需要,请随意根据您的喜好自定义此代码。
为了使用重新排序的 SQL 脚本,只需将 ResourceDatabasePopulator
替换为 OrderedResourceDatabasePopulator
@Override
public synchronized void beforeTestClass(TestContext testContext) {
...
// import the table definitions from a previously dumped file
ResourceDatabasePopulator populator = new OrderedResourceDatabasePopulator();
populator.addScript(new ClassPathResource("some-table-definitions.sql"));
populator.execute(dataSource);
...
}
关于java - 使用 Spring 的 ResourceDatabasePopulator 导入 MySQL 表定义转储,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37833643/
我已经在标准 WPF 控件中实现了一个报告,并且还实现了一个 DocumentPaginator获取这些控件并将它们转换为用于打印的文档。 我还实现了一些使用文档分页器将页面呈现为图像并使用 PDFS
在 C# 中,我有以下代码: public static string GetHashCode(string p) { var a = new SHA256Managed();
您好,我正在尝试在编码后将我的 mysqli 数据库输出到一个 js 文件,我用 json_encode 对其进行编码没有任何问题,但是如何将其放入 js 文件中(每次更新时更新) mysqli数据已
我需要将 select 从 JS 传递到 HTML。 select 应该包含来自 PHP 的 option。 所以,首先我有一个 HTML div,我将在其中添加来自 JS 的内容。
我有一个相当大且复杂的 SVG 代码,它根据页面信息使用 JavaScript 和 jQuery 动态生成。 然后我有一个 AJAX 帖子保存。 我无法将其转换为正确发布图像数据? var canva
我想将我的本地日期 ([NSDate date]) 转换为 GMT 以创建一个 JSON 字符串 (/Date(1324435876019-0000)/)。 当我将时钟设置为 EST 时区时,我的代码
1. 原始单据与实体之间的关系 可以是一对1、一对多、多对多的关系。在一般情况下,它们是一对一的关系:即一张原始单据对应且只对应一个实体。在特殊情况下,它们可能是一对多或多对一的关系,即一张原
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界. 这篇CFSDN的博客文章服务器五大相关基础知识【转】由作者收集整理,如果你对这篇文章有兴趣,记得
Google Apps 脚本 - Gmail 是否会实现 GmailMessage (GmailThread) .getAsPdf() 方法?预期输出与 Gmail 中可用的打印为 PDF 的输出相同
有一个需求是要在一个云监控的状态值中存储多个状态(包括可同时存在的各种异常、警告状态)使用了位运算机制在一个int型中存储。 现在监控日志数据量非常大(亿级别)需要对数据按每小时、每天进行聚合,供
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界. 这篇CFSDN的博客文章1张图看懂RAID功能,6张图教会配置服务器【转】由作者收集整理,如果你
我正在使用 FFMPeg(版本 ffmpeg-20170330-ad7aff0-win64-static)将 RTSP 转换为 .m3u8。 命令是: ffmpeg -rtsp_transport t
我有一个 JTree使用 DefaultTreeModel 的对象作为模型,我添加/删除与该模型相关的节点。 此时,我需要在图形界面中显示树结构,例如 JPanel .如何映射 DefaultTree
我当前正在接收一个文件并将其存储到 NSString 中。然后,我从字符串中创建一个数组并将其呈现在 TableView 中。这在一定程度上有效。我目前收到的数据如下: 公司名称|帐户代码\r\n公司
我需要创建 NSImage cocoa 对象的 base64 字符串表示形式。处理这个问题的最佳方法是什么,苹果文档似乎在这个主题上有点短(或者我只是找不到它)。 Base64 编码从外面看起来相当复
JS 中的 .toISOString() 函数给我这样的字符串: 2015-06-14T20:00:00:000Z 我需要它是这样的: 2015-06-14T20:00:00Z JS 中是否有其他函数
我正在尝试使用 JavaScript 转换 COLORREF: COLORREF : When specifying an explicit RGB color, the COLORREF value
我在这里遇到了这个代码的问题,只是想制作一个小计算器: 打包申请; import javafx.event.ActionEvent; import javafx.scene.control.TextF
我想要做的是能够通过本地PC上的USS通过sshfs挂载主机上的一些文件。我可以做到这一点,但 sshfs 不能直接完成从 EBCDIC 到 ascii/unicode 的转换。有没有我可以设置的标志
我正在尝试在 python 中将一堆 Visio 文件转换为 pdf。我已经引用了这个.doc to pdf using python并编写了以下代码: import comtypes.client
我是一名优秀的程序员,十分优秀!