实体数据导入与导出

加载实体 XML 和 CSV

实体记录可以用 EntityDataLoader 从XML和CSV文件中导入。可以通过 EntityFacade API 用 ec.entity.makeDataLoader() 方法来获取一个实现了此接口的对象,并使用它的方法来实现:

  • 指定加载什么数据并加载(load()方法)
  • 获取一个记录的 EntityList(list()方法)
  • 校验数据库的数据(check()方法)

指定加载什么数据有一些选项。你能用location(String location)和locationList(List locationList)方法指定一个或多个位置。能用xmlText(String xmlText)和csvText(String csvText)方法直接使用文本。也能使用dataTypes(Set dataTypes)方法通过指定加载的数据类型直接从组件data目录和Moqui配置XML文件中的 entity-facade.load-data元素加载(只有类型匹配的文件将被加载)。

要设置与默认不同的事务超时时间,一般在处理大文件时需要更长的时间,使用transactionTimeout(int tt)方法。 如果知道绝大部分是插入,你可以传true到useTryInsert(boolean useTryInsert)方法来不查询是否记录已存在就执行插入、在插入失败是再尝试更新来提高性能。

为了帮助处理外键,当记录没有按顺序,而你知道后面将会载入外键依赖的数据时,传 true 到 dummyFKs(boolean dummyFks)方法,在没有存在的外键记录时,它将创建空记录。当此外键对应的真是记录加载时,它将简单地更新那条空的虚设记录。要在数据加载时禁止实体ECA规则,传true到disableEntityEca(boolean disableEeca)方法。

对于CSV文件,你可以指定在解析文件时使用哪些字符:csvDelimiter(char delimiter)(默认为都好',')、csvCommentStart(char commentStart)(默认为'#')和csvQuoteChar(char quoteChar)(默认为'"')。

注意EntityDataLoader上的所有这些方法都返回一个对自身的引用,这样你能把调用链起来。它是一个DSL风格的API。例如:

ec.entity.makeDataLoader().dataTypes([‘seed’, ‘demo’]).load()

除了直接使用API以外,你可以使用Moqui默认runtime中自带的tool组件的 Tool => Entity => Import 页面来加载数据。也可以用可执行的WAR文件加-load参数在命令行运行来加载数据。这里有数据加载器相关的命令行参数:

参数
load 运行数据加载器
types=<type>[,<type>] 要加载的数据类型(可以是任意,一般是: seed, seed-initial, demo, ...)
location=<location> 要加载的数据文件的位置
timeout=<seconds> 处理每个文件的事务超时时间,默认为600秒(10分钟)
dummy-fks 使用虚构的外键key来避免引用完整错误
use-try-insert 尝试插入并在有错时更新,而不是先检查
tenantId=<tenantId> 加载数据的目标租户的ID

例如:

$ java -jar moqui.war load types=seed,demo

实体数据XML文件的根元素必须是 entity-facade-xml,它有一个 type 属性来指定文件中数据的类型,(如果指定类型加载)和特定类型比较,只在类型在集合中或者所有类型都加载的情况下加载。在根元素下,每个元素的名字是一个实体或服务名。对于实体,每个属性是一个字段名,对于服务每个属性是一个输入参数。

这里有一个实体数据XML文件的例子:

<entity-facade-xml>
    <moqui.basic.LocalizedMessage original="Example" locale="es" localized="Ejemplo"/>
    <moqui.basic.LocalizedMessage original="Example" locale="zh" localized="样例"/>
</entiy-facade-xml>

这有个调用服务的CSV文件例子(同样的模式也应用于加载实体数据)

# first line is ${entityName or serviceName},${dataType}
org.moqui.example.ExampleServices.create#Example, demo

# second line is list of field names
exampleTypeEnumId, statusId, exampleName, exampleSize, exampleDate

# each additional line has values for those fields
EXT_MADE_UP, EXST_IN_DESIGN, Test Example Name 3, 13, 2014-03-03 15:00:00

写实体 XML

导出实体数据到XML文件的最简单的方法是用 EntityDataWriter,你可以用 ec.entity.makeDataWriter() 获得。通过这个接口你可以:

  • 指定要都指出的实体的名字和多种其他选项,然后它执行查询并导出:
    • 到文件(用int file(String filename) 方法)
    • 一个目录下每个实体一个文件(用int directory(String path)方法)
    • 一个Writer对象(用 int writer(Writer writer)方法)。
    • 所有这些方法返回一个整数,表示写入的记录数。

这些指定导出参数的方法都返回一个对自身的引用,以启用链式调用。下面是设置查询和导出参数的方法:

  • entityName(String entityName): 指定要查询和导出的一个实体的名字。导出多个实体时,调用此方法或entityNames方法多次,将按调用的顺序进行对数据的查询和导出。
  • entityNames(List entityNames):要查询和导出的实体名称列表。按调用此方法或时列表中的顺序或多次调用entityName方法的顺序进行对数据的查询和导出。
  • dependentRecords(boolean dependents):如果为true,将导出每条记录的依赖记录。这显著降低了导出速度,所以只在较小的数据集上用它。关于会包含什么的细节请查看 依赖实体 的章节。
  • filterMap(Map filterMap):用以过滤结果的一个字段名、值的映射表。每个名字/值只被用在有字段匹配此名字的实体上。
  • orderBy(List orderByList): 用于排序结果的字段名列表。每个名字只被用在有字段匹配此名字的实体上。可以被调用多次。每个条目可以是都好分隔的字段名列表。
  • fromDate(Timestamp fromDate), thruDate(Timestamp thruDate):用以过滤记录的起始、结束日期,和EntityFacade自动加到每个实体(如果在实体定义中没有关闭)的 lastUpdatedStamp 字段比较。

这有个导出所有在一个时间范围内的所有OrderHeader记录及其依赖的例子:

ec.entity.makeDataWriter()
    .entityName("mantle.order.OrderHeader")
    .dependentRecords(true)
    .orderBy(["orderId"])
    .fromDate(lastExportDate).thruDate(ec.user.nowTimestamp)
    .file("/tmp/TestOrderExport.xml")

另一个导出实体记录的方法是执行一个查询并获取一个EntityList或EntityListIterator对象,并调用它的int writeXmlText(Writer writer, String prefix, boolean dependents)方法。此方法将XML写到writer,可选地在每个元素前加上前缀以及包含依赖。

与实体导入界面类似,你可以使用默认的Moqui运行时中自带的tools组件中的 Tool => Entity => Export 页面来导出数据。

简化查看和导出的页面和表单

这么多工具一起使得能够非常容易地查看、导出来自许多不同表的数据库数据。我们看了各种选项包括静态(XML)、动态和数据库定义的实体。在 用户界面 章节有关于XML表单的详情,特别是列表表单。

当 form-list 有 dynamic=true 并且在 auto-fields-entity.entity-name 属性中有 ${} 字符串扩展时,那么在页面被渲染时将被即时扩展,意味着提供实体名作为页面参数时,一个单一表单能被用来为任意实体生成表格HTML或CSV输出。

更有趣的是,被查看的结果可以被过滤,一般使用一个有 auto-fields-entity 的 dynamic form-single 来基于实体生成一个搜索表单,一个有 search-form-inputs 的 entity-find 元素来基于实体名参数和搜索表单的搜索参数来执行查询。

下面是这些特性连同页面转换(transition DbView.csv)来导出一个CSV文件的例子。别担心太多关于页面、转换、表单和渲染选项的细节,它们将会在用户界面章节中详细介绍。界面定义来自Moqui框架自带的tools组件中的 ViewDbVie.xml 页面。

<screen>
        <parameter name="dbViewEntityName" required="true"/>
        <transition name="filter">
             <default-response url="."/>
        </transition>
        <transition name="DbView.csv">
        <default-response url="."><parameter name="renderMode" value="csv"/>
            <parameter name="pageNoLimit" value="true"/><parameter name="lastStandalone" value="true"/></default-response>
        </transition>
        <actions>
            <entity-find entity-name="${dbViewEntityName}" list="dbViewList">
                <search-form-inputs/>
            </entity-find>
        </actions>
       <widgets>
              <container>
                  <link url="edit" text="Edit ${dbViewEntityName}"/>
                  <link url="DbView.csv" text="Get as CSV"/>
              </container>
              <label text="Data View for: ${dbViewEntityName}" type="h2"/>
              <container-dialog id="FilterViewDialog" button-text="Filter ${ec.entity.getEntityDefinition(dbViewEntityName).getPrettyName(null, null)}">
                  <form-single name="FilterDbView" transition="filter" dynamic="true">
                      <auto-fields-entity entity-name="${dbViewEntityName}" field-type="find"/>
                      <field name="dbViewEntityName"><default-field><hidden/></default-field></field>
                      <field name="submitButton"><default-field title="Find"><submit/></default-field></field>
                  </form-single>
              </container-dialog>
              <form-list name="ViewList" list="dbViewList" dynamic="true">
                  <auto-fields-entity entity-name="${dbViewEntityName}" field-type="display"/>
              </form-list>
    </widgets>
</screen>

这个页面被设计为供用户使用的同时,它也能在web或其他UI上下文外被渲染来发送到一个文件或其他位置。如果你只是为那写一个页面,将会简单得多,基本上只有parameter元素,单个entity-find动作和简单的form-list定义。不需要转换和搜索表单。

使用页面渲染器的代码会像是这样:

ec.context.putAll([pageNoLimit:"true", lastStandalone:"true", dbViewEntityName: "moqui.example.ExampleStatusDetail"])
String csvOutput = ec.screen.makeRender().rootScreen("component://tools/screen/Tools/DataView/ViewDbView.xml").renderMode("csv").render()

results matching ""

    No results matching ""