Search in sources :

Example 21 with PersistentPropertyPath

use of io.micronaut.data.model.PersistentPropertyPath in project micronaut-data by micronaut-projects.

the class AbstractSqlLikeQueryBuilder method appendFunctionProjection.

private void appendFunctionProjection(PersistentEntity entity, String functionName, QueryModel.PropertyProjection propertyProjection, String tableAlias, StringBuilder queryString) {
    PersistentPropertyPath propertyPath = entity.getPropertyPath(propertyProjection.getPropertyName());
    if (propertyPath == null) {
        throw new IllegalArgumentException("Cannot project on non-existent property: " + propertyProjection.getPropertyName());
    }
    String columnName;
    if (computePropertyPaths()) {
        columnName = entity.getNamingStrategy().mappedName(propertyPath.getAssociations(), propertyPath.getProperty());
        if (shouldEscape(entity)) {
            columnName = quote(columnName);
        }
    } else {
        columnName = propertyPath.getPath();
    }
    queryString.append(functionName).append(OPEN_BRACKET).append(tableAlias).append(DOT).append(columnName).append(CLOSE_BRACKET);
}
Also used : PersistentPropertyPath(io.micronaut.data.model.PersistentPropertyPath)

Example 22 with PersistentPropertyPath

use of io.micronaut.data.model.PersistentPropertyPath in project micronaut-data by micronaut-projects.

the class AbstractSqlLikeQueryBuilder method findPropertyInternal.

private QueryPropertyPath findPropertyInternal(QueryState queryState, PersistentEntity entity, String tableAlias, String name, Class criterionType) {
    PersistentPropertyPath propertyPath = entity.getPropertyPath(name);
    if (propertyPath != null) {
        if (propertyPath.getAssociations().isEmpty()) {
            return new QueryPropertyPath(propertyPath, tableAlias);
        }
        Association joinAssociation = null;
        StringJoiner joinPathJoiner = new StringJoiner(".");
        String lastJoinAlias = null;
        for (Association association : propertyPath.getAssociations()) {
            joinPathJoiner.add(association.getName());
            if (association instanceof Embedded) {
                continue;
            }
            if (joinAssociation == null) {
                joinAssociation = association;
                continue;
            }
            if (association != joinAssociation.getAssociatedEntity().getIdentity()) {
                if (!queryState.isAllowJoins()) {
                    throw new IllegalArgumentException("Joins cannot be used in a DELETE or UPDATE operation");
                }
                String joinStringPath = joinPathJoiner.toString();
                if (!queryState.isJoined(joinStringPath)) {
                    throw new IllegalArgumentException("Property is not joined at path: " + joinStringPath);
                }
                lastJoinAlias = joinInPath(queryState, joinStringPath);
                // Continue to look for a joined property
                joinAssociation = association;
            } else {
                // We don't need to join to access the id of the relation
                joinAssociation = null;
            }
        }
        PersistentProperty property = propertyPath.getProperty();
        if (joinAssociation != null) {
            // We don't need to join to access the id of the relation if it is not a foreign key association
            if (property != joinAssociation.getAssociatedEntity().getIdentity() || joinAssociation.isForeignKey()) {
                String joinStringPath = joinPathJoiner.toString();
                if (!queryState.isJoined(joinStringPath)) {
                    throw new IllegalArgumentException("Property is not joined at path: " + joinStringPath);
                }
                if (lastJoinAlias == null) {
                    lastJoinAlias = joinInPath(queryState, joinPathJoiner.toString());
                }
            }
            if (lastJoinAlias != null) {
                // 'joinPath.prop' should be represented as a path of 'prop' with a join alias
                return new QueryPropertyPath(new PersistentPropertyPath(Collections.emptyList(), property, property.getName()), lastJoinAlias);
            }
        }
    } else if (TypeRole.ID.equals(name) && entity.getIdentity() != null) {
        // special case handling for ID
        return new QueryPropertyPath(new PersistentPropertyPath(Collections.emptyList(), entity.getIdentity(), entity.getIdentity().getName()), queryState.getRootAlias());
    }
    if (propertyPath == null) {
        if (criterionType == null || criterionType == Sort.Order.class) {
            throw new IllegalArgumentException("Cannot order on non-existent property path: " + name);
        } else {
            throw new IllegalArgumentException("Cannot use [" + criterionType.getSimpleName() + "] criterion on non-existent property path: " + name);
        }
    }
    return new QueryPropertyPath(propertyPath, tableAlias);
}
Also used : Association(io.micronaut.data.model.Association) PersistentPropertyPath(io.micronaut.data.model.PersistentPropertyPath) Embedded(io.micronaut.data.model.Embedded) PersistentProperty(io.micronaut.data.model.PersistentProperty) StringJoiner(java.util.StringJoiner)

