繁体   English   中英

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

[英]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操作即时创建主变更日志文件。

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

  • 该模式只能用于主变更日志文件
  • 我使用一个空的主变更日志文件作为模板
  • 所有其他变更日志文件必须使用正常的允许加载机制
  • 仅在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"/>

通过此更改,我实现了主要变更日志文件按其名称排序。

希望对别人也有帮助。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM