mirror of
https://gitee.com/jmix/cuba.git
synced 2024-12-05 04:38:10 +08:00
PL-8650 Rework EntityImportView API
This commit is contained in:
parent
839f1cdadc
commit
82174d8c9c
@ -21,6 +21,7 @@ import com.google.common.collect.ArrayListMultimap;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.haulmont.chile.core.model.MetaClass;
|
||||
import com.haulmont.chile.core.model.MetaProperty;
|
||||
import com.haulmont.chile.core.model.Range;
|
||||
import com.haulmont.cuba.core.*;
|
||||
import com.haulmont.cuba.core.app.dynamicattributes.DynamicAttributesManagerAPI;
|
||||
import com.haulmont.cuba.core.app.serialization.EntitySerializationAPI;
|
||||
@ -161,7 +162,7 @@ public class EntityImportExport implements EntityImportExportAPI {
|
||||
}
|
||||
|
||||
for (String storeName : entitiesByStore.keySet()) {
|
||||
Map<Object, Entity> entitiesToCreate = new HashMap<>();
|
||||
Map<Object, Entity> entitiesToPersist = new HashMap<>();
|
||||
Set<Entity> entitiesToRemove = new HashSet<>();
|
||||
List<ReferenceInfo> referenceInfoList = new ArrayList<>();
|
||||
Map<Object, Entity> loadedEntities = new HashMap<>();
|
||||
@ -169,20 +170,21 @@ public class EntityImportExport implements EntityImportExportAPI {
|
||||
try (Transaction tx = persistence.getTransaction(storeName)) {
|
||||
|
||||
//import is performed in two steps. We have to do so, because imported entity may have a reference to
|
||||
//some next imported entity.
|
||||
//1. entities that should be created processed first, fields that should be references to existing entities
|
||||
//the reference that is imported in the same batch.
|
||||
//
|
||||
//1. entities that should be created are processing first, fields that should be references to existing entities
|
||||
//are stored in the referenceInfoList variable
|
||||
for (Entity entity : entities) {
|
||||
importEntity(entity, view, storeName, entitiesToCreate, entitiesToRemove, referenceInfoList);
|
||||
importEntity(entity, view, storeName, entitiesToPersist, entitiesToRemove, referenceInfoList);
|
||||
}
|
||||
|
||||
//2. references to existing entities are processed
|
||||
for (ReferenceInfo referenceInfo : referenceInfoList) {
|
||||
processReferenceInfo(referenceInfo, storeName, entitiesToCreate, loadedEntities);
|
||||
processReferenceInfo(referenceInfo, storeName, entitiesToPersist, loadedEntities);
|
||||
}
|
||||
|
||||
EntityManager em = persistence.getEntityManager(storeName);
|
||||
for (Entity entity : entitiesToCreate.values()) {
|
||||
for (Entity entity : entitiesToPersist.values()) {
|
||||
if (PersistenceHelper.isNew(entity)) {
|
||||
em.persist(entity);
|
||||
result.add(entity);
|
||||
@ -239,10 +241,17 @@ public class EntityImportExport implements EntityImportExportAPI {
|
||||
Entity embeddedEntity = importEmbeddedAttribute(srcEntity, dstEntity, viewProperty, storeName, entitiesToCreate, entitiesToRemove, referenceInfoList);
|
||||
dstEntity.setValue(viewProperty.getName(), embeddedEntity);
|
||||
}
|
||||
} else if (metaProperty.getRange().getCardinality().isMany()) {
|
||||
importCollectionAttribute(srcEntity, dstEntity, viewProperty, storeName, entitiesToCreate, entitiesToRemove, referenceInfoList);
|
||||
} else {
|
||||
importReference(srcEntity, dstEntity, viewProperty, storeName, entitiesToCreate, entitiesToRemove, referenceInfoList);
|
||||
switch (metaProperty.getRange().getCardinality()) {
|
||||
case MANY_TO_MANY:
|
||||
importManyToManyCollectionAttribute(srcEntity, dstEntity, viewProperty, storeName, entitiesToCreate, entitiesToRemove, referenceInfoList);
|
||||
break;
|
||||
case ONE_TO_MANY:
|
||||
importOneToManyCollectionAttribute(srcEntity, dstEntity, viewProperty, storeName, entitiesToCreate, entitiesToRemove, referenceInfoList);
|
||||
break;
|
||||
default:
|
||||
importReference(srcEntity, dstEntity, viewProperty, storeName, entitiesToCreate, entitiesToRemove, referenceInfoList);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -258,6 +267,124 @@ public class EntityImportExport implements EntityImportExportAPI {
|
||||
return entity instanceof BaseGenericIdEntity && ((BaseGenericIdEntity) entity).getDynamicAttributes() != null;
|
||||
}
|
||||
|
||||
protected void importReference(Entity srcEntity,
|
||||
Entity dstEntity,
|
||||
EntityImportViewProperty viewProperty,
|
||||
String storeName,
|
||||
Map<Object, Entity> entitiesToCreate,
|
||||
Set<Entity> entitiesToRemove, Collection<ReferenceInfo> referenceInfoList) {
|
||||
Entity srcPropertyValue = srcEntity.<Entity>getValue(viewProperty.getName());
|
||||
if (viewProperty.getView() == null) {
|
||||
ReferenceInfo referenceInfo = new ReferenceInfo(dstEntity, viewProperty, srcPropertyValue);
|
||||
referenceInfoList.add(referenceInfo);
|
||||
} else {
|
||||
Entity dstPropertyValue = importEntity(srcPropertyValue, viewProperty.getView(), storeName, entitiesToCreate, entitiesToRemove, referenceInfoList);
|
||||
dstEntity.setValue(viewProperty.getName(), dstPropertyValue);
|
||||
}
|
||||
}
|
||||
|
||||
protected void importOneToManyCollectionAttribute(Entity srcEntity,
|
||||
Entity dstEntity,
|
||||
EntityImportViewProperty viewProperty,
|
||||
String storeName,
|
||||
Map<Object, Entity> entitiesToCreate,
|
||||
Set<Entity> entitiesToRemove,
|
||||
Collection<ReferenceInfo> referenceInfoList) {
|
||||
MetaProperty metaProperty = srcEntity.getMetaClass().getPropertyNN(viewProperty.getName());
|
||||
MetaProperty inverseMetaProperty = metaProperty.getInverse();
|
||||
|
||||
//filteredItems collection will contain entities filtered by the row-level security
|
||||
Multimap<String, UUID> filteredItems = ArrayListMultimap.create();
|
||||
if (srcEntity instanceof BaseGenericIdEntity) {
|
||||
//create an entity copy here, because filtered items must not be reloaded in the srcEntity for now,
|
||||
//we only need a collection of filtered properties
|
||||
byte[] securityToken = BaseEntityInternalAccess.getSecurityToken((BaseGenericIdEntity) srcEntity);
|
||||
Entity srcEntityCopy = metadata.getTools().deepCopy(srcEntity);
|
||||
BaseEntityInternalAccess.setSecurityToken((BaseGenericIdEntity) srcEntityCopy, securityToken);
|
||||
persistenceSecurity.restoreFilteredData((BaseGenericIdEntity<?>) srcEntityCopy);
|
||||
filteredItems = BaseEntityInternalAccess.getFilteredData((BaseGenericIdEntity) srcEntityCopy);
|
||||
}
|
||||
|
||||
Collection<Entity> srcPropertyValue = srcEntity.getValue(viewProperty.getName());
|
||||
|
||||
Collection<Entity> collection;
|
||||
try {
|
||||
collection = srcPropertyValue.getClass().newInstance();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Error on import entities", e);
|
||||
}
|
||||
|
||||
if (srcPropertyValue != null) {
|
||||
for (Entity srcChildEntity : srcPropertyValue) {
|
||||
if (viewProperty.getView() != null) {
|
||||
//create new referenced entity
|
||||
Entity dstChildEntity = importEntity(srcChildEntity, viewProperty.getView(), storeName, entitiesToCreate, entitiesToRemove, referenceInfoList);
|
||||
if (inverseMetaProperty != null) {
|
||||
dstChildEntity.setValue(inverseMetaProperty.getName(), dstEntity);
|
||||
}
|
||||
collection.add(dstChildEntity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (viewProperty.getCollectionImportPolicy() == CollectionImportPolicy.REMOVE_ABSENT_ITEMS) {
|
||||
Collection<? extends Entity> dstValue = dstEntity.getValue(viewProperty.getName());
|
||||
if (dstValue != null) {
|
||||
Multimap<String, UUID> finalFilteredItems = filteredItems;
|
||||
List<? extends Entity> collectionItemsToRemove = dstValue.stream()
|
||||
.filter(entity -> !collection.contains(entity) &&
|
||||
(finalFilteredItems == null || !finalFilteredItems.containsValue(entity.getId())))
|
||||
.collect(Collectors.toList());
|
||||
entitiesToRemove.addAll(collectionItemsToRemove);
|
||||
}
|
||||
}
|
||||
|
||||
dstEntity.setValue(viewProperty.getName(), collection);
|
||||
}
|
||||
|
||||
protected void importManyToManyCollectionAttribute(Entity srcEntity,
|
||||
Entity dstEntity,
|
||||
EntityImportViewProperty viewProperty,
|
||||
String storeName,
|
||||
Map<Object, Entity> entitiesToCreate,
|
||||
Set<Entity> entitiesToRemove,
|
||||
Collection<ReferenceInfo> referenceInfoList) {
|
||||
Collection<Entity> srcPropertyValue = srcEntity.getValue(viewProperty.getName());
|
||||
if (viewProperty.getView() != null) {
|
||||
//create/update passed entities
|
||||
Collection<Entity> collection;
|
||||
try {
|
||||
collection = srcPropertyValue.getClass().newInstance();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Error on import entities", e);
|
||||
}
|
||||
|
||||
for (Entity srcChildEntity : srcPropertyValue) {
|
||||
//create new referenced entity
|
||||
Entity dstChildEntity = importEntity(srcChildEntity, viewProperty.getView(), storeName, entitiesToCreate, entitiesToRemove, referenceInfoList);
|
||||
collection.add(dstChildEntity);
|
||||
}
|
||||
|
||||
if (viewProperty.getCollectionImportPolicy() == CollectionImportPolicy.KEEP_ABSENT_ITEMS) {
|
||||
Collection<Entity> existingCollectionValue = dstEntity.getValue(viewProperty.getName());
|
||||
if (existingCollectionValue != null) {
|
||||
for (Entity existingCollectionItem : existingCollectionValue) {
|
||||
if (!collection.contains(existingCollectionItem)) collection.add(existingCollectionItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dstEntity.setValue(viewProperty.getName(), collection);
|
||||
} else {
|
||||
//create ReferenceInfo objects - they will be parsed later
|
||||
Collection<Entity> existingCollectionValue = dstEntity.getValue(viewProperty.getName());
|
||||
if (existingCollectionValue != null) {
|
||||
ReferenceInfo referenceInfo = new ReferenceInfo(dstEntity, viewProperty, srcPropertyValue, existingCollectionValue);
|
||||
referenceInfoList.add(referenceInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected Entity importEmbeddedAttribute(Entity srcEntity,
|
||||
Entity dstEntity,
|
||||
EntityImportViewProperty viewProperty,
|
||||
@ -281,115 +408,27 @@ public class EntityImportExport implements EntityImportExportAPI {
|
||||
if ((mp.getRange().isDatatype() && !"version".equals(mp.getName())) || mp.getRange().isEnum()) {
|
||||
dstEmbeddedEntity.setValue(vp.getName(), srcEmbeddedEntity.getValue(vp.getName()));
|
||||
} else if (mp.getRange().isClass()) {
|
||||
if (mp.getRange().getCardinality().isMany()) {
|
||||
importCollectionAttribute(srcEmbeddedEntity, dstEmbeddedEntity, vp, storeName, entitiesToCreate, entitiesToRemove, referenceInfoList);
|
||||
} else {
|
||||
if (metaProperty.getRange().getCardinality() == Range.Cardinality.ONE_TO_MANY) {
|
||||
importOneToManyCollectionAttribute(srcEmbeddedEntity, dstEmbeddedEntity, vp, storeName, entitiesToCreate, entitiesToRemove, referenceInfoList);
|
||||
} else if (metaProperty.getRange().getCardinality() == Range.Cardinality.MANY_TO_MANY) {
|
||||
importManyToManyCollectionAttribute(srcEmbeddedEntity, dstEmbeddedEntity, vp, storeName, entitiesToCreate, entitiesToRemove, referenceInfoList);
|
||||
}
|
||||
else {
|
||||
importReference(srcEmbeddedEntity, dstEmbeddedEntity, vp, storeName, entitiesToCreate, entitiesToRemove, referenceInfoList);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return dstEmbeddedEntity;
|
||||
|
||||
}
|
||||
|
||||
protected void importReference(Entity srcEntity,
|
||||
Entity dstEntity,
|
||||
EntityImportViewProperty viewProperty,
|
||||
String storeName,
|
||||
Map<Object, Entity> entitiesToCreate,
|
||||
Set<Entity> entitiesToRemove, Collection<ReferenceInfo> referenceInfoList) {
|
||||
Entity srcPropertyValue = srcEntity.<Entity>getValue(viewProperty.getName());
|
||||
if (viewProperty.getView() == null) {
|
||||
ReferenceInfo referenceInfo = new ReferenceInfo(dstEntity, viewProperty.getName(), srcPropertyValue, viewProperty.getReferenceImportBehaviour());
|
||||
referenceInfoList.add(referenceInfo);
|
||||
} else {
|
||||
Entity dstPropertyValue = importEntity(srcPropertyValue, viewProperty.getView(), storeName, entitiesToCreate, entitiesToRemove, referenceInfoList);
|
||||
dstEntity.setValue(viewProperty.getName(), dstPropertyValue);
|
||||
}
|
||||
}
|
||||
|
||||
protected void importCollectionAttribute(Entity srcEntity,
|
||||
Entity dstEntity,
|
||||
EntityImportViewProperty viewProperty,
|
||||
String storeName,
|
||||
Map<Object, Entity> entitiesToCreate,
|
||||
Set<Entity> entitiesToRemove,
|
||||
Collection<ReferenceInfo> referenceInfoList) {
|
||||
MetaProperty metaProperty = srcEntity.getMetaClass().getPropertyNN(viewProperty.getName());
|
||||
MetaProperty inverseMetaProperty = metaProperty.getInverse();
|
||||
boolean isComposition = metaProperty.getType() == MetaProperty.Type.COMPOSITION;
|
||||
|
||||
// if (srcEntity instanceof BaseGenericIdEntity) {
|
||||
// persistenceSecurity.restoreFilteredData((BaseGenericIdEntity<?>) srcEntity);
|
||||
// }
|
||||
|
||||
Multimap<String, UUID> filteredItems = ArrayListMultimap.create();
|
||||
if (srcEntity instanceof BaseGenericIdEntity) {
|
||||
//create an entity copy here, because filtered items must not be reloaded in the srcEntity for now,
|
||||
//we only need a collection of filtered properties
|
||||
byte[] securityToken = BaseEntityInternalAccess.getSecurityToken((BaseGenericIdEntity) srcEntity);
|
||||
Entity srcEntityCopy = metadata.getTools().deepCopy(srcEntity);
|
||||
BaseEntityInternalAccess.setSecurityToken((BaseGenericIdEntity) srcEntityCopy, securityToken);
|
||||
persistenceSecurity.restoreFilteredData((BaseGenericIdEntity<?>) srcEntityCopy);
|
||||
filteredItems = BaseEntityInternalAccess.getFilteredData((BaseGenericIdEntity) srcEntityCopy);
|
||||
}
|
||||
|
||||
Collection<Entity> srcPropertyValue = srcEntity.getValue(viewProperty.getName());
|
||||
if (srcPropertyValue == null && filteredItems.isEmpty()) {
|
||||
//remove absent items from the composition collection
|
||||
if (isComposition) {
|
||||
Collection<? extends Entity> value = dstEntity.getValue(viewProperty.getName());
|
||||
if (value != null) {
|
||||
entitiesToRemove.addAll(value);
|
||||
}
|
||||
}
|
||||
dstEntity.setValue(viewProperty.getName(), null);
|
||||
return;
|
||||
}
|
||||
Collection<Entity> collection;
|
||||
try {
|
||||
collection = srcPropertyValue.getClass().newInstance();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Error on import entities", e);
|
||||
}
|
||||
|
||||
for (Entity srcChildEntity : srcPropertyValue) {
|
||||
if (viewProperty.getView() == null) {
|
||||
ReferenceInfo referenceInfo = new ReferenceInfo(dstEntity, viewProperty.getName(), srcPropertyValue, viewProperty.getReferenceImportBehaviour());
|
||||
referenceInfoList.add(referenceInfo);
|
||||
} else {
|
||||
//create new referenced entity
|
||||
Entity dstChildEntity = importEntity(srcChildEntity, viewProperty.getView(), storeName, entitiesToCreate, entitiesToRemove, referenceInfoList);
|
||||
if (inverseMetaProperty != null) {
|
||||
dstChildEntity.setValue(inverseMetaProperty.getName(), dstEntity);
|
||||
}
|
||||
collection.add(dstChildEntity);
|
||||
}
|
||||
}
|
||||
|
||||
if (isComposition) {
|
||||
Collection<? extends Entity> dstValue = dstEntity.getValue(viewProperty.getName());
|
||||
if (dstValue != null) {
|
||||
Multimap<String, UUID> finalFilteredItems = filteredItems;
|
||||
List<? extends Entity> compositionEntitiesToRemove = dstValue.stream()
|
||||
.filter(entity -> !collection.contains(entity) &&
|
||||
(finalFilteredItems == null || !finalFilteredItems.containsValue(entity.getId())))
|
||||
.collect(Collectors.toList());
|
||||
entitiesToRemove.addAll(compositionEntitiesToRemove);
|
||||
}
|
||||
}
|
||||
|
||||
dstEntity.setValue(viewProperty.getName(), collection);
|
||||
}
|
||||
|
||||
protected void processReferenceInfo(ReferenceInfo referenceInfo, String store, Map<Object, Entity> entitiesToCreate, Map<Object, Entity> loadedEntities) {
|
||||
Entity entity = referenceInfo.getEntity();
|
||||
|
||||
String propertyName = referenceInfo.getPropertyName();
|
||||
String propertyName = referenceInfo.getViewProperty().getName();
|
||||
|
||||
MetaProperty metaProperty = entity.getMetaClass().getPropertyNN(propertyName);
|
||||
if (metaProperty.getRange().getCardinality().isMany()) {
|
||||
if (metaProperty.getRange().getCardinality() == Range.Cardinality.MANY_TO_MANY) {
|
||||
Collection<Entity> propertyValue = (Collection<Entity>) referenceInfo.getPropertyValue();
|
||||
|
||||
if (propertyValue == null) {
|
||||
@ -413,7 +452,7 @@ public class EntityImportExport implements EntityImportExportAPI {
|
||||
EntityManager em = persistence.getEntityManager(store);
|
||||
Entity loadedReference = em.reload(childEntity);
|
||||
if (loadedReference == null) {
|
||||
if (referenceInfo.getImportBehaviour() == ReferenceImportBehaviour.ERROR_ON_MISSING) {
|
||||
if (referenceInfo.getViewProperty().getReferenceImportBehaviour() == ReferenceImportBehaviour.ERROR_ON_MISSING) {
|
||||
throw new EntityImportException("Referenced entity for property '" + propertyName + "' with id = " + entity.getId() + " is missing");
|
||||
}
|
||||
} else {
|
||||
@ -422,6 +461,19 @@ public class EntityImportExport implements EntityImportExportAPI {
|
||||
loadedEntities.put(entity.getId(), loadedReference);
|
||||
}
|
||||
}
|
||||
|
||||
//keep absent collection members if we need it
|
||||
if (referenceInfo.getViewProperty().getCollectionImportPolicy() == CollectionImportPolicy.KEEP_ABSENT_ITEMS) {
|
||||
Collection<Entity> prevCollectionValue = (Collection<Entity>) referenceInfo.getPrevPropertyValue();
|
||||
if (prevCollectionValue != null) {
|
||||
for (Entity prevCollectionItem : prevCollectionValue) {
|
||||
if (!collection.contains(prevCollectionItem)) {
|
||||
collection.add(prevCollectionItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
entity.setValue(propertyName, collection);
|
||||
|
||||
//restore filtered data, otherwise they will be lost
|
||||
@ -438,7 +490,7 @@ public class EntityImportExport implements EntityImportExportAPI {
|
||||
EntityManager em = persistence.getEntityManager(store);
|
||||
Entity loadedReference = em.find(propertyValue.getClass(), propertyValue.getId());
|
||||
if (loadedReference == null) {
|
||||
if (referenceInfo.getImportBehaviour() == ReferenceImportBehaviour.ERROR_ON_MISSING) {
|
||||
if (referenceInfo.getViewProperty().getReferenceImportBehaviour() == ReferenceImportBehaviour.ERROR_ON_MISSING) {
|
||||
throw new EntityImportException("Referenced entity for property '" + propertyName + "' with id = " + propertyValue.getId() + " is missing");
|
||||
}
|
||||
} else {
|
||||
@ -451,31 +503,36 @@ public class EntityImportExport implements EntityImportExportAPI {
|
||||
|
||||
protected class ReferenceInfo {
|
||||
protected Entity entity;
|
||||
protected String propertyName;
|
||||
protected EntityImportViewProperty viewProperty;
|
||||
protected Object propertyValue;
|
||||
protected ReferenceImportBehaviour importBehaviour;
|
||||
protected Object prevPropertyValue;
|
||||
|
||||
public ReferenceInfo(Entity entity, String propertyName, Object propertyValue, ReferenceImportBehaviour importBehaviour) {
|
||||
public ReferenceInfo(Entity entity, EntityImportViewProperty viewProperty, Object propertyValue) {
|
||||
this.entity = entity;
|
||||
this.propertyName = propertyName;
|
||||
this.viewProperty = viewProperty;
|
||||
this.propertyValue = propertyValue;
|
||||
this.importBehaviour = importBehaviour;
|
||||
}
|
||||
|
||||
public ReferenceInfo(Entity entity, EntityImportViewProperty viewProperty, Object propertyValue, Object prevPropertyValue) {
|
||||
this.entity = entity;
|
||||
this.viewProperty = viewProperty;
|
||||
this.propertyValue = propertyValue;
|
||||
this.prevPropertyValue = prevPropertyValue;
|
||||
}
|
||||
|
||||
public EntityImportViewProperty getViewProperty() {
|
||||
return viewProperty;
|
||||
}
|
||||
|
||||
public Object getPrevPropertyValue() {
|
||||
return prevPropertyValue;
|
||||
}
|
||||
public Entity getEntity() {
|
||||
return entity;
|
||||
}
|
||||
|
||||
public String getPropertyName() {
|
||||
return propertyName;
|
||||
}
|
||||
|
||||
public Object getPropertyValue() {
|
||||
return propertyValue;
|
||||
}
|
||||
|
||||
public ReferenceImportBehaviour getImportBehaviour() {
|
||||
return importBehaviour;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2017 Haulmont.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.haulmont.cuba.core.app.importexport;
|
||||
|
||||
/**
|
||||
* Enum describes the policy of processing collection members when importing the entity with the {@link EntityImportView}
|
||||
*/
|
||||
public enum CollectionImportPolicy {
|
||||
|
||||
/**
|
||||
* Absent collection items will be keep.
|
||||
*/
|
||||
KEEP_ABSENT_ITEMS,
|
||||
|
||||
/**
|
||||
* Absent collection items will be removed from the database or excluded from the collection.
|
||||
*/
|
||||
REMOVE_ABSENT_ITEMS
|
||||
}
|
@ -29,39 +29,42 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* <p>Class describing how entity fields should be saved during the import performed by
|
||||
* {@link EntityImportExportService EntityImportExportService}</p>
|
||||
*
|
||||
* <p>Only fields that are added as properties to the {@code EntityImportView} will be saved.</p>
|
||||
*
|
||||
* <p>For simple entity property the rule is simple: if property name is added to the view, then the property will be saved.</p>
|
||||
*
|
||||
* <p>For references to other entities there are three options:</p>
|
||||
* <ul>
|
||||
* <li>Create or update the referenced entity/entities. This is useful when you are importing the master entities
|
||||
* with a collection of its detail entities, like Order and its OrderItems</li>
|
||||
* <li>Try to find the referenced entity in the database and continue the import process if it is not found</li>
|
||||
* <li>Try to find the referenced entity in the database and stop the import process if it is not found</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>Use the {@link #addProperty(String, EntityImportView)} method when you want to save the referenced entity. It will
|
||||
* be saved according to the {@code EntityImportView} passed as parameter.</p>
|
||||
*
|
||||
* <p>The property should be added with the {@link #addProperty(String, ReferenceImportBehaviour)} method if you want
|
||||
* to find the reference in the database. {@link ReferenceImportBehaviour} enum describes the desired behavior:
|
||||
* ignore missing reference or throw an exception if it was not found in the database.</p>
|
||||
*
|
||||
* <p>You can invoke {@code addProperty} methods in fluent interface style. There are also useful methods like
|
||||
* {@link #addLocalProperties()}, {@link #addSystemProperties()} or {@link #addProperties(String...)}</p>
|
||||
*
|
||||
* <p>Example of creating the EntityImportView object:</p>
|
||||
* Class describing how entity fields should be saved during the import performed by {@link EntityImportExportService
|
||||
* EntityImportExportService} <p> Only fields that are added as properties to the {@code EntityImportView} will be
|
||||
* saved.</p> <p> For local entity property the rule is simple: if property name is added to the view, then the property
|
||||
* will be saved. Use {@link #addLocalProperty(String)} method for adding local property to the view. <p> For
|
||||
* <b>many-to-one</b> references there are two possible options: <ul> <li>Create or update the referenced entity. Use
|
||||
* the {@link #addManyToOneProperty(String, EntityImportView)} method. The referenced entity will be saved according to
|
||||
* the {@code EntityImportView} passed as parameter</li> <li>Try to find the reference in the database and put it to the
|
||||
* property value. {@link #addManyToOneProperty(String, ReferenceImportBehaviour)} must be used for this. {@link
|
||||
* ReferenceImportBehaviour} parameter specifies the behaviour in case when referenced entity is missed in the database:
|
||||
* missing entity can be ignored or import may fail with an error.</li> </ul>
|
||||
* <p>
|
||||
* For <b>one-to-one</b> references behavior is the same as for the many-to-one references. Just use the corresponding
|
||||
* methods for adding properties to the view: {@link #addOneToOneProperty(String, EntityImportView)} or {@link
|
||||
* #addOneToOneProperty(String, ReferenceImportBehaviour)}.
|
||||
* <p>
|
||||
* For <b>one-to-many</b> references you must specify the {@link EntityImportView} which defines how entities from the
|
||||
* collection must be saved. The second parameter is the {@link CollectionImportPolicy} which specifies what to do with
|
||||
* collection items that weren't passed to the import: they can be removed or remained.
|
||||
* <p>
|
||||
* For <b>many-to-many</b> references the following things must be defined: <ul> <li>Whether the passed collection
|
||||
* members must be created/updated or just searched in the database</li> <li>Whether the collection items not passed to
|
||||
* the import must be removed or remain. Keep in mind that for many-to-many properties missing collection members will be
|
||||
* removed from the collection only, not from the database</li> </ul>
|
||||
* <p>
|
||||
* You can invoke methods for adding view properties in fluent interface style. There are also useful methods like
|
||||
* {@link #addLocalProperties()}, {@link #addSystemProperties()} or {@link #addProperties(String...)}
|
||||
* <p>
|
||||
* Example of creating the EntityImportView object:
|
||||
* <pre>
|
||||
* EntityImportView importView = new EntityImportView(Group.class)
|
||||
* .addLocalProperties()
|
||||
* .addProperty("constraints", new EntityImportView(Constraint.class).addLocalProperties())
|
||||
* .addProperty("parent", ReferenceImportBehaviour.IGNORE_MISSING);
|
||||
* .addOneToManyProperty("constraints",
|
||||
* new EntityImportView(Constraint.class).addLocalProperties(),
|
||||
* CollectionImportPolicy.KEEP_ABSENT_ITEMS)
|
||||
* .addManyToOneProperty("parent", ReferenceImportBehaviour.ERROR_ON_MISSING);
|
||||
* </pre>
|
||||
*
|
||||
*/
|
||||
public class EntityImportView implements Serializable {
|
||||
|
||||
@ -73,20 +76,56 @@ public class EntityImportView implements Serializable {
|
||||
this.entityClass = entityClass;
|
||||
}
|
||||
|
||||
public EntityImportView addProperty(String name, EntityImportView view) {
|
||||
public EntityImportView addLocalProperty(String name) {
|
||||
EntityImportViewProperty property = new EntityImportViewProperty(name);
|
||||
properties.put(name, property);
|
||||
return this;
|
||||
}
|
||||
|
||||
public EntityImportView addManyToOneProperty(String name, EntityImportView view) {
|
||||
EntityImportViewProperty property = new EntityImportViewProperty(name, view);
|
||||
properties.put(name, property);
|
||||
return this;
|
||||
}
|
||||
|
||||
public EntityImportView addProperty(String name, ReferenceImportBehaviour referenceImportBehaviour) {
|
||||
public EntityImportView addManyToOneProperty(String name, ReferenceImportBehaviour referenceImportBehaviour) {
|
||||
EntityImportViewProperty property = new EntityImportViewProperty(name, referenceImportBehaviour);
|
||||
properties.put(name, property);
|
||||
return this;
|
||||
}
|
||||
|
||||
public EntityImportView addProperty(String name) {
|
||||
EntityImportViewProperty property = new EntityImportViewProperty(name);
|
||||
public EntityImportView addOneToOneProperty(String name, EntityImportView view) {
|
||||
EntityImportViewProperty property = new EntityImportViewProperty(name, view);
|
||||
properties.put(name, property);
|
||||
return this;
|
||||
}
|
||||
|
||||
public EntityImportView addOneToOneProperty(String name, ReferenceImportBehaviour referenceImportBehaviour) {
|
||||
EntityImportViewProperty property = new EntityImportViewProperty(name, referenceImportBehaviour);
|
||||
properties.put(name, property);
|
||||
return this;
|
||||
}
|
||||
|
||||
public EntityImportView addOneToManyProperty(String name, EntityImportView view, CollectionImportPolicy collectionImportPolicy) {
|
||||
EntityImportViewProperty property = new EntityImportViewProperty(name, view, collectionImportPolicy);
|
||||
properties.put(name, property);
|
||||
return this;
|
||||
}
|
||||
|
||||
public EntityImportView addManyToManyProperty(String name, EntityImportView view, CollectionImportPolicy collectionImportPolicy) {
|
||||
EntityImportViewProperty property = new EntityImportViewProperty(name, view, collectionImportPolicy);
|
||||
properties.put(name, property);
|
||||
return this;
|
||||
}
|
||||
|
||||
public EntityImportView addManyToManyProperty(String name, ReferenceImportBehaviour referenceImportBehaviour, CollectionImportPolicy collectionImportPolicy) {
|
||||
EntityImportViewProperty property = new EntityImportViewProperty(name, referenceImportBehaviour, collectionImportPolicy);
|
||||
properties.put(name, property);
|
||||
return this;
|
||||
}
|
||||
|
||||
public EntityImportView addEmbeddedProperty(String name, EntityImportView view) {
|
||||
EntityImportViewProperty property = new EntityImportViewProperty(name, view);
|
||||
properties.put(name, property);
|
||||
return this;
|
||||
}
|
||||
@ -114,7 +153,7 @@ public class EntityImportView implements Serializable {
|
||||
MetadataTools metadataTools = metadata.getTools();
|
||||
metaClass.getProperties().stream()
|
||||
.filter(property -> !property.getRange().isClass() && !metadataTools.isSystem(property))
|
||||
.forEach(metaProperty -> addProperty(metaProperty.getName()));
|
||||
.forEach(metaProperty -> addLocalProperty(metaProperty.getName()));
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -124,13 +163,13 @@ public class EntityImportView implements Serializable {
|
||||
MetadataTools metadataTools = metadata.getTools();
|
||||
metaClass.getProperties().stream()
|
||||
.filter(metadataTools::isSystem)
|
||||
.forEach(metaProperty -> addProperty(metaProperty.getName()));
|
||||
.forEach(metaProperty -> addLocalProperty(metaProperty.getName()));
|
||||
return this;
|
||||
}
|
||||
|
||||
public EntityImportView addProperties(String... names) {
|
||||
for (String name : names) {
|
||||
addProperty(name);
|
||||
addLocalProperty(name);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ import java.util.Map;
|
||||
/**
|
||||
*/
|
||||
@Component(EntityImportViewBuilderAPI.NAME)
|
||||
public class EntityImportViewBuilder implements EntityImportViewBuilderAPI{
|
||||
public class EntityImportViewBuilder implements EntityImportViewBuilderAPI {
|
||||
|
||||
@Inject
|
||||
protected MetadataTools metadataTools;
|
||||
@ -68,7 +68,7 @@ public class EntityImportViewBuilder implements EntityImportViewBuilderAPI{
|
||||
Class<?> propertyType = metaProperty.getJavaType();
|
||||
if (propertyRange.isDatatype() || propertyRange.isEnum()) {
|
||||
if (security.isEntityAttrUpdatePermitted(metaClass, propertyName))
|
||||
view.addProperty(propertyName);
|
||||
view.addLocalProperty(propertyName);
|
||||
} else if (propertyRange.isClass()) {
|
||||
if (Entity.class.isAssignableFrom(propertyType)) {
|
||||
if (metadataTools.isEmbedded(metaProperty)) {
|
||||
@ -79,7 +79,7 @@ public class EntityImportViewBuilder implements EntityImportViewBuilderAPI{
|
||||
}
|
||||
if (security.isEntityAttrUpdatePermitted(metaClass, propertyName)) {
|
||||
EntityImportView propertyImportView = buildFromJsonObject(propertyJsonObject.getAsJsonObject(), propertyMetaClass);
|
||||
view.addProperty(propertyName, propertyImportView);
|
||||
view.addEmbeddedProperty(propertyName, propertyImportView);
|
||||
}
|
||||
} else {
|
||||
MetaClass propertyMetaClass = metadata.getClass(propertyType);
|
||||
@ -90,28 +90,40 @@ public class EntityImportViewBuilder implements EntityImportViewBuilderAPI{
|
||||
}
|
||||
if (security.isEntityAttrUpdatePermitted(metaClass, propertyName)) {
|
||||
EntityImportView propertyImportView = buildFromJsonObject(propertyJsonObject.getAsJsonObject(), propertyMetaClass);
|
||||
view.addProperty(propertyName, propertyImportView);
|
||||
if (metaProperty.getRange().getCardinality() == Range.Cardinality.MANY_TO_ONE) {
|
||||
view.addManyToOneProperty(propertyName, propertyImportView);
|
||||
} else {
|
||||
view.addOneToOneProperty(propertyName, propertyImportView);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
if (security.isEntityAttrUpdatePermitted(metaClass, propertyName))
|
||||
view.addProperty(propertyName, ReferenceImportBehaviour.ERROR_ON_MISSING);
|
||||
if (metaProperty.getRange().getCardinality() == Range.Cardinality.MANY_TO_ONE) {
|
||||
view.addManyToOneProperty(propertyName, ReferenceImportBehaviour.ERROR_ON_MISSING);
|
||||
} else {
|
||||
view.addOneToOneProperty(propertyName, ReferenceImportBehaviour.ERROR_ON_MISSING);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (Collection.class.isAssignableFrom(propertyType)) {
|
||||
MetaClass propertyMetaClass = metaProperty.getRange().asClass();
|
||||
if (metaProperty.getType() == MetaProperty.Type.COMPOSITION) {
|
||||
JsonElement compositionJsonArray = entry.getValue();
|
||||
if (!compositionJsonArray.isJsonArray()) {
|
||||
throw new RuntimeException("JsonArray was expected for property " + propertyName);
|
||||
}
|
||||
EntityImportView propertyImportView = buildFromJsonArray(compositionJsonArray.getAsJsonArray(), propertyMetaClass);
|
||||
if (security.isEntityAttrUpdatePermitted(metaClass, propertyName))
|
||||
view.addProperty(propertyName, propertyImportView);
|
||||
|
||||
} else {
|
||||
if (security.isEntityAttrUpdatePermitted(metaClass, propertyName))
|
||||
view.addProperty(propertyName, ReferenceImportBehaviour.ERROR_ON_MISSING);
|
||||
switch (metaProperty.getRange().getCardinality()) {
|
||||
case MANY_TO_MANY:
|
||||
if (security.isEntityAttrUpdatePermitted(metaClass, propertyName))
|
||||
view.addManyToManyProperty(propertyName, ReferenceImportBehaviour.ERROR_ON_MISSING, CollectionImportPolicy.REMOVE_ABSENT_ITEMS);
|
||||
break;
|
||||
case ONE_TO_MANY:
|
||||
if (metaProperty.getType() == MetaProperty.Type.COMPOSITION) {
|
||||
JsonElement compositionJsonArray = entry.getValue();
|
||||
if (!compositionJsonArray.isJsonArray()) {
|
||||
throw new RuntimeException("JsonArray was expected for property " + propertyName);
|
||||
}
|
||||
EntityImportView propertyImportView = buildFromJsonArray(compositionJsonArray.getAsJsonArray(), propertyMetaClass);
|
||||
if (security.isEntityAttrUpdatePermitted(metaClass, propertyName))
|
||||
view.addOneToManyProperty(propertyName, propertyImportView, CollectionImportPolicy.REMOVE_ABSENT_ITEMS);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -139,6 +151,7 @@ public class EntityImportViewBuilder implements EntityImportViewBuilderAPI{
|
||||
EntityImportViewProperty propertyCopy = new EntityImportViewProperty(p.getName());
|
||||
propertyCopy.setView(p.getView());
|
||||
propertyCopy.setReferenceImportBehaviour(p.getReferenceImportBehaviour());
|
||||
propertyCopy.setCollectionImportPolicy(p.getCollectionImportPolicy());
|
||||
resultView.addProperty(propertyCopy);
|
||||
});
|
||||
}
|
||||
|
@ -32,7 +32,10 @@ public interface EntityImportViewBuilderAPI {
|
||||
* All references will be added to the view as a {@link com.haulmont.cuba.core.app.importexport.ReferenceImportBehaviour#ERROR_ON_MISSING}
|
||||
* behavior. All references that have a @Composition annotation will be added to the view with a property that has a
|
||||
* {@link com.haulmont.cuba.core.app.importexport.EntityImportViewProperty}. This means that compositions will be
|
||||
* persisted during the import.
|
||||
* persisted during the import. Absent collection items will be removed from the database.
|
||||
* <p>
|
||||
* For many-to-many association items corresponding entities will be searched in the database. If any of them is
|
||||
* missing, an error will be thrown. Absent collection members will be excluded from the many-to-many association.
|
||||
*
|
||||
* @param json a string that represents a JSON object
|
||||
* @param metaClass a MetaClass of the entity
|
||||
|
@ -27,6 +27,8 @@ public class EntityImportViewProperty implements Serializable {
|
||||
|
||||
protected ReferenceImportBehaviour referenceImportBehaviour;
|
||||
|
||||
protected CollectionImportPolicy collectionImportPolicy;
|
||||
|
||||
public EntityImportViewProperty(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
@ -36,11 +38,23 @@ public class EntityImportViewProperty implements Serializable {
|
||||
this.view = view;
|
||||
}
|
||||
|
||||
public EntityImportViewProperty(String name, EntityImportView view, CollectionImportPolicy collectionImportPolicy) {
|
||||
this.name = name;
|
||||
this.view = view;
|
||||
this.collectionImportPolicy = collectionImportPolicy;
|
||||
}
|
||||
|
||||
public EntityImportViewProperty(String name, ReferenceImportBehaviour referenceImportBehaviour) {
|
||||
this.name = name;
|
||||
this.referenceImportBehaviour = referenceImportBehaviour;
|
||||
}
|
||||
|
||||
public EntityImportViewProperty(String name, ReferenceImportBehaviour referenceImportBehaviour, CollectionImportPolicy collectionImportPolicy) {
|
||||
this.name = name;
|
||||
this.referenceImportBehaviour = referenceImportBehaviour;
|
||||
this.collectionImportPolicy = collectionImportPolicy;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
@ -64,4 +78,12 @@ public class EntityImportViewProperty implements Serializable {
|
||||
public void setReferenceImportBehaviour(ReferenceImportBehaviour referenceImportBehaviour) {
|
||||
this.referenceImportBehaviour = referenceImportBehaviour;
|
||||
}
|
||||
|
||||
public CollectionImportPolicy getCollectionImportPolicy() {
|
||||
return collectionImportPolicy;
|
||||
}
|
||||
|
||||
public void setCollectionImportPolicy(CollectionImportPolicy collectionImportPolicy) {
|
||||
this.collectionImportPolicy = collectionImportPolicy;
|
||||
}
|
||||
}
|
@ -20,8 +20,10 @@ package com.haulmont.cuba.gui.app.core.entityinspector;
|
||||
import com.haulmont.bali.util.ParamsMap;
|
||||
import com.haulmont.chile.core.model.MetaClass;
|
||||
import com.haulmont.chile.core.model.MetaProperty;
|
||||
import com.haulmont.chile.core.model.Range;
|
||||
import com.haulmont.chile.core.model.Session;
|
||||
import com.haulmont.cuba.client.ClientConfig;
|
||||
import com.haulmont.cuba.core.app.importexport.CollectionImportPolicy;
|
||||
import com.haulmont.cuba.core.app.importexport.EntityImportExportService;
|
||||
import com.haulmont.cuba.core.app.importexport.EntityImportView;
|
||||
import com.haulmont.cuba.core.app.importexport.ReferenceImportBehaviour;
|
||||
@ -401,12 +403,18 @@ public class EntityInspectorBrowse extends AbstractLookup {
|
||||
switch (metaProperty.getType()) {
|
||||
case DATATYPE:
|
||||
case ENUM:
|
||||
entityImportView.addProperty(metaProperty.getName());
|
||||
entityImportView.addLocalProperty(metaProperty.getName());
|
||||
break;
|
||||
case ASSOCIATION:
|
||||
case COMPOSITION:
|
||||
if (!metaProperty.getRange().getCardinality().isMany()) {
|
||||
entityImportView.addProperty(metaProperty.getName(), ReferenceImportBehaviour.IGNORE_MISSING);
|
||||
Range.Cardinality cardinality = metaProperty.getRange().getCardinality();
|
||||
switch (cardinality) {
|
||||
case MANY_TO_ONE:
|
||||
entityImportView.addManyToOneProperty(metaProperty.getName(), ReferenceImportBehaviour.IGNORE_MISSING);
|
||||
break;
|
||||
case ONE_TO_ONE:
|
||||
entityImportView.addOneToOneProperty(metaProperty.getName(), ReferenceImportBehaviour.IGNORE_MISSING);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
@ -18,6 +18,7 @@ package com.haulmont.cuba.gui.app.security.group.browse;
|
||||
|
||||
import com.haulmont.bali.util.ParamsMap;
|
||||
import com.haulmont.chile.core.model.MetaClass;
|
||||
import com.haulmont.cuba.core.app.importexport.CollectionImportPolicy;
|
||||
import com.haulmont.cuba.core.app.importexport.EntityImportExportService;
|
||||
import com.haulmont.cuba.core.app.importexport.EntityImportView;
|
||||
import com.haulmont.cuba.core.app.importexport.ReferenceImportBehaviour;
|
||||
@ -278,12 +279,18 @@ public class GroupBrowser extends AbstractWindow {
|
||||
protected EntityImportView createGroupsImportView() {
|
||||
return new EntityImportView(Group.class)
|
||||
.addLocalProperties()
|
||||
.addProperty("parent", ReferenceImportBehaviour.ERROR_ON_MISSING)
|
||||
.addProperty("hierarchyList", new EntityImportView(GroupHierarchy.class)
|
||||
.addManyToOneProperty("parent", ReferenceImportBehaviour.ERROR_ON_MISSING)
|
||||
.addOneToManyProperty("hierarchyList",
|
||||
new EntityImportView(GroupHierarchy.class)
|
||||
.addLocalProperties()
|
||||
.addProperty("parent", ReferenceImportBehaviour.ERROR_ON_MISSING))
|
||||
.addProperty("sessionAttributes", new EntityImportView(SessionAttribute.class).addLocalProperties())
|
||||
.addProperty("constraints", new EntityImportView(Constraint.class).addLocalProperties());
|
||||
.addManyToOneProperty("parent", ReferenceImportBehaviour.ERROR_ON_MISSING),
|
||||
CollectionImportPolicy.REMOVE_ABSENT_ITEMS)
|
||||
.addOneToManyProperty("sessionAttributes",
|
||||
new EntityImportView(SessionAttribute.class).addLocalProperties(),
|
||||
CollectionImportPolicy.REMOVE_ABSENT_ITEMS)
|
||||
.addOneToManyProperty("constraints",
|
||||
new EntityImportView(Constraint.class).addLocalProperties(),
|
||||
CollectionImportPolicy.REMOVE_ABSENT_ITEMS);
|
||||
}
|
||||
|
||||
public void copyGroup() {
|
||||
|
@ -16,6 +16,7 @@
|
||||
*/
|
||||
package com.haulmont.cuba.gui.app.security.role.browse;
|
||||
|
||||
import com.haulmont.cuba.core.app.importexport.CollectionImportPolicy;
|
||||
import com.haulmont.cuba.core.app.importexport.EntityImportExportService;
|
||||
import com.haulmont.cuba.core.app.importexport.EntityImportView;
|
||||
import com.haulmont.cuba.core.entity.Entity;
|
||||
@ -212,7 +213,9 @@ public class RoleBrowser extends AbstractLookup {
|
||||
protected EntityImportView createRolesImportView() {
|
||||
return new EntityImportView(Role.class)
|
||||
.addLocalProperties()
|
||||
.addProperty("permissions", new EntityImportView(Permission.class).addLocalProperties());
|
||||
.addOneToManyProperty("permissions",
|
||||
new EntityImportView(Permission.class).addLocalProperties(),
|
||||
CollectionImportPolicy.REMOVE_ABSENT_ITEMS);
|
||||
}
|
||||
|
||||
protected class ExportAction extends ItemTrackingAction {
|
||||
|
Loading…
Reference in New Issue
Block a user