Example 23 with PersistentPropertyPath

use of io.micronaut.data.model.PersistentPropertyPath in project micronaut-data by micronaut-projects.

the class SqlQueryBuilder method buildCreateTableStatements.

/**
 * Builds the create table statement. Designed for testing and not production usage. For production a
 * SQL migration tool such as Flyway or Liquibase is recommended.
 *
 * @param entity The entity
 * @return The tables for the give entity
 */
@Experimental
@NonNull
public String[] buildCreateTableStatements(@NonNull PersistentEntity entity) {
    ArgumentUtils.requireNonNull("entity", entity);
    final String unescapedTableName = getUnescapedTableName(entity);
    String tableName = getTableName(entity);
    boolean escape = shouldEscape(entity);
    PersistentProperty identity = entity.getIdentity();
    List<String> createStatements = new ArrayList<>();
    String schema = entity.getAnnotationMetadata().stringValue(MappedEntity.class, SqlMembers.SCHEMA).orElse(null);
    if (StringUtils.isNotEmpty(schema)) {
        if (escape) {
            schema = quote(schema);
        }
        createStatements.add("CREATE SCHEMA " + schema + ";");
    }
    Collection<Association> foreignKeyAssociations = getJoinTableAssociations(entity);
    NamingStrategy namingStrategy = entity.getNamingStrategy();
    if (CollectionUtils.isNotEmpty(foreignKeyAssociations)) {
        for (Association association : foreignKeyAssociations) {
            StringBuilder joinTableBuilder = new StringBuilder("CREATE TABLE ");
            PersistentEntity associatedEntity = association.getAssociatedEntity();
            Optional<Association> inverseSide = association.getInverseSide().map(Function.identity());
            Association owningAssociation = inverseSide.orElse(association);
            AnnotationMetadata annotationMetadata = owningAssociation.getAnnotationMetadata();
            String joinTableName = annotationMetadata.stringValue(ANN_JOIN_TABLE, "name").orElseGet(() -> namingStrategy.mappedName(association));
            if (escape) {
                joinTableName = quote(joinTableName);
            }
            joinTableBuilder.append(joinTableName).append(" (");
            List<PersistentPropertyPath> leftProperties = new ArrayList<>();
            List<PersistentPropertyPath> rightProperties = new ArrayList<>();
            boolean isAssociationOwner = !inverseSide.isPresent();
            List<String> leftJoinTableColumns = resolveJoinTableJoinColumns(annotationMetadata, isAssociationOwner, entity, namingStrategy);
            List<String> rightJoinTableColumns = resolveJoinTableJoinColumns(annotationMetadata, !isAssociationOwner, association.getAssociatedEntity(), namingStrategy);
            traversePersistentProperties(entity.getIdentity(), (associations, property) -> {
                leftProperties.add(PersistentPropertyPath.of(associations, property, ""));
            });
            traversePersistentProperties(associatedEntity.getIdentity(), (associations, property) -> {
                rightProperties.add(PersistentPropertyPath.of(associations, property, ""));
            });
            if (leftJoinTableColumns.size() == leftProperties.size()) {
                for (int i = 0; i < leftJoinTableColumns.size(); i++) {
                    PersistentPropertyPath pp = leftProperties.get(i);
                    String columnName = leftJoinTableColumns.get(i);
                    if (escape) {
                        columnName = quote(columnName);
                    }
                    joinTableBuilder.append(addTypeToColumn(pp.getProperty(), columnName, true)).append(',');
                }
            } else {
                for (PersistentPropertyPath pp : leftProperties) {
                    String columnName = namingStrategy.mappedJoinTableColumn(entity, pp.getAssociations(), pp.getProperty());
                    if (escape) {
                        columnName = quote(columnName);
                    }
                    joinTableBuilder.append(addTypeToColumn(pp.getProperty(), columnName, true)).append(',');
                }
            }
            if (rightJoinTableColumns.size() == rightProperties.size()) {
                for (int i = 0; i < rightJoinTableColumns.size(); i++) {
                    PersistentPropertyPath pp = rightProperties.get(i);
                    String columnName = rightJoinTableColumns.get(i);
                    if (escape) {
                        columnName = quote(columnName);
                    }
                    joinTableBuilder.append(addTypeToColumn(pp.getProperty(), columnName, true)).append(',');
                }
            } else {
                for (PersistentPropertyPath pp : rightProperties) {
                    String columnName = namingStrategy.mappedJoinTableColumn(entity, pp.getAssociations(), pp.getProperty());
                    if (escape) {
                        columnName = quote(columnName);
                    }
                    joinTableBuilder.append(addTypeToColumn(pp.getProperty(), columnName, true)).append(',');
                }
            }
            joinTableBuilder.setLength(joinTableBuilder.length() - 1);
            joinTableBuilder.append(")");
            if (dialect != Dialect.ORACLE) {
                joinTableBuilder.append(';');
            }
            createStatements.add(joinTableBuilder.toString());
        }
    }
    boolean generatePkAfterColumns = false;
    List<String> primaryColumnsName = new ArrayList<>();
    List<String> columns = new ArrayList<>();
    if (identity != null) {
        List<PersistentPropertyPath> ids = new ArrayList<>();
        traversePersistentProperties(identity, (associations, property) -> {
            ids.add(PersistentPropertyPath.of(associations, property, ""));
        });
        if (ids.size() > 1) {
            generatePkAfterColumns = true;
        }
        boolean finalGeneratePkAfterColumns = generatePkAfterColumns;
        for (PersistentPropertyPath pp : ids) {
            String column = namingStrategy.mappedName(pp.getAssociations(), pp.getProperty());
            if (escape) {
                column = quote(column);
            }
            primaryColumnsName.add(column);
            column = addTypeToColumn(pp.getProperty(), column, isRequired(pp.getAssociations(), pp.getProperty()));
            if (isNotForeign(pp.getAssociations())) {
                column = addGeneratedStatementToColumn(pp.getProperty(), column, !finalGeneratePkAfterColumns);
            }
            columns.add(column);
        }
    }
    PersistentProperty version = entity.getVersion();
    if (version != null) {
        String column = namingStrategy.mappedName(Collections.emptyList(), version);
        if (escape) {
            column = quote(column);
        }
        column = addTypeToColumn(version, column, true);
        columns.add(column);
    }
    BiConsumer<List<Association>, PersistentProperty> addColumn = (associations, property) -> {
        String column = namingStrategy.mappedName(associations, property);
        if (escape) {
            column = quote(column);
        }
        column = addTypeToColumn(property, column, isRequired(associations, property));
        if (isNotForeign(associations)) {
            column = addGeneratedStatementToColumn(property, column, false);
        }
        columns.add(column);
    };
    for (PersistentProperty prop : entity.getPersistentProperties()) {
        traversePersistentProperties(prop, addColumn);
    }
    StringBuilder builder = new StringBuilder("CREATE TABLE ").append(tableName).append(" (");
    builder.append(String.join(",", columns));
    if (generatePkAfterColumns) {
        builder.append(", PRIMARY KEY(").append(String.join(",", primaryColumnsName)).append(')');
    }
    if (dialect == Dialect.ORACLE) {
        builder.append(")");
    } else {
        builder.append(");");
    }
    if (identity != null && identity.isGenerated()) {
        GeneratedValue.Type idGeneratorType = identity.getAnnotationMetadata().enumValue(GeneratedValue.class, GeneratedValue.Type.class).orElseGet(() -> selectAutoStrategy(identity));
        boolean isSequence = idGeneratorType == GeneratedValue.Type.SEQUENCE;
        final String generatedDefinition = identity.getAnnotationMetadata().stringValue(GeneratedValue.class, "definition").orElse(null);
        if (generatedDefinition != null) {
            createStatements.add(generatedDefinition);
        } else if (isSequence) {
            final boolean isSqlServer = dialect == Dialect.SQL_SERVER;
            final String sequenceName = quote(unescapedTableName + SEQ_SUFFIX);
            String createSequenceStmt = "CREATE SEQUENCE " + sequenceName;
            if (isSqlServer) {
                createSequenceStmt += " AS BIGINT";
            }
            createSequenceStmt += " MINVALUE 1 START WITH 1";
            if (dialect == Dialect.ORACLE) {
                createSequenceStmt += " NOCACHE NOCYCLE";
            } else {
                if (isSqlServer) {
                    createSequenceStmt += " INCREMENT BY 1";
                }
            }
            createStatements.add(createSequenceStmt);
        }
    }
    createStatements.add(builder.toString());
    addIndexes(entity, tableName, createStatements);
    return createStatements.toArray(new String[0]);
}
Also used : DataType(io.micronaut.data.model.DataType) SqlMembers(io.micronaut.data.annotation.sql.SqlMembers) Arrays(java.util.Arrays) IDENTITY(io.micronaut.data.annotation.GeneratedValue.Type.IDENTITY) ListIterator(java.util.ListIterator) ArrayUtils(io.micronaut.core.util.ArrayUtils) SEQUENCE(io.micronaut.data.annotation.GeneratedValue.Type.SEQUENCE) MappedProperty(io.micronaut.data.annotation.MappedProperty) GeneratedValue(io.micronaut.data.annotation.GeneratedValue) Locale(java.util.Locale) Map(java.util.Map) QueryResult(io.micronaut.data.model.query.builder.QueryResult) ArgumentUtils(io.micronaut.core.util.ArgumentUtils) PersistentPropertyPath(io.micronaut.data.model.PersistentPropertyPath) PersistentProperty(io.micronaut.data.model.PersistentProperty) Index(io.micronaut.data.annotation.Index) MappingException(io.micronaut.data.exceptions.MappingException) Collection(java.util.Collection) Collectors(java.util.stream.Collectors) Objects(java.util.Objects) AUTO(io.micronaut.data.annotation.GeneratedValue.Type.AUTO) StringUtils(io.micronaut.core.util.StringUtils) AbstractSqlLikeQueryBuilder(io.micronaut.data.model.query.builder.AbstractSqlLikeQueryBuilder) List(java.util.List) Stream(java.util.stream.Stream) UUID(io.micronaut.data.annotation.GeneratedValue.Type.UUID) AnnotationValue(io.micronaut.core.annotation.AnnotationValue) Annotation(java.lang.annotation.Annotation) Optional(java.util.Optional) Experimental(io.micronaut.core.annotation.Experimental) Pattern(java.util.regex.Pattern) Relation(io.micronaut.data.annotation.Relation) IntStream(java.util.stream.IntStream) QueryParameterBinding(io.micronaut.data.model.query.builder.QueryParameterBinding) Join(io.micronaut.data.annotation.Join) Creator(io.micronaut.core.annotation.Creator) QueryModel(io.micronaut.data.model.query.QueryModel) MappedEntity(io.micronaut.data.annotation.MappedEntity) HashMap(java.util.HashMap) OptionalInt(java.util.OptionalInt) Function(java.util.function.Function) ArrayList(java.util.ArrayList) Embedded(io.micronaut.data.model.Embedded) Pageable(io.micronaut.data.model.Pageable) BiConsumer(java.util.function.BiConsumer) Clob(java.sql.Clob) NamingStrategy(io.micronaut.data.model.naming.NamingStrategy) Indexes(io.micronaut.data.annotation.Indexes) NonNull(io.micronaut.core.annotation.NonNull) JoinPath(io.micronaut.data.model.query.JoinPath) Association(io.micronaut.data.model.Association) PersistentEntity(io.micronaut.data.model.PersistentEntity) CollectionUtils(io.micronaut.core.util.CollectionUtils) StringJoiner(java.util.StringJoiner) QueryBuilder(io.micronaut.data.model.query.builder.QueryBuilder) Repository(io.micronaut.data.annotation.Repository) AnnotationMetadata(io.micronaut.core.annotation.AnnotationMetadata) Blob(java.sql.Blob) Collections(java.util.Collections) MappedEntity(io.micronaut.data.annotation.MappedEntity) PersistentEntity(io.micronaut.data.model.PersistentEntity) ArrayList(java.util.ArrayList) PersistentProperty(io.micronaut.data.model.PersistentProperty) PersistentPropertyPath(io.micronaut.data.model.PersistentPropertyPath) AnnotationMetadata(io.micronaut.core.annotation.AnnotationMetadata) GeneratedValue(io.micronaut.data.annotation.GeneratedValue) NamingStrategy(io.micronaut.data.model.naming.NamingStrategy) DataType(io.micronaut.data.model.DataType) Association(io.micronaut.data.model.Association) List(java.util.List) ArrayList(java.util.ArrayList) Experimental(io.micronaut.core.annotation.Experimental) NonNull(io.micronaut.core.annotation.NonNull)

Example 24 with PersistentPropertyPath

use of io.micronaut.data.model.PersistentPropertyPath in project micronaut-data by micronaut-projects.

the class AbstractCriteriaBuilder method parameter.

@Override
@NonNull
public <T> ParameterExpression<T> parameter(@NonNull Class<T> paramClass, @NonNull String name) {
    return new ParameterExpressionImpl<T>(paramClass, name) {

        @Override
        public QueryParameterBinding bind(BindingContext bindingContext) {
            String name = bindingContext.getName() == null ? String.valueOf(bindingContext.getIndex()) : bindingContext.getName();
            PersistentPropertyPath outgoingQueryParameterProperty = bindingContext.getOutgoingQueryParameterProperty();
            return new QueryParameterBinding() {

                @Override
                public String getKey() {
                    return name;
                }

                @Override
                public DataType getDataType() {
                    return outgoingQueryParameterProperty.getProperty().getDataType();
                }

                @Override
                public String[] getPropertyPath() {
                    return asStringPath(outgoingQueryParameterProperty.getAssociations(), outgoingQueryParameterProperty.getProperty());
                }
            };
        }
    };
}
Also used : QueryParameterBinding(io.micronaut.data.model.query.builder.QueryParameterBinding) PersistentPropertyPath(io.micronaut.data.model.PersistentPropertyPath) NonNull(io.micronaut.core.annotation.NonNull)

