我在弹簧环境(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"/>...
我使用的命名策略如&#34; nnn_changesetname.xml&#34;继续订购。但是当我查看变更集表时,不会保留通过文件名排序的顺序。如果变更集文件包含在目录中而不包含在类路径中,这只能起作用吗?
更新
嗨,我发现以下建议的解决方案还不够。我认为这取决于liquibase如何解析includAll属性。在我的情况下,它首先解决所有&#34;文件夹&#34;然后查看每个文件夹中的变更集xmls。这将打破所有classpath *:/ changes位置中xml文件的排序,因为现在有几个&#34;更改&#34;不同位置的文件夹。在这种情况下我怀疑的是这个&#34;虚拟&#34;的所有内容的合并。 classpath文件夹和一个枚举中的所有资源的加载。或者我们可以在inlcudeAll标签中允许一些资源模式,例如resources =&#34; classpath *:/ changes / * .xml&#34;直接选择所有需要的文件(尝试使用path属性,但不起作用,因为它检查文件夹)?
更新
我做了一个hack来检查返回的枚举中的顺序是否使用下面的anwser保留。为了实现这一点,我检查了给定的包名称,如果它与我的模式匹配,我添加了一个额外的&#34; * .xml&#34;它。通过此扩展,我可以根据需要获得所有变更集。
@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 对返回的枚举进行了重新排序。所以我的改变没有任何效果。我不认为我也可以入侵这门课程。
答案 0 :(得分:3)
你是对的,Liquibase依赖于底层的“列表文件”逻辑,它通过文件系统按字母顺序排序文件,但显然不是通过类路径。
我创建了https://liquibase.jira.com/browse/CORE-1843来跟踪修复。
现在,如果使用liquibase.integration.spring.SpringLiquibase的子类配置spring,它将使用一个方法覆盖getResources(String packageName),该方法对返回的Enumeration进行排序,以便为您解决问题。
答案 1 :(得分:0)
因此经过一些思考和一夜的睡眠后,我想出了以下hack来保证通过classpath模式classpath *:/ my / path / to / changelog / * .xml来加载更改日志文件的顺序。想法是在liquibase请求时通过dom操作动态创建主要的changelog文件。
它仅适用于主更改日志文件。以下先决条件:
首先,我必须用我的实现扩展/覆盖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"/>
通过这些更改,我实现了我的主要更改日志文件按名称排序。
希望能帮助别人。