将includeAll与classpath *一起使用时,更改集文件的Liquibase执行顺序:

时间:2014-04-07 08:41:46

标签: java spring liquibase

我在弹簧环境(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 对返回的枚举进行了重新排序。所以我的改变没有任何效果。我不认为我也可以入侵这门课程。

2 个答案:

答案 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文件。

它仅适用于主更改日志文件。以下先决条件:

  • 该模式只能用于主更改日志文件
  • 我使用空的主更新日志文件作为模板
  • 所有其他更改日志文件必须使用正常的允许加载机制
  • 仅适用于Spring环境

首先,我必须用我的实现扩展/覆盖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"/>

通过这些更改,我实现了我的主要更改日志文件按名称排序。

希望能帮助别人。

相关问题