Example 25 with PersistentPropertyPath

use of io.micronaut.data.model.PersistentPropertyPath in project micronaut-data by micronaut-projects.

the class AbstractCriteriaBuilder method parameter.

@Override
@NonNull
public <T> ParameterExpression<T> parameter(@NonNull Class<T> paramClass) {
    return new ParameterExpressionImpl<T>(paramClass, null) {

        @Override
        public QueryParameterBinding bind(BindingContext bindingContext) {
            String name = bindingContext.getName() == null ? String.valueOf(bindingContext.getIndex()) : bindingContext.getName();
            PersistentPropertyPath outgoingQueryParameterProperty = bindingContext.getOutgoingQueryParameterProperty();
            return new QueryParameterBinding() {

                @Override
                public String getKey() {
                    return name;
                }

                @Override
                public DataType getDataType() {
                    return outgoingQueryParameterProperty.getProperty().getDataType();
                }

                @Override
                public String[] getPropertyPath() {
                    return asStringPath(outgoingQueryParameterProperty.getAssociations(), outgoingQueryParameterProperty.getProperty());
                }

                @Override
                public boolean isExpandable() {
                    return bindingContext.isExpandable();
                }
            };
        }
    };
}
Also used : QueryParameterBinding(io.micronaut.data.model.query.builder.QueryParameterBinding) PersistentPropertyPath(io.micronaut.data.model.PersistentPropertyPath) NonNull(io.micronaut.core.annotation.NonNull)

Aggregations

PersistentPropertyPath (io.micronaut.data.model.PersistentPropertyPath)26 Association (io.micronaut.data.model.Association)10 QueryParameterBinding (io.micronaut.data.model.query.builder.QueryParameterBinding)10 PersistentProperty (io.micronaut.data.model.PersistentProperty)8 NonNull (io.micronaut.core.annotation.NonNull)7 DataType (io.micronaut.data.model.DataType)7 Map (java.util.Map)7 ParameterElement (io.micronaut.inject.ast.ParameterElement)6 ArrayList (java.util.ArrayList)6 Embedded (io.micronaut.data.model.Embedded)5 QueryResult (io.micronaut.data.model.query.builder.QueryResult)5 Arrays (java.util.Arrays)5 List (java.util.List)5 StringJoiner (java.util.StringJoiner)5 AnnotationMetadata (io.micronaut.core.annotation.AnnotationMetadata)4 BindingParameter (io.micronaut.data.model.query.BindingParameter)4 QueryModel (io.micronaut.data.model.query.QueryModel)4 MatchFailedException (io.micronaut.data.processor.visitors.MatchFailedException)4 ClassElement (io.micronaut.inject.ast.ClassElement)4 Collections (java.util.Collections)4