![](/img/trans.png)
[英]Liquibase doesn't load *.sql from classpath inside of changeSet
[英]Liquibase execution order of changeset files when using includeAll with classpath*:
我在春季环境(3.2.x)中使用liquibase(3.1.1),并通过inlcudeAll标记将更改集加载到主文件中。 在那里,我使用“ classpath *:/ package / to / changesets”作为路径。
<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
<includeAll path="classpath*:/package/to/changesets"/>...
我使用诸如“ nnn_changesetname.xml”之类的命名策略来保持排序。 但是当我查看变更集表时,不会保留通过文件名进行的排序。 仅当变更集文件包含在目录中而不在类路径中时,这才起作用吗?
更新
嗨,我发现以下建议的解决方案是不够的。 我认为这取决于实现liquibase如何解决includAll属性。 就我而言,它首先解析所有“文件夹”,然后在每个文件夹中查找changeset xml。 这将破坏所有classpath *:/ changes位置中xml文件的顺序,因为现在在不同位置有多个“ changes”文件夹。 在这种情况下,我会怀疑是将这个“虚拟”类路径文件夹的所有内容合并在一起,并在一个枚举中加载了所有资源。 或者,我们可以允许inlcudeAll标签中的一些资源模式(例如resources =“ classpath *:/ changes / *。xml”)直接选择所有需要的文件(使用path属性对其进行了尝试,但是没有用,因为它会检查是否存在夹)?
更新
我做了一个黑客检查返回枚举中的顺序是否从下面用anwser保留。 为了达到这个目的,我检查了给定的程序包名称,如果它与我的模式匹配,我在其中添加了一个额外的“ * .xml”。 通过此扩展,我可以根据需要获取所有变更集。
@Override
public Enumeration<URL> getResources(String packageName)
throws IOException {
if(packageName.equals("classpath*:/plugin/liquibase/changes/")) {
packageName = packageName + "*.xml";
}
List<URL> resources = Collections.list(super.getResources(packageName));
Collections.sort(resources, new Comparator<URL>() {
@Override
public int compare(URL url1, URL url2) {
String path1 = FilenameUtils.getName(url1.getPath());
String path2 = FilenameUtils.getName(url2.getPath());
return String.CASE_INSENSITIVE_ORDER.compare(path1, path2);
}
});
logger.info("Found resources: {}", resources);
return Collections.enumeration(resources);
}};
在日志中,我现在可以看到资源具有正确的顺序。 但是当我查看表DATABASECHANGELOCK时,它不能反映我在枚举中的顺序。 因此,似乎该值在其他地方被拒绝了。
更新
对代码进行了进一步分析,发现类liquibase.parser.core.xml.XMLChangeLogSAXHandler对返回的枚举进行了重新排序。 因此,我的更改将无效。 我不认为我也可以参加此类课程。
没错,Liquibase依赖于底层的“列表文件”逻辑,该逻辑通过文件系统按字母顺序对文件进行排序,但显然不通过类路径。
我创建了https://liquibase.jira.com/browse/CORE-1843来跟踪此修复程序。
现在,如果您使用spring.springLiquibase的liquibase.integration.spring.SpringLiquibase子类配置spring,该子类使用对返回的枚举进行排序的方法覆盖getResources(String packageName),该枚举应为您解决问题。
因此,经过一番思考和一夜的睡眠,我想出了以下技巧,以通过classpath模式classpath *:/ my / path / to / changelog / *。xml保证加载的changelog文件的顺序。 这个想法是在liquibase请求时通过dom操作即时创建主变更日志文件。
它仅适用于主变更日志文件。 满足以下先决条件:
首先,我必须在实现中扩展/覆盖liquibase.integration.spring.SpringLiquibase。
public class MySpringLiquibase extends SpringLiquibase {
private static final Logger logger = LoggerFactory.getLogger(MySpringLiquibase.class);
private ApplicationContext context;
private String changeLogLocationPattern;
private List<String> changeLogLocations;
@Autowired
public void setContext(ApplicationContext context) {
this.context = context;
}
/**
* Location pattern to search for changelog files.
*
* @param changeLogLocationPattern
*/
public void setChangeLogLocationPattern(String changeLogLocationPattern) {
this.changeLogLocationPattern = changeLogLocationPattern;
}
@Override
public void afterPropertiesSet() throws LiquibaseException {
try {
changeLogLocations = new ArrayList<String>();
// retrieve all changelog resources for the pattern
List<Resource> changeLogResources = Arrays.asList(context.getResources(changeLogLocationPattern));
for (Resource changeLogResource : changeLogResources) {
// get only the classpath path of the resource
String changeLogLocation = changeLogResource.getURL().getPath();
changeLogLocation = "classpath:" + StringUtils.substringAfterLast(changeLogLocation, "!");
changeLogLocations.add(changeLogLocation);
}
// sort all found resources by string
Collections.sort(changeLogLocations, String.CASE_INSENSITIVE_ORDER);
} catch (IOException e) {
throw new LiquibaseException("Could not resolve changeLogLocationPattern", e);
}
super.afterPropertiesSet();
}
@Override
protected SpringResourceOpener createResourceOpener() {
final String mainChangeLog = getChangeLog();
return new SpringResourceOpener(getChangeLog()) {
@Override
public InputStream getResourceAsStream(String file)
throws IOException {
// check if main changelog file
if(mainChangeLog.equals(file)) {
// load master template and convert to dom object
Resource masterResource = getResourceLoader().getResource(file);
Document masterDocument = DomUtils.parse(masterResource, true);
// add all changelog locations as include elements
for (String changeLogLocation : changeLogLocations) {
Element inlcudeElement = masterDocument.createElement("include");
inlcudeElement.setAttribute("file", changeLogLocation);
masterDocument.getDocumentElement().appendChild(inlcudeElement);
}
if(logger.isDebugEnabled()) {
logger.debug("Master changeset: {}", DomUtils.toString(masterDocument));
}
// convert dom back to string and give it back as input resource
return new ByteArrayInputStream(DomUtils.toBytes(masterDocument));
} else {
return super.getResourceAsStream(file);
}
}
};
}
}
现在需要在spring xml配置中使用此类。
<bean id="liquibase" class="liquibase.integration.spring.MySpringLiquibase"
p:changeLog="classpath:/plugin/liquibase/master.xml"
p:dataSource-ref="dataSource"
p:contexts="${liquibase.contexts:prod}"
p:ignoreClasspathPrefix="true"
p:changeLogLocationPattern="classpath*:/plugin/liquibase/changes/*.xml"/>
通过此更改,我实现了主要变更日志文件按其名称排序。
希望对别人也有帮助。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.