use of com.blazebit.persistence.spi.JpaProvider in project blaze-persistence by Blazebit.
the class JpaUtils method expandBindings.
public static void expandBindings(Map<String, Integer> bindingMap, Map<String, String> columnBindingMap, Map<String, ExtendedAttribute<?, ?>> attributeEntries, ClauseType clause, AbstractCommonQueryBuilder<?, ?, ?, ?, ?> queryBuilder, String keyFunctionExpression, boolean enableElementCollectionIdCutoff) {
SelectManager<?> selectManager = queryBuilder.selectManager;
JoinManager joinManager = queryBuilder.joinManager;
ParameterManager parameterManager = queryBuilder.parameterManager;
JpaProvider jpaProvider = queryBuilder.mainQuery.jpaProvider;
EntityMetamodelImpl metamodel = queryBuilder.mainQuery.metamodel;
boolean requiresNullCast = queryBuilder.mainQuery.dbmsDialect.requiresNullCast();
boolean needsCastParameters = queryBuilder.mainQuery.dbmsDialect.needsCastParameters();
JpaMetamodelAccessor jpaMetamodelAccessor = jpaProvider.getJpaMetamodelAccessor();
boolean needsElementCollectionIdCutoff = enableElementCollectionIdCutoff && jpaProvider.needsElementCollectionIdCutoff();
final Queue<String> attributeQueue = new ArrayDeque<>(bindingMap.keySet());
while (!attributeQueue.isEmpty()) {
final String attributeName = attributeQueue.remove();
Integer tupleIndex = bindingMap.get(attributeName);
Class<?> elementType;
String columnType;
boolean splitExpression;
ExtendedAttribute<?, ?> attributeEntry = attributeEntries.get(attributeName);
if (attributeEntry == null) {
if (!attributeName.equalsIgnoreCase(keyFunctionExpression)) {
continue;
}
String realAttributeName = attributeName.substring(attributeName.indexOf('(') + 1, attributeName.length() - 1);
attributeEntry = attributeEntries.get(realAttributeName);
if (attributeEntry.getAttribute() instanceof ListAttribute<?, ?>) {
elementType = Integer.class;
columnType = queryBuilder.mainQuery.dbmsDialect.getSqlType(Integer.class);
} else {
MapAttribute<?, ?, ?> mapAttribute = (MapAttribute<?, ?, ?>) attributeEntry.getAttribute();
elementType = mapAttribute.getKeyJavaType();
columnType = attributeEntry.getJoinTable() != null && attributeEntry.getJoinTable().getKeyColumnTypes() != null && attributeEntry.getJoinTable().getKeyColumnTypes().size() == 1 ? attributeEntry.getJoinTable().getKeyColumnTypes().values().iterator().next() : null;
}
splitExpression = false;
} else {
elementType = attributeEntry.getElementClass();
columnType = attributeEntry.getColumnTypes().length == 0 ? null : attributeEntry.getColumnTypes()[0];
final List<Attribute<?, ?>> attributePath = attributeEntry.getAttributePath();
final Attribute<?, ?> lastAttribute = attributePath.get(attributePath.size() - 1);
splitExpression = lastAttribute.getPersistentAttributeType() == Attribute.PersistentAttributeType.EMBEDDED;
if (!splitExpression) {
if ((clause != ClauseType.SET || jpaProvider.supportsUpdateSetAssociationId()) && jpaMetamodelAccessor.isJoinable(lastAttribute) && !isBasicElementType(lastAttribute)) {
splitExpression = true;
if (needsElementCollectionIdCutoff) {
OUTER: for (int i = 0; i < attributePath.size() - 1; i++) {
Attribute<?, ?> attribute = attributePath.get(i);
if (attribute.getPersistentAttributeType() == Attribute.PersistentAttributeType.ELEMENT_COLLECTION) {
// This is a special case, when an embeddable is between an element collection and the association, we still need to split the expression
for (int j = i + 1; j < attributePath.size() - 1; j++) {
attribute = attributePath.get(j);
if (attribute.getPersistentAttributeType() == Attribute.PersistentAttributeType.EMBEDDED) {
break OUTER;
}
}
splitExpression = false;
break;
}
}
}
}
}
}
SelectInfo selectInfo = selectManager.getSelectInfos().get(tupleIndex);
final Expression selectExpression = selectInfo.getExpression();
if (splitExpression) {
// TODO: Maybe also allow Treat, Case-When, Array?
if (selectExpression instanceof NullExpression) {
final Collection<String> embeddedPropertyNames = getEmbeddedPropertyPaths(attributeEntries, attributeName, needsElementCollectionIdCutoff, false);
if (embeddedPropertyNames.size() > 0) {
selectManager.getSelectInfos().remove(tupleIndex.intValue());
bindingMap.remove(attributeName);
// We are going to insert the expanded attributes as new select items and shift existing ones
int delta = embeddedPropertyNames.size() - 1;
if (delta > 0) {
for (Map.Entry<String, Integer> entry : bindingMap.entrySet()) {
if (entry.getValue() > tupleIndex) {
entry.setValue(entry.getValue() + delta);
}
}
}
int offset = 0;
for (String embeddedPropertyName : embeddedPropertyNames) {
String nestedAttributePath = attributeName + "." + embeddedPropertyName;
ExtendedAttribute<?, ?> nestedAttributeEntry = attributeEntries.get(nestedAttributePath);
// Process the nested attribute path recursively
attributeQueue.add(nestedAttributePath);
// Replace this binding in the binding map, additional selects need an updated index
bindingMap.put(nestedAttributePath, tupleIndex + offset);
selectManager.select(offset == 0 ? selectExpression : selectExpression.copy(ExpressionCopyContext.EMPTY), null, tupleIndex + offset);
if (columnBindingMap != null) {
for (String column : nestedAttributeEntry.getColumnNames()) {
columnBindingMap.put(column, nestedAttributePath);
}
}
offset++;
}
}
} else if (selectExpression instanceof PathExpression) {
boolean firstBinding = true;
final Collection<String> embeddedPropertyNames = getEmbeddedPropertyPaths(attributeEntries, attributeName, needsElementCollectionIdCutoff, false);
PathExpression baseExpression = embeddedPropertyNames.size() > 1 ? ((PathExpression) selectExpression).copy(ExpressionCopyContext.EMPTY) : ((PathExpression) selectExpression);
joinManager.implicitJoin(baseExpression, true, true, true, null, ClauseType.SELECT, new HashSet<String>(), false, false, false, false);
if (elementType != baseExpression.getPathReference().getType().getJavaType()) {
throw new IllegalStateException("An association should be bound to its association type and not its identifier type");
}
if (embeddedPropertyNames.size() > 0) {
bindingMap.remove(attributeName);
// We are going to insert the expanded attributes as new select items and shift existing ones
int delta = embeddedPropertyNames.size() - 1;
if (delta > 0) {
for (Map.Entry<String, Integer> entry : bindingMap.entrySet()) {
if (entry.getValue() > tupleIndex) {
entry.setValue(entry.getValue() + delta);
}
}
}
int offset = 0;
for (String embeddedPropertyName : embeddedPropertyNames) {
PathExpression pathExpression = firstBinding ? ((PathExpression) selectExpression) : baseExpression.copy(ExpressionCopyContext.EMPTY);
for (String propertyNamePart : embeddedPropertyName.split("\\.")) {
pathExpression.getExpressions().add(new PropertyExpression(propertyNamePart));
}
String nestedAttributePath = attributeName + "." + embeddedPropertyName;
ExtendedAttribute<?, ?> nestedAttributeEntry = attributeEntries.get(nestedAttributePath);
// Process the nested attribute path recursively
attributeQueue.add(nestedAttributePath);
// Replace this binding in the binding map, additional selects need an updated index
bindingMap.put(nestedAttributePath, firstBinding ? tupleIndex : tupleIndex + offset);
if (!firstBinding) {
selectManager.select(pathExpression, null, tupleIndex + offset);
} else {
firstBinding = false;
}
if (columnBindingMap != null) {
for (String column : nestedAttributeEntry.getColumnNames()) {
columnBindingMap.put(column, nestedAttributePath);
}
}
offset++;
}
}
} else if (selectExpression instanceof ParameterExpression) {
final Collection<String> embeddedPropertyNames = getEmbeddedPropertyPaths(attributeEntries, attributeName, jpaProvider.needsElementCollectionIdCutoff(), false);
if (embeddedPropertyNames.size() > 0) {
ParameterExpression parameterExpression = (ParameterExpression) selectExpression;
String parameterName = parameterExpression.getName();
Map<String, List<String>> parameterAccessPaths = new HashMap<>(embeddedPropertyNames.size());
ParameterValueTransformer tranformer = parameterManager.getParameter(parameterName).getTransformer();
if (tranformer instanceof SplittingParameterTransformer) {
for (String name : ((SplittingParameterTransformer) tranformer).getParameterNames()) {
parameterManager.unregisterParameterName(name, clause, queryBuilder);
}
}
selectManager.getSelectInfos().remove(tupleIndex.intValue());
bindingMap.remove(attributeName);
// We are going to insert the expanded attributes as new select items and shift existing ones
int delta = embeddedPropertyNames.size() - 1;
if (delta > 0) {
for (Map.Entry<String, Integer> entry : bindingMap.entrySet()) {
if (entry.getValue() > tupleIndex) {
entry.setValue(entry.getValue() + delta);
}
}
}
int offset = 0;
for (String embeddedPropertyName : embeddedPropertyNames) {
String subParamName = "_" + parameterName + "_" + embeddedPropertyName.replace('.', '_');
parameterManager.registerParameterName(subParamName, false, clause, queryBuilder);
parameterAccessPaths.put(subParamName, Arrays.asList(embeddedPropertyName.split("\\.")));
String nestedAttributePath = attributeName + "." + embeddedPropertyName;
ExtendedAttribute<?, ?> nestedAttributeEntry = attributeEntries.get(nestedAttributePath);
// Process the nested attribute path recursively
attributeQueue.add(nestedAttributePath);
// Replace this binding in the binding map, additional selects need an updated index
bindingMap.put(nestedAttributePath, tupleIndex + offset);
selectManager.select(new ParameterExpression(subParamName), null, tupleIndex + offset);
if (columnBindingMap != null) {
for (String column : nestedAttributeEntry.getColumnNames()) {
columnBindingMap.put(column, nestedAttributePath);
}
}
offset++;
}
parameterManager.getParameter(parameterName).setTransformer(new SplittingParameterTransformer(parameterManager, metamodel, elementType, parameterAccessPaths));
}
} else {
throw new IllegalArgumentException("Illegal expression '" + selectExpression.toString() + "' for binding relation '" + attributeName + "'!");
}
} else if (requiresNullCast && selectExpression instanceof NullExpression) {
if (BasicCastTypes.TYPES.contains(elementType) && queryBuilder.statementType != DbmsStatementType.INSERT) {
// We also need a cast for parameter expressions except in the SET clause
List<Expression> arguments = new ArrayList<>(2);
arguments.add(selectExpression);
if (columnType != null) {
arguments.add(new StringLiteral(columnType));
}
selectInfo.set(new FunctionExpression("CAST_" + elementType.getSimpleName(), arguments, selectExpression));
} else {
final EntityMetamodelImpl.AttributeExample attributeExample = metamodel.getBasicTypeExampleAttributes().get(elementType);
if (attributeExample != null) {
List<Expression> arguments = new ArrayList<>(2);
arguments.add(new SubqueryExpression(new Subquery() {
@Override
public String getQueryString() {
return attributeExample.getExampleJpql() + selectExpression;
}
}));
if (queryBuilder.statementType != DbmsStatementType.INSERT && needsCastParameters) {
arguments.add(new StringLiteral(attributeExample.getAttribute().getColumnTypes()[0]));
}
selectInfo.set(new FunctionExpression(NullfnFunction.FUNCTION_NAME, arguments, selectExpression));
}
}
} else if (selectExpression instanceof ParameterExpression && clause != ClauseType.SET) {
if (BasicCastTypes.TYPES.contains(elementType) && queryBuilder.statementType != DbmsStatementType.INSERT) {
// We also need a cast for parameter expressions except in the SET clause
List<Expression> arguments = new ArrayList<>(2);
arguments.add(selectExpression);
if (columnType != null) {
arguments.add(new StringLiteral(columnType));
}
selectInfo.set(new FunctionExpression("CAST_" + elementType.getSimpleName(), arguments, selectExpression));
} else {
final EntityMetamodelImpl.AttributeExample attributeExample = metamodel.getBasicTypeExampleAttributes().get(elementType);
if (attributeExample != null) {
List<Expression> arguments = new ArrayList<>(2);
arguments.add(new SubqueryExpression(new Subquery() {
@Override
public String getQueryString() {
return attributeExample.getExampleJpql() + selectExpression;
}
}));
if (queryBuilder.statementType != DbmsStatementType.INSERT && needsCastParameters) {
arguments.add(new StringLiteral(attributeExample.getAttribute().getColumnTypes()[0]));
}
selectInfo.set(new FunctionExpression(ParamFunction.FUNCTION_NAME, arguments, selectExpression));
}
}
}
}
}
use of com.blazebit.persistence.spi.JpaProvider in project blaze-persistence by Blazebit.
the class MainQuery method create.
public static MainQuery create(CriteriaBuilderFactoryImpl cbf, EntityManager em, String dbms, DbmsDialect dbmsDialect, Map<String, JpqlFunction> registeredFunctions, Map<String, String> registeredFunctionNames) {
if (cbf == null) {
throw new NullPointerException("criteriaBuilderFactory");
}
JpaProvider jpaProvider = cbf.getJpaProvider();
ParameterManager parameterManager = new ParameterManager(jpaProvider, cbf.getMetamodel());
return new MainQuery(cbf, em, jpaProvider, dbmsDialect, registeredFunctions, registeredFunctionNames, parameterManager);
}
use of com.blazebit.persistence.spi.JpaProvider in project blaze-persistence by Blazebit.
the class TypeDescriptor method getAttributeElementIdentifier.
public static String getAttributeElementIdentifier(EntityViewManagerImpl evm, EntityType<?> ownerEntityType, String attributeName, String ownerMapping, String attributeMapping, ManagedType<?> elementType) {
if (attributeMapping == null) {
return null;
}
JpaProvider jpaProvider = evm.getJpaProvider();
Collection<String> joinTableOwnerProperties;
if (ownerMapping == null) {
joinTableOwnerProperties = jpaProvider.getJoinMappingPropertyNames(ownerEntityType, ownerMapping, attributeMapping).keySet();
} else {
joinTableOwnerProperties = jpaProvider.getJoinMappingPropertyNames(ownerEntityType, ownerMapping, ownerMapping + "." + attributeMapping).keySet();
}
if (joinTableOwnerProperties.isEmpty()) {
if (elementType instanceof EntityType<?>) {
return JpaMetamodelUtils.getSingleIdAttribute((EntityType<?>) elementType).getName();
}
throw new IllegalArgumentException("Couldn't find joinable owner properties for attribute " + attributeName + " of " + ownerEntityType.getJavaType().getName());
} else if (joinTableOwnerProperties.size() != 1) {
SingularAttribute<?, ?> singleIdAttribute = JpaMetamodelUtils.getSingleIdAttribute(ownerEntityType);
// If the id type is an embeddable, we could get multiple properties, so we need to check if the property paths are properly prefixed
String prefix = singleIdAttribute.getName() + ".";
for (String joinTableOwnerProperty : joinTableOwnerProperties) {
if (!joinTableOwnerProperty.startsWith(prefix)) {
throw new IllegalArgumentException("Multiple joinable owner properties for attribute " + attributeName + " of " + ownerEntityType.getJavaType().getName() + " found which is not yet supported. Consider using the primary key instead!");
}
}
return singleIdAttribute.getName();
}
return joinTableOwnerProperties.iterator().next();
}
use of com.blazebit.persistence.spi.JpaProvider in project blaze-persistence by Blazebit.
the class AbstractCorrelatedSubselectTupleTransformer method prepare.
private void prepare() {
JpaProvider jpaProvider = entityViewConfiguration.getCriteriaBuilder().getService(JpaProvider.class);
FullQueryBuilder<?, ?> queryBuilder = entityViewConfiguration.getCriteriaBuilder();
Map<String, Object> optionalParameters = entityViewConfiguration.getOptionalParameters();
Class<?> correlationBasisEntityType = correlationBasisEntity;
String viewRootExpression = viewRootAlias;
EmbeddingViewJpqlMacro embeddingViewJpqlMacro = entityViewConfiguration.getEmbeddingViewJpqlMacro();
ViewJpqlMacro viewJpqlMacro = entityViewConfiguration.getViewJpqlMacro();
if (queryBuilder instanceof PaginatedCriteriaBuilder<?>) {
criteriaBuilder = queryBuilder.copyCriteriaBuilder(Object[].class, false);
} else {
LimitBuilder<?> limitBuilder = (LimitBuilder<?>) queryBuilder;
// To set the limit, we need the JPA provider to support this
if (jpaProvider.supportsSubqueryInFunction() && (limitBuilder.getFirstResult() > 0 || limitBuilder.getMaxResults() < Integer.MAX_VALUE)) {
// we must turn this query builder into a paginated criteria builder first
try {
criteriaBuilder = queryBuilder.copyCriteriaBuilder(Object[].class, true).page(limitBuilder.getFirstResult(), limitBuilder.getMaxResults()).copyCriteriaBuilder(Object[].class, false);
} catch (IllegalStateException ex) {
LOG.log(Level.WARNING, "Could not create a paginated criteria builder for SUBSELECT fetching which might lead to bad performance", ex);
criteriaBuilder = queryBuilder.copyCriteriaBuilder(Object[].class, false);
}
} else {
// Regular query without limit/offset
criteriaBuilder = queryBuilder.copyCriteriaBuilder(Object[].class, false);
}
}
int originalFirstResult = 0;
int originalMaxResults = Integer.MAX_VALUE;
// A copied query that is extended with further joins can't possibly use the limits provided by the outer query
((LimitBuilder<?>) criteriaBuilder).setFirstResult(originalFirstResult);
((LimitBuilder<?>) criteriaBuilder).setMaxResults(originalMaxResults);
this.viewRootJpqlMacro = new CorrelatedSubqueryViewRootJpqlMacro(criteriaBuilder, optionalParameters, false, viewRootEntityClass, idAttributePath, viewRootExpression);
criteriaBuilder.registerMacro("view", viewJpqlMacro);
criteriaBuilder.registerMacro("view_root", viewRootJpqlMacro);
criteriaBuilder.registerMacro("embedding_view", embeddingViewJpqlMacro);
String oldViewPath = viewJpqlMacro.getViewPath();
String oldEmbeddingViewPath = embeddingViewJpqlMacro.getEmbeddingViewPath();
viewJpqlMacro.setViewPath(correlationResultExpression);
embeddingViewJpqlMacro.setEmbeddingViewPath(embeddingViewPath);
String joinBase = embeddingViewPath;
SubqueryCorrelationBuilder correlationBuilder = new SubqueryCorrelationBuilder(queryBuilder, optionalParameters, criteriaBuilder, correlationAlias, correlationExternalAlias, correlationResultExpression, correlationBasisType, correlationBasisEntityType, joinBase, attributePath, 1, limiter, true);
CorrelationProvider provider = correlationProviderFactory.create(entityViewConfiguration.getCriteriaBuilder(), entityViewConfiguration.getOptionalParameters());
provider.applyCorrelation(correlationBuilder, correlationBasisExpression);
if (criteriaBuilder instanceof LimitBuilder<?>) {
if (originalFirstResult != ((LimitBuilder<?>) criteriaBuilder).getFirstResult() || originalMaxResults != ((LimitBuilder<?>) criteriaBuilder).getMaxResults()) {
throw new IllegalArgumentException("Correlation provider '" + provider + "' wrongly uses setFirstResult() or setMaxResults() on the query builder which might lead to wrong results. Use SELECT fetching with batch size 1 or reformulate the correlation provider to use the limit/offset in a subquery!");
}
}
if (fetches.length != 0) {
for (int i = 0; i < fetches.length; i++) {
criteriaBuilder.fetch(fetches[i]);
}
}
if (indexFetches.length != 0) {
for (int i = 0; i < indexFetches.length; i++) {
criteriaBuilder.fetch(indexFetches[i]);
}
}
// Before we can determine whether we use view roots or embedding views, we need to add all selects, otherwise macros might report false although they are used
final String correlationRoot = correlationBuilder.getCorrelationRoot();
final int tupleSuffix = maximumViewMapperCount + 1 + (indexCorrelator == null && indexExpression == null ? 0 : 1);
ObjectBuilder<Object[]> objectBuilder = (ObjectBuilder<Object[]>) correlator.finish(criteriaBuilder, entityViewConfiguration, 0, tupleSuffix, correlationRoot, embeddingViewJpqlMacro, true);
final boolean usesViewRoot = viewRootJpqlMacro.usesViewMacro();
final boolean usesEmbeddingView = embeddingViewJpqlMacro.usesEmbeddingView();
if (usesEmbeddingView && !(embeddingViewType instanceof ViewType<?>)) {
throw new IllegalStateException("The use of EMBEDDING_VIEW in the correlation for '" + embeddingViewType.getJavaType().getName() + "." + attributePath.substring(attributePath.lastIndexOf('.') + 1) + "' is illegal because the embedding view type '" + embeddingViewType.getJavaType().getName() + "' does not declare a @IdMapping!");
} else if (usesViewRoot && !(viewRootType instanceof ViewType<?>)) {
throw new IllegalStateException("The use of VIEW_ROOT in the correlation for '" + embeddingViewType.getJavaType().getName() + "." + attributePath.substring(attributePath.lastIndexOf('.') + 1) + "' is illegal because the view root type '" + viewRootType.getJavaType().getName() + "' does not declare a @IdMapping!");
}
final int maximumSlotsFilled;
final int elementKeyIndex;
final int elementViewIndex;
if (usesEmbeddingView) {
maximumSlotsFilled = embeddingViewIdMapperCount == 0 ? 1 : embeddingViewIdMapperCount;
elementKeyIndex = (maximumViewMapperCount - maximumSlotsFilled) + 2 + valueIndex;
elementViewIndex = (maximumViewMapperCount - maximumSlotsFilled) + 1 + valueIndex;
viewIndex = embeddingViewIndex;
} else if (usesViewRoot) {
maximumSlotsFilled = viewRootIdMapperCount == 0 ? 1 : viewRootIdMapperCount;
elementKeyIndex = (maximumViewMapperCount - maximumSlotsFilled) + 2 + valueIndex;
elementViewIndex = (maximumViewMapperCount - maximumSlotsFilled) + 1 + valueIndex;
viewIndex = viewRootIndex;
} else {
maximumSlotsFilled = 0;
elementKeyIndex = maximumViewMapperCount + 1 + valueIndex;
elementViewIndex = 1 + valueIndex;
viewIndex = -1;
}
for (int i = maximumSlotsFilled; i < maximumViewMapperCount; i++) {
criteriaBuilder.select("NULL");
}
ExpressionFactory ef = criteriaBuilder.getService(ExpressionFactory.class);
if (usesEmbeddingView) {
EntityViewConfiguration configuration = new EntityViewConfiguration(criteriaBuilder, ef, new MutableViewJpqlMacro(), new MutableEmbeddingViewJpqlMacro(), Collections.<String, Object>emptyMap(), Collections.<String, Object>emptyMap(), entityViewConfiguration.getFetches(), attributePath);
ObjectBuilder<Object[]> embeddingViewObjectBuilder = createViewAwareObjectBuilder(criteriaBuilder, embeddingViewType, configuration, embeddingViewIdExpression);
if (embeddingViewObjectBuilder == null) {
criteriaBuilder.select(embeddingViewIdExpression);
} else {
criteriaBuilder.selectNew(objectBuilder = new LateAdditionalObjectBuilder(objectBuilder, embeddingViewObjectBuilder, true));
}
} else if (usesViewRoot) {
EntityViewConfiguration configuration = new EntityViewConfiguration(criteriaBuilder, ef, new MutableViewJpqlMacro(), new MutableEmbeddingViewJpqlMacro(), Collections.<String, Object>emptyMap(), Collections.<String, Object>emptyMap(), entityViewConfiguration.getFetches(), attributePath);
ObjectBuilder<Object[]> viewRootObjectBuilder = createViewAwareObjectBuilder(criteriaBuilder, viewRootType, configuration, viewRootIdExpression);
if (viewRootObjectBuilder == null) {
criteriaBuilder.select(viewRootIdExpression);
} else {
criteriaBuilder.selectNew(objectBuilder = new LateAdditionalObjectBuilder(objectBuilder, viewRootObjectBuilder, true));
}
}
criteriaBuilder.select(correlationKeyExpression);
if (indexCorrelator != null) {
ObjectBuilder<?> indexBuilder = indexCorrelator.finish(criteriaBuilder, entityViewConfiguration, maximumViewMapperCount + 2, 0, indexExpression, embeddingViewJpqlMacro, true);
if (indexBuilder != null) {
criteriaBuilder.selectNew(new LateAdditionalObjectBuilder(objectBuilder, indexBuilder, false));
}
}
populateParameters(entityViewConfiguration, criteriaBuilder);
viewJpqlMacro.setViewPath(oldViewPath);
embeddingViewJpqlMacro.setEmbeddingViewPath(oldEmbeddingViewPath);
List<Object[]> resultList = (List<Object[]>) criteriaBuilder.getResultList();
Map<Object, Map<Object, Object>> collections = new HashMap<>(resultList.size());
for (int i = 0; i < resultList.size(); i++) {
Object[] element = resultList.get(i);
Map<Object, Object> viewRootResult = collections.get(element[elementViewIndex]);
if (viewRootResult == null) {
viewRootResult = new HashMap<>();
collections.put(element[elementViewIndex], viewRootResult);
}
if (this.containerAccumulator == null) {
viewRootResult.put(element[elementKeyIndex], element[valueIndex]);
} else {
Object result = viewRootResult.get(element[elementKeyIndex]);
if (result == null) {
result = createDefaultResult();
viewRootResult.put(element[elementKeyIndex], result);
}
Object indexObject = null;
if (indexCorrelator != null || indexExpression != null) {
indexObject = element[elementKeyIndex + 1];
}
this.containerAccumulator.add(result, indexObject, element[valueIndex], isRecording());
}
}
this.collections = collections;
}
use of com.blazebit.persistence.spi.JpaProvider in project blaze-persistence by Blazebit.
the class EntityViewUpdaterImpl method createSingularAttributeFlusher.
private DirtyAttributeFlusher<?, ?, ?> createSingularAttributeFlusher(EntityViewManagerImpl evm, Map<Object, EntityViewUpdaterImpl> localCache, ManagedViewTypeImplementor<?> viewType, AbstractMethodAttribute<?, ?> attribute, EntityViewUpdaterImpl owner, String ownerMapping) {
EntityMetamodel entityMetamodel = evm.getMetamodel().getEntityMetamodel();
Class<?> entityClass = viewType.getEntityClass();
String attributeName = attribute.getName();
String attributeMapping = attribute.getMapping();
AttributeAccessor entityAttributeAccessor = Accessors.forEntityMapping(evm, attribute);
String attributeLocation = attribute.getLocation();
boolean cascadePersist = attribute.isPersistCascaded();
boolean cascadeUpdate = attribute.isUpdateCascaded();
boolean cascadeDelete = attribute.isDeleteCascaded();
boolean viewOnlyDeleteCascaded = cascadeDelete && !entityMetamodel.getManagedType(ExtendedManagedType.class, entityClass).getAttribute(attributeMapping).isDeleteCascaded();
boolean optimisticLockProtected = attribute.isOptimisticLockProtected();
Set<Type<?>> readOnlyAllowedSubtypes = attribute.getReadOnlyAllowedSubtypes();
Set<Type<?>> persistAllowedSubtypes = attribute.getPersistCascadeAllowedSubtypes();
Set<Type<?>> updateAllowedSubtypes = attribute.getUpdateCascadeAllowedSubtypes();
JpaProvider jpaProvider = evm.getJpaProvider();
if (attribute.isSubview()) {
boolean shouldFlushUpdates = cascadeUpdate && !updateAllowedSubtypes.isEmpty();
boolean shouldFlushPersists = cascadePersist && !persistAllowedSubtypes.isEmpty();
ManagedViewTypeImplementor<?> subviewType = (ManagedViewTypeImplementor<?>) ((com.blazebit.persistence.view.metamodel.SingularAttribute<?, ?>) attribute).getType();
boolean passThrough = false;
if (attribute.isUpdatable() || shouldFlushUpdates || (passThrough = shouldPassThrough(evm, viewType, attribute))) {
// TODO: shouldn't this be done for any flat view? or are updatable flat views for entity types disallowed?
if (!(subviewType.getJpaManagedType() instanceof EntityType<?>)) {
AttributeAccessor viewAttributeAccessor = Accessors.forViewAttribute(evm, attribute, true);
// A singular attribute where the subview refers to an embeddable type
EmbeddableUpdaterBasedViewToEntityMapper viewToEntityMapper = new EmbeddableUpdaterBasedViewToEntityMapper(attributeLocation, evm, subviewType.getJavaType(), readOnlyAllowedSubtypes, persistAllowedSubtypes, updateAllowedSubtypes, EntityLoaders.referenceLoaderForAttribute(evm, localCache, subviewType, attribute), shouldFlushPersists, null, owner == null ? this : owner, ownerMapping == null ? attributeMapping : ownerMapping + "." + attributeMapping, localCache);
CompositeAttributeFlusher nestedFlusher = (CompositeAttributeFlusher) viewToEntityMapper.getFullGraphNode();
boolean supportsQueryFlush = nestedFlusher.supportsQueryFlush() && jpaProvider.supportsUpdateSetEmbeddable();
String parameterName;
String updateFragment;
if (supportsQueryFlush) {
parameterName = attributeName;
updateFragment = attributeMapping;
} else {
parameterName = attributeName + "_";
updateFragment = attributeMapping + ".";
}
return new EmbeddableAttributeFlusher<>(attributeName, attributeMapping, updateFragment, parameterName, optimisticLockProtected, passThrough, supportsQueryFlush, entityAttributeAccessor, viewAttributeAccessor, viewToEntityMapper);
} else {
// Subview refers to entity type
ViewTypeImplementor<?> attributeViewType = (ViewTypeImplementor<?>) subviewType;
InitialValueAttributeAccessor viewAttributeAccessor = Accessors.forMutableViewAttribute(evm, attribute);
AttributeAccessor subviewIdAccessor;
InverseFlusher<Object> inverseFlusher = InverseFlusher.forAttribute(evm, localCache, viewType, attribute, TypeDescriptor.forType(evm, localCache, this, attribute, subviewType, owner, ownerMapping), owner, ownerMapping);
InverseRemoveStrategy inverseRemoveStrategy = attribute.getInverseRemoveStrategy();
ManagedType<?> ownerEntityType = owner == null ? viewType.getJpaManagedType() : owner.managedViewType.getJpaManagedType();
ViewToEntityMapper viewToEntityMapper;
boolean fetch = shouldFlushUpdates;
String parameterName = null;
String attributeElementIdMapping;
if (ownerEntityType instanceof EntityType<?> && attribute.getMapping() != null) {
ExtendedManagedType<?> extendedManagedType = evm.getMetamodel().getEntityMetamodel().getManagedType(ExtendedManagedType.class, attributeViewType.getEntityClass());
attributeElementIdMapping = TypeDescriptor.getAttributeElementIdentifier(evm, (EntityType<?>) ownerEntityType, attribute.getName(), ownerMapping, attribute.getMapping(), extendedManagedType.getType());
} else {
attributeElementIdMapping = ((MappingAttribute<?, ?>) attributeViewType.getIdAttribute()).getMapping();
}
subviewIdAccessor = Accessors.forSubviewAssociationId(evm, attributeViewType, attributeElementIdMapping, true);
Attribute<?, ?> attributeIdAttribute = attributeViewType.getJpaManagedType().getAttribute(attributeElementIdMapping);
javax.persistence.metamodel.Type<?> attributeIdAttributeType = entityMetamodel.type(JpaMetamodelUtils.resolveFieldClass(attributeViewType.getEntityClass(), attributeIdAttribute));
List<String> idComponentMappings;
boolean requiresComponentWiseSetInUpdate = true;
if (requiresComponentWiseSetInUpdate && attributeIdAttributeType instanceof EmbeddableType<?>) {
// If the identifier used for the association is an embeddable, we must collect the individual attribute components since updates don't work on embeddables directly
Set<Attribute<?, ?>> idAttributeComponents = (Set<Attribute<?, ?>>) ((EmbeddableType<?>) attributeIdAttributeType).getAttributes();
idComponentMappings = new ArrayList<>(idAttributeComponents.size());
for (Attribute<?, ?> idAttributeComponent : idAttributeComponents) {
idComponentMappings.add(attributeMapping + "." + attributeElementIdMapping + "." + idAttributeComponent.getName());
}
} else {
idComponentMappings = Collections.singletonList(attributeMapping + "." + attributeElementIdMapping);
}
String[] idAttributeMappings = idComponentMappings.toArray(new String[idComponentMappings.size()]);
if (attribute.isUpdatable() && ownerEntityType instanceof EntityType<?>) {
viewToEntityMapper = createViewToEntityMapper(attributeLocation, evm, localCache, (EntityType<?>) ownerEntityType, attributeName, attributeMapping, attributeViewType, cascadePersist, cascadeUpdate, readOnlyAllowedSubtypes, persistAllowedSubtypes, updateAllowedSubtypes, EntityLoaders.referenceLoaderForAttribute(evm, localCache, attributeViewType, attribute.getViewTypes(), attributeElementIdMapping), owner, ownerMapping);
parameterName = attributeName;
} else {
String elementIdentifier;
if (ownerEntityType instanceof EntityType<?>) {
elementIdentifier = TypeDescriptor.getAttributeElementIdentifier(evm, (EntityType<?>) ownerEntityType, attributeName, ownerMapping, attributeMapping, attributeViewType.getJpaManagedType());
} else {
elementIdentifier = null;
}
AttributeAccessor entityIdAccessor = Accessors.forEntityMapping(evm, attributeViewType.getEntityClass(), elementIdentifier);
if (shouldFlushUpdates) {
viewToEntityMapper = new UpdaterBasedViewToEntityMapper(attributeLocation, evm, subviewType.getJavaType(), readOnlyAllowedSubtypes, persistAllowedSubtypes, updateAllowedSubtypes, EntityLoaders.referenceLoaderForAttribute(evm, localCache, subviewType, attribute), subviewIdAccessor, entityIdAccessor, shouldFlushPersists, owner, ownerMapping, localCache);
} else if (!shouldFlushPersists && shouldPassThrough(evm, viewType, attribute)) {
viewToEntityMapper = new LoadOnlyViewToEntityMapper(EntityLoaders.referenceLoaderForAttribute(evm, localCache, subviewType, attribute), subviewIdAccessor, entityIdAccessor);
} else {
viewToEntityMapper = new LoadOrPersistViewToEntityMapper(attributeLocation, evm, subviewType.getJavaType(), readOnlyAllowedSubtypes, persistAllowedSubtypes, updateAllowedSubtypes, EntityLoaders.referenceLoaderForAttribute(evm, localCache, subviewType, attribute), subviewIdAccessor, entityIdAccessor, shouldFlushPersists, owner, ownerMapping, localCache);
}
}
return new SubviewAttributeFlusher<>(attributeName, attributeMapping, optimisticLockProtected, attribute.isUpdatable(), cascadeDelete, attribute.isOrphanRemoval(), viewOnlyDeleteCascaded, subviewType.getConverter(), fetch, idAttributeMappings, parameterName, passThrough, owner != null, entityAttributeAccessor, viewAttributeAccessor, subviewIdAccessor, viewToEntityMapper, inverseFlusher, inverseRemoveStrategy);
}
} else {
return null;
}
} else {
BasicTypeImpl<?> attributeType = (BasicTypeImpl<?>) ((com.blazebit.persistence.view.metamodel.SingularAttribute<?, ?>) attribute).getType();
TypeDescriptor elementDescriptor = TypeDescriptor.forType(evm, localCache, this, attribute, attributeType, owner, ownerMapping);
// Basic attributes like String, Integer but also JPA managed types
boolean updatable = attribute.isUpdatable();
if (updatable || elementDescriptor.shouldFlushMutations() || shouldPassThrough(evm, viewType, attribute)) {
// Basic attributes can normally be updated by queries
InverseFlusher<Object> inverseFlusher = InverseFlusher.forAttribute(evm, localCache, viewType, attribute, elementDescriptor, owner, ownerMapping);
InverseRemoveStrategy inverseRemoveStrategy = attribute.getInverseRemoveStrategy();
String parameterName = attributeName;
String updateFragment = attributeMapping;
ManagedType<?> ownerEntityType = owner == null ? viewType.getJpaManagedType() : owner.managedViewType.getJpaManagedType();
UnmappedBasicAttributeCascadeDeleter deleter;
if (elementDescriptor.isJpaEntity() && cascadeDelete && ownerEntityType instanceof EntityType<?>) {
String elementIdAttributeName = TypeDescriptor.getAttributeElementIdentifier(evm, (EntityType<?>) ownerEntityType, attributeName, ownerMapping, attributeMapping, attributeType.getManagedType());
deleter = new UnmappedBasicAttributeCascadeDeleter(evm, attributeName, entityMetamodel.getManagedType(ExtendedManagedType.class, entityClass).getAttribute(attributeMapping), attributeMapping + "." + elementIdAttributeName, false);
} else {
deleter = null;
}
// When wanting to read the actual value of non-updatable attributes or writing values to attributes we need the view attribute accessor
// Whenever we merge or persist, we are going to need that
AttributeAccessor viewAttributeAccessor;
if (elementDescriptor.shouldFlushMutations()) {
viewAttributeAccessor = Accessors.forMutableViewAttribute(evm, attribute);
} else {
viewAttributeAccessor = Accessors.forViewAttribute(evm, attribute, true);
}
Map.Entry<AttributeAccessor, BasicAttributeFlusher>[] componentFlusherEntries = null;
if (elementDescriptor.isJpaEmbeddable()) {
if (!jpaProvider.supportsUpdateSetEmbeddable()) {
Set<Attribute<?, ?>> attributes = (Set<Attribute<?, ?>>) attributeType.getManagedType().getAttributes();
Map<AttributeAccessor, BasicAttributeFlusher> componentFlushers = new HashMap<>(attributes.size());
buildComponentFlushers(evm, viewType.getEntityClass(), attributeType.getJavaType(), attributeName + "_", attributeMapping + ".", "", attributes, componentFlushers);
componentFlusherEntries = componentFlushers.entrySet().toArray(new Map.Entry[componentFlushers.size()]);
}
}
return new BasicAttributeFlusher<>(attributeName, attributeMapping, true, optimisticLockProtected, updatable, cascadeDelete, attribute.isOrphanRemoval(), viewOnlyDeleteCascaded, componentFlusherEntries, elementDescriptor, updateFragment, parameterName, entityAttributeAccessor, viewAttributeAccessor, deleter, inverseFlusher, inverseRemoveStrategy);
} else {
return null;
}
}
}
Aggregations