# Data Import
# Target Audience
The target audience for this document may include:
- Developers and implementers of this system
- User data administrators preparing to import data
# Feature Description
This system follows the principle that everything is data. All types of data support importing through CSV files, with operations such as creation, update, and deletion supported during import.
TIP
The CSV parsing format of this system follows the RFC 4180 (opens new window) standard.
# Data Import Methods
Currently supported import methods include:
# Auto import upon system startup
The system's data supports automatic import of a set of CSV files from a configured server directory when the system starts, refer to Configure Import File Path Development for details
# Import from domain list page
- For each domain model, manual import of CSV files is supported through the system interface.
# Import using "Reload Seed Data"
- On the domain model list page, use the "Reload Seed Data" action to reload seed data from configured seed data folder
# Import using "Import Seed Data Package"
- On the domain model list page, use the "Import Seed Data Package" action to upload the packaged zip file containing .CSV and other referenced files.
The platform development environment comes with a gradle task called packageSeedData
to generate such a zip file
After import, you can view the import process records on the system interface.
TIP
Seed Data generally refers to a set of initial data inserted into the database during application initialization or deployment. This data is used to provide the basic data structure and reference data required by the application to ensure normal operation.
Specifically, the design principle of the Muyan low-code platform is that everything, including customized code, is data, so the entire business system is built entirely from seed data.
The range of supported import objects includes:
- All object definitions in the system, such as users, user groups, roles, and other permission-related data, as well as organizations, contracts, contract lines, contract line definitions, etc. This also includes domain model definitions, domain model field definitions, form definitions, dynamic fields, dynamic field definitions, and all other metadata.
If you prefer to learn through an example, you can directly check A Complete Example.
# Feature Activation Development
This feature is enabled by default and does not require configuration to activate. However, the normal operation of this feature depends on the configuration of the import file path, as described below.
# Configure Import File Path Development
You need to configure the seedData.folder
option in the application.yml
file. This option specifies the root path for reading CSV files.
Below is an example configuration for the development
environment:
development:
seedData:
folder: ${SEED_DATA_FOLDER:/app/data}
2
3
TIP
This configuration will prioritize reading from the environment variable SEED_DATA_FOLDER
The directory structure is briefly described as follows. The first-level subdirectories of this directory correspond to different system environments. For example, all CSV files to be imported in the development
environment should be placed in the development
subdirectory. The currently predefined environments are listed as follows:
In the directories of each environment, seed files for different tenants are stored in subdirectories for different tenants.
- development
- testing
- staging
- production
In each environment's data directory, there will be an Organization.csv file that stores the list of top-level tenants to be imported first into the system. This file will import the top-level organization and tenant identifier for each tenant, serving as the basis for importing all tenant-specific data.
âââ README.md
âââ attachments --> Generally, attachments that need to be imported are placed in this directory, such as logos used by DynamicTheme
â âââ README.md
â âââ background.jpeg
â âââ favicon.png
â âââ logo.png
âââ css --> Generally, customized css files for DynamicTheme are placed in this directory
â âââ README.md
âââ development --> (development environment)
âââ groovy --> Generally, customized DynamicLogic source code is placed in this directory
âââ review --> (review environment)
â âââ Organization.csv --> (List of all top-level tenants in the system to be imported first)
â âââ README.md
â âââ jiayu --> Corresponds to the tenant name of the top-level tenant
â â âââ DomainColumnClientSideTypeConfig.csv
â â âââ DynamicAction.csv
â â âââ DynamicActionDomainClass.csv
â â âââ DynamicActionDomainClass_jiayu.csv
â â âââ UserGroup.csv
â âââ muyan --> Corresponds to the tenant name of the top-level tenant
â âââ Group.csv
â âââ GroupRole.csv
â âââ Organization.csv
â âââ RequestMap.csv
â âââ Role.csv
â âââ User.csv
â âââ UserGroup.csv
âââ tree.txt
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# File Update Determination
Before importing, for each CSV file, the system will compare the md5 code of the CSV file to be imported with the md5 code of the previously imported CSV file. If the md5 codes of the two files are the same and the status of the previous import was successful, the system will skip this import.
# Data Preparation and Import
The following sections describe the content related to CSV file data preparation.
# File Names
All CSV files should start with the class name of the object, without the package definition. For example, CSV import files for tech.muyan.Contract
and tech.muyan.security.GroupRole
objects can be named Contract.csv
and GroupRole.csv
, or Contract_2019.csv
, GroupRole_security.csv
, etc. The .csv
suffix should be in lowercase.
# Import Order and Dependencies
For a set of data with associated relationships, the system will automatically identify the dependencies between them based on the association relationships in the model definition, and automatically import the dependent data before the data that depends on it during import.
If special dependency relationships need to be handled, please define them using loadAfter
in the extInfo
of the domain definition.
# Dynamic Domain Models
- For detailed information on specifying the Seed Data import order in dynamic domain model definitions, refer to the Domain Model Metadata section.
# Legacy Domain
- Here's an example of defining the
loadAfter
property in Legacy Domain definitions:
The following is an example from the DynamicIntegration
Domain definition:
// Indicates that Organization, DynamicLogic, and User objects need to be imported before importing DynamicIntegration
static loadAfter = [Organization, DynamicLogic, User]
2
If a certain type of object should be imported first, to prevent the system from parsing its dependencies from its Domain definition, the loadAfter
property can be set to an empty array. Here's an example from the DynamicLogic
Domain definition:
static loadAfter = []
# Header Row
The first row of all CSV files to be imported should be the header row. The header row describes the structure of the data in the CSV file.
Below is an example of a header row for a CSV file importing a Contract
object and its explanation:
name(*),title,effectiveDate,ownerOrganization.name,Tags(#),Purpose(#),Total Amount(#),Seal Type(#),Contract Quantity(#),language,DELETE_FLAG
- For basic fields defined in the model, the column name is the field name in the model definition.
- For dynamic fields customized in the interface, the column name is the value of the Label field displayed on the object's interface.
- Columns with the suffix
(*)
, such asname(*)
, are query fields, as described in the Query Fields section below. - Columns containing
.
, such asownerOrganization.name
, are associated object fields, as described in the Associated Object Queries section below. - Columns with the suffix
(#)
, such asPurpose(#)
, are dynamic fields, as described in the Dynamic Fields section below. - The column named
DELETE_FLAG
is a special column. Its value can beY
orN
. When it'sY
, it indicates that the system needs to delete the object found through the query fields during import. - Columns with the suffix
(F)
, such as code(F), indicate that the content of this column will be read from a file. The starting directory for the file is the directory configured byseedData.folder
. - If the field type defined in the Domain for a certain column is StorageFieldValue, then the value in that column should be the file path of the attachment to be imported.
WARNING
Internally in the system, this field name is saved in the label
field of the DynamicFieldInstance
object.
Be careful to distinguish this from the label field of the DynamicFieldDefinition
field.
# Comment Lines
Lines starting with a semicolon (;
) are comment lines and will be ignored during import. Below is a sample import CSV file containing comments.
All lines starting with ;
are comment lines and will not affect the import process.
name(*)
; Normal user, only have permission to login
USER
; Admin user
ADMIN
; Developer user
DEVELOPER
; Biz user, can operate on biz objects(contract)
BIZ_USER
; Biz Admin
BIZ_ADMIN
2
3
4
5
6
7
8
9
10
11
# Query Fields
To support creating or updating existing records through CSV files, the system supports querying existing records through query fields. All columns in the header of the import file with the suffix (*)
will be identified by the system as query fields. The system will use strict equality matching to query existing records from the system based on the values of all query fields, and perform the following corresponding processing:
- If the query result is empty, the system will attempt to create a new record and save it, and record the operation for this row of data in the import record as creation.
- If the query result is not empty and only one record exists, the system will attempt to update that record, and record the operation for this row of data in the import record as update.
- If the query result is not empty and more than one record exists, the system will skip the import of this row of data, and record the operation for this row of data in the import record as skipped.
TIP
If there are no query fields in the CSV file being imported, it means that the update and delete functions are not available, and all records will be processed as new creations during import.
# Associated Object Queries
The system supports querying associated objects through associated object fields and associating the queried associated objects with the objects to be imported.
For example, the field ownerOrganization.name
mentioned above indicates that when importing the ownerOrganzation
field of this object (this field is an Organization
object), for each row of data being imported, it uses the name
attribute of the Organization
type to query existing Organization
object records based on the value in that column of the CSV file, and associates the queried records with the Contract
object to be imported. Depending on the query results, the specific handling may be as follows:
- If the queried associated object does not exist, the import of that row is terminated, and the operation for this row of data in the import record is marked as failed.
- If there is one and only one queried associated object, the import of that row continues, with the final result depending on the operations of other columns.
- If there are multiple queried associated objects, the import of that row is terminated, and the operation for this row of data in the import record is marked as skipped.
TIP
Associated fields can also be used as query fields
WARNING
When importing an object, whether creating or updating, its dependent associated objects must already exist in the system, otherwise the import of that object will fail
# Dynamic Fields
When importing data, the system also supports importing values for dynamic fields created on the interface. All dynamic fields in the header row of the import CSV file should end with (#)
. The saving of dynamic field values occurs after the main object has been saved. For example, in the CSV file mentioned above, for the column titled Purpose(#)
, during import, the system will query the contract dynamic field with the Label "Purpose" and perform data import.
If the field is a selection field, then during import, the system will check whether the value in that column of the CSV file is within the range of options for that field. If its value is not within the optional range, it will terminate the import of that row of data and mark the import of that row as failed.
System Limitation
Because the saving of dynamic fields and the saving of the main object are not in the same transaction, it's possible for the main object to be saved successfully but the dynamic fields to fail to save. In this case, the state of the main object and the CSV file will become out of sync.
# Escaping Special Characters
- If the content of a column contains an English comma (
,
), that column needs to be wrapped in English quotation marks ("
). - If the content of a column contains English quotation marks, and that column is wrapped in quotation marks, then it needs to be escaped with
\
.
# Whitespace Handling
When reading data from CSV files, the system will automatically remove whitespace characters before and after each column.
WARNING
If there are whitespace characters before or after columns in the CSV file, that row will be identified as needing update each time it's imported, and after the update, the value saved in the database will not include whitespace characters before or after.
# Object Deletion
CSV file import supports deleting existing data, implemented in the following way:
- Add a
DELETE_FLAG
column in the header row of the CSV file - For rows to be deleted, set the value of the
DELETE_FLAG
column toY
in the CSV file
WARNING
The delete operation can only work correctly if query fields that can uniquely identify a record are specified in the header row of the CSV file.
For rows marked for deletion, here is the related error handling logic:
- If there are 0 existing data records found by the query, the system will skip the deletion processing for this row.
- If there are multiple existing data records found by the query, the system will skip the deletion processing for this row.
- If there are no query fields in the imported CSV file, the system will skip the processing of all rows marked for deletion.
# Data Type Mapping
The following lists the types currently supported for import by the system and their data types after import
Data Type | Data Type After Import |
---|---|
Boolean | java.lang.Boolean |
String | java.lang.String |
datetime | java.time.LocalDateTime |
date | java.time.LocalDate |
decimal | java.math.BigDecimal |
integer | java.lang.Integer |
long | java.lang.Long |
Enum | java Enum |
httpMethod | org.springframework.http.HttpMethod |
java class name | Domain Object |
StorageFieldValue | tech.muyan.storage.StorageFieldValue |
# Boolean
For Boolean type fields, the following lists the value correspondences. If the field value in the CSV file is not within the following range, the system will mark the import of that row as failed
Value in CSV file | Value after import |
---|---|
Y , y , Yes , YES , true , TRUE , T , t , æ¯ , 1 | true |
N , n , No , NO , false , FALSE , F , f , åĻ , 0 | false |
# Date and Time
For date type fields, the following is a list of supported date/time formats. The letters in the list represent the relevant parts of time and date. You can refer to SimpleDateFormat (opens new window) for more details.
"yyyyMMdd"
"dd-MM-yyyy"
"yyyy-MM-dd"
"MM/dd/yyyy"
"yyyy/MM/dd"
"dd MMM yyyy"
"dd MMMM yyyy"
"yyyyMMddHHmm"
"yyyyMMdd HHmm"
"dd-MM-yyyy HH:mm"
"yyyy-MM-dd HH:mm"
"MM/dd/yyyy HH:mm"
"yyyy/MM/dd HH:mm"
"dd MMM yyyy HH:mm"
"dd MMMM yyyy HH:mm"
"yyyyMMddHHmmss"
"yyyyMMdd HHmmss"
"dd-MM-yyyy HH:mm:ss"
"yyyy-MM-dd HH:mm:ss"
"MM/dd/yyyy HH:mm:ss"
"yyyy/MM/dd HH:mm:ss"
"dd MMM yyyy HH:mm:ss"
"dd MMMM yyyy HH:mm:ss"
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# Enumeration Types
If the enumeration type defines a get method that can query the enum object from its display value, the system supports importing enum data columns in CSV files where the column value is its interface display value. Otherwise, the system can only support importing enum data where the value in the CSV exactly matches its internal value.
# Attachment Types
For attachment type columns, when preparing the CSV file, the corresponding column should be filled with the relative file path calculated from the configured seedData.folder
directory. During import, the system will import this column as a StorageFieldValue
object in the system and establish the necessary object associations.
# Root Directory Fields
If a field is of type tech.muyan.Organization
, you can use the placeholder $ROOT_ORG$ to specify a reference to the current tenant's root organization.
TIP
This special handling is to enable quick copying of system default related seed files between different tenants without needing to replace organization names for each tenant.
# Support for New Data Type Mappings Development
All data type mappings during import are defined in tech.muyan.importexport.converter.ConverterFactory
. This class provides a utility method registerConverter
for registering data mapping implementations for CSV import in the system. For example:
ConverterFactory.registerConverter(type -> type != null && type.contains(".enums."), new EnumConverter());
- The above method registers a data mapping implementation named EnumConverter.
- The condition for enabling this implementation is specified using a predictor that receives a character type parameter. This parameter is the type field in the metadata returned by calling the
DomainMetaController.get
method on the domain model class to be imported. - The predictor in the above example limits the condition for this type mapping to: the type attribute of the field is not empty and contains the part
.enums.
.
# View Import Records
The system's input import records are saved in ImportRecord
, which can be viewed by users with GET
permission for the /ImportRecord
address. By default, users with ROLE_ADMIN
and ROLE_DEVELOPER
roles have permission to view import records.
The following information is saved in the import record:
- Type of imported object
- MD5 code of the imported CSV file
- Import status: can be
- Not started
- Successful
- Failed
- Partially successful
- In progress
- Import start time
- Import end time
- Title row of the imported CSV file
- List of inserted object ids
- List of updated object ids
- List of deleted object ids
- Original information of failed rows in CSV
- Original information of skipped rows in CSV
- Import log records
- Number of skipped rows
- Number of failed rows
- Number of successfully updated rows
- Number of successfully created rows
- Number of successfully deleted rows
# A Complete Example
Below is an example of a complete CSV file that can be imported into the system, and based on this, the related settings and concepts mentioned above are explained.
CSV file name: Contract.csv
CSV file content example:
name(*),title,effectiveDate,total,ownerOrganization.name(*),contractStatus,Tags(#),Purpose(#),Total Amount(#),Seal Type(#),Contract Quantity(#),Is Important(#),language,lines.name[:],DELETE_FLAG
MuYan Technology 2020 Laptop Purchase Contract,Laptop Purchase Contract,2020-01-01 00:00:00,45000,MuYan Technology,Active,"[\"One-time Contract\",\"Low Risk\"]","Purchase laptops for development use",35000,Official Seal,3,Yes,contracts\language_2020.docx,"[line1:line2]",N
MuYan Technology 2019 Laptop Purchase Contract,Laptop Purchase Contract,2020-01-01 00:00:00,45000,MuYan Technology,Active,"[\"One-time Contract\",\"Low Risk\"]","Purchase laptops for development use",35000,Official Seal,3,Yes,contracts\language_2019.docx,"[line3:line4]",Y
2
3
# File Description
Mapping from file name to object: The file name is
Contract.csv
, so the system will query all domain models defined inDomainClass
, find the model definition namedContract
without a package, and decide that the import object corresponding to this file isContract
.Existing record lookup: The system will combine the fields ending with
(*)
in the title row:name
andownerOrganization.name
, use equal matching to query existing records in the system. If data is found based on these two pieces of information, the existing record will be updated; otherwise, a new record will be created.Associated object query and dependency ordering: From the title row, we can see that the
ownerOrganization.name
field contains the.
character, indicating that this field is an associated object field.- This field is a
tech.muyan.Organization
object in the system, so if the system simultaneously detects the existence of anOrganization.csv
file, it will automatically sort and import theOrganization.csv
file before theContract.csv
file. - From
ownerOrganization.name
, it can be determined that when importing, the system will use itsname
field to match and query when looking up theOrganization
object associated withContract
, and associate the foundOrganization
object with theContract
object to be imported.
- This field is a
Dynamic fields: From the title row, we can see that the fields
Tags
,Purpose
,Total Amount
,Seal Type
,Contract Quantity
, andIs Important
end with(#)
, so they are all dynamic fields. The system will look for dynamic fields of theContract
object defined in the system with labels matching these values, and update the corresponding dynamic field values accordingly.
# Field Descriptions
effectiveDate
- This field is a date type, and its value
2020-01-01 00:00:00
conforms to the format definition ofyyyy-MM-dd HH:mm:ss
, so the system can parse and import this data normally.
- This field is a date type, and its value
contractStatus
- This field is an enumeration type data, its value
Active
is the display value of the enumeration typeACTIVE
. This requires the system to implement aget
method to reverse lookup its defined valueACTIVE
from its display valueActive
. If there is no method implemented to reverse lookup the enum value from the display value, then in the CSV file,ACTIVE
must be filled in for the system to recognize and import the data normally.
- This field is an enumeration type data, its value
Tags
- Because the value of this field contains the
,
character, it needs to be wrapped in English quotation marks"
. - Because the value of this field contains English quotation marks
"
, this character needs to be escaped using the escape character\
. - In the system, the
Tags
field is defined as a multiple-choice field. From the value in the second row, we can see that the value of theTags
field is a valid JSON string. This contract's JSON string will be saved to thejsonValue
column of the dynamic field. - During import, the system will verify that all options in the JSON string of this field (in this case:
One-time Contract
,Low Risk
) are predefined, valid options in the system.
- Because the value of this field contains the
Is Important
- This field is a Boolean type field. According to the Boolean type conversion list, its value
Yes
will be converted totrue
during import.
- This field is a Boolean type field. According to the Boolean type conversion list, its value
language
- This field is a
StorageFieldValue
field, so during import, the system will import the files corresponding to the file path
contracts\language_2019.docx
in this column asStorageFieldValue
object, which is the object represent an attachment in our platform, aStorageFieldValue
object will be created, and the association relationship between this object and the mainContract
object will be established.
- This field is a
lines.name[:]
- This indicates that this field is a list field, used to import
lines
field objects. The system will look up the correspondingContractLine
objects based on the value of thename
attribute of each item in this field, and establish the association relationship between them and the mainContract
object. - The list separator used here is
:
. Iflines.name[]
is used directly as the column header, it means using the default separator comma (,). - It should be noted that before importing, the objects associated with the
lines
field must already exist, otherwise it will cause the import to fail.
- This indicates that this field is a list field, used to import
DELETE_FLAG
- This field indicates whether to perform a delete operation for a certain row of data. According to the value of this field in each row of the above example file, the data in the first row performs an update or create operation, and the data in the second row performs a delete operation.
TIP
If the string to be imported contains English quotation marks, use another English quotation mark or backslash for escaping. For example, "" or \" represents an English quotation mark.