use of io.micronaut.data.exceptions.MappingException in project micronaut-data by micronaut-projects.
the class MongoQueryBuilder method resolveJoinTableAssociatedFields.
@NonNull
private List<String> resolveJoinTableAssociatedFields(AnnotationMetadata annotationMetadata, boolean associationOwner, PersistentEntity entity, NamingStrategy namingStrategy) {
List<String> joinColumns = getJoinedFields(annotationMetadata, associationOwner, "referencedColumnName");
if (!joinColumns.isEmpty()) {
return joinColumns;
}
PersistentProperty identity = entity.getIdentity();
if (identity == null) {
throw new MappingException("Cannot have a foreign key association without an ID on entity: " + entity.getName());
}
List<String> fields = new ArrayList<>();
traversePersistentProperties(identity, (associations, property) -> {
fields.add(asPath(associations, property));
});
return fields;
}
use of io.micronaut.data.exceptions.MappingException in project micronaut-data by micronaut-projects.
the class MongoQueryBuilder method addLookups.
private void addLookups(Collection<JoinPath> joins, QueryState queryState) {
if (joins.isEmpty()) {
return;
}
List<String> joined = joins.stream().map(JoinPath::getPath).sorted((o1, o2) -> Comparator.comparingInt(String::length).thenComparing(String::compareTo).compare(o1, o2)).collect(Collectors.toList());
for (String join : joined) {
StringJoiner rootPath = new StringJoiner(".");
StringJoiner currentEntityPath = new StringJoiner(".");
LookupsStage currentLookup = queryState.rootLookups;
for (String path : StringUtils.splitOmitEmptyStrings(join, '.')) {
rootPath.add(path);
currentEntityPath.add(path);
String thisPath = currentEntityPath.toString();
if (currentLookup.subLookups.containsKey(thisPath)) {
currentLookup = currentLookup.subLookups.get(path);
currentEntityPath = new StringJoiner(".");
continue;
}
PersistentPropertyPath propertyPath = currentLookup.persistentEntity.getPropertyPath(thisPath);
PersistentProperty property = propertyPath.getProperty();
if (!(property instanceof Association)) {
continue;
}
Association association = (Association) property;
if (association.getKind() == Relation.Kind.EMBEDDED) {
continue;
}
LookupsStage lookupStage = new LookupsStage(association.getAssociatedEntity());
List<Map<String, Object>> pipeline = currentLookup.pipeline;
Optional<Association> inverseSide = association.getInverseSide().map(Function.identity());
PersistentEntity persistentEntity = association.getOwner();
String joinedCollectionName = association.getAssociatedEntity().getPersistedName();
String ownerCollectionName = persistentEntity.getPersistedName();
if (association.getKind() == Relation.Kind.MANY_TO_MANY || association.isForeignKey() && !inverseSide.isPresent()) {
PersistentEntity associatedEntity = association.getAssociatedEntity();
PersistentEntity associationOwner = association.getOwner();
// JOIN TABLE
PersistentProperty identity = associatedEntity.getIdentity();
if (identity == null) {
throw new IllegalArgumentException("Associated entity [" + associatedEntity.getName() + "] defines no ID. Cannot join.");
}
final PersistentProperty associatedId = associationOwner.getIdentity();
if (associatedId == null) {
throw new MappingException("Cannot join on entity [" + associationOwner.getName() + "] that has no declared ID");
}
Association owningAssociation = inverseSide.orElse(association);
boolean isAssociationOwner = !association.getInverseSide().isPresent();
NamingStrategy namingStrategy = associationOwner.getNamingStrategy();
AnnotationMetadata annotationMetadata = owningAssociation.getAnnotationMetadata();
List<String> ownerJoinFields = resolveJoinTableAssociatedFields(annotationMetadata, isAssociationOwner, associationOwner, namingStrategy);
List<String> ownerJoinCollectionFields = resolveJoinTableJoinFields(annotationMetadata, isAssociationOwner, associationOwner, namingStrategy);
List<String> associationJoinFields = resolveJoinTableAssociatedFields(annotationMetadata, !isAssociationOwner, associatedEntity, namingStrategy);
List<String> associationJoinCollectionFields = resolveJoinTableJoinFields(annotationMetadata, !isAssociationOwner, associatedEntity, namingStrategy);
String joinCollectionName = namingStrategy.mappedName(owningAssociation);
// String joinTableName = annotationMetadata
// .stringValue(ANN_JOIN_TABLE, "name")
// .orElseGet(() -> namingStrategy.mappedName(association));
List<Map<String, Object>> joinCollectionLookupPipeline = new ArrayList<>();
pipeline.add(lookup(joinCollectionName, "_id", ownerCollectionName, joinCollectionLookupPipeline, thisPath));
joinCollectionLookupPipeline.add(lookup(joinedCollectionName, joinedCollectionName, "_id", lookupStage.pipeline, joinedCollectionName));
joinCollectionLookupPipeline.add(unwind("$" + joinedCollectionName, true));
joinCollectionLookupPipeline.add(singletonMap("$replaceRoot", singletonMap("newRoot", "$" + joinedCollectionName)));
} else {
String currentPath = asPath(propertyPath.getAssociations(), propertyPath.getProperty());
if (association.isForeignKey()) {
String mappedBy = association.getAnnotationMetadata().stringValue(Relation.class, "mappedBy").orElseThrow(IllegalStateException::new);
PersistentPropertyPath mappedByPath = association.getAssociatedEntity().getPropertyPath(mappedBy);
if (mappedByPath == null) {
throw new IllegalStateException("Cannot find mapped path: " + mappedBy);
}
if (!(mappedByPath.getProperty() instanceof Association)) {
throw new IllegalStateException("Expected association as a mapped path: " + mappedBy);
}
List<String> localMatchFields = new ArrayList<>();
List<String> foreignMatchFields = new ArrayList<>();
traversePersistentProperties(currentLookup.persistentEntity.getIdentity(), (associations, p) -> {
String fieldPath = asPath(associations, p);
localMatchFields.add(fieldPath);
});
List<Association> mappedAssociations = new ArrayList<>(mappedByPath.getAssociations());
mappedAssociations.add((Association) mappedByPath.getProperty());
traversePersistentProperties(mappedAssociations, currentLookup.persistentEntity.getIdentity(), (associations, p) -> {
String fieldPath = asPath(associations, p);
foreignMatchFields.add(fieldPath);
});
pipeline.add(lookup(joinedCollectionName, localMatchFields, foreignMatchFields, lookupStage.pipeline, currentPath));
} else {
List<Association> mappedAssociations = new ArrayList<>(propertyPath.getAssociations());
mappedAssociations.add((Association) propertyPath.getProperty());
List<String> localMatchFields = new ArrayList<>();
List<String> foreignMatchFields = new ArrayList<>();
PersistentProperty identity = lookupStage.persistentEntity.getIdentity();
if (identity == null) {
throw new IllegalStateException("Null identity of persistent entity: " + lookupStage.persistentEntity);
}
traversePersistentProperties(mappedAssociations, identity, (associations, p) -> {
String fieldPath = asPath(associations, p);
localMatchFields.add(fieldPath);
});
traversePersistentProperties(identity, (associations, p) -> {
String fieldPath = asPath(associations, p);
foreignMatchFields.add(fieldPath);
});
pipeline.add(lookup(joinedCollectionName, localMatchFields, foreignMatchFields, lookupStage.pipeline, currentPath));
}
if (association.getKind().isSingleEnded()) {
pipeline.add(unwind("$" + currentPath, true));
}
}
currentLookup.subLookups.put(currentEntityPath.toString(), lookupStage);
}
queryState.joinPaths.add(join);
}
}
use of io.micronaut.data.exceptions.MappingException in project micronaut-data by micronaut-projects.
the class SqlQueryBuilder method addTypeToColumn.
private String addTypeToColumn(PersistentProperty prop, String column, boolean required) {
if (prop instanceof Association) {
throw new IllegalStateException("Association is not supported here");
}
AnnotationMetadata annotationMetadata = prop.getAnnotationMetadata();
String definition = annotationMetadata.stringValue(MappedProperty.class, "definition").orElse(null);
DataType dataType = prop.getDataType();
if (definition != null) {
return column + " " + definition;
}
OptionalInt precision = annotationMetadata.intValue("javax.persistence.Column", "precision");
OptionalInt scale = annotationMetadata.intValue("javax.persistence.Column", "scale");
switch(dataType) {
case STRING:
int stringLength = annotationMetadata.findAnnotation("javax.validation.constraints.Size$List").flatMap(v -> {
Optional value = v.getValue(AnnotationValue.class);
return (Optional<AnnotationValue<Annotation>>) value;
}).map(v -> v.intValue("max")).orElseGet(() -> annotationMetadata.intValue("javax.persistence.Column", "length")).orElse(255);
column += " VARCHAR(" + stringLength + ")";
if (required) {
column += " NOT NULL";
}
break;
case UUID:
if (dialect == Dialect.ORACLE || dialect == Dialect.MYSQL) {
column += " VARCHAR(36)";
} else if (dialect == Dialect.SQL_SERVER) {
column += " UNIQUEIDENTIFIER";
} else {
column += " UUID";
}
if (required) {
column += " NOT NULL";
}
break;
case BOOLEAN:
if (dialect == Dialect.ORACLE) {
column += " NUMBER(3)";
} else if (dialect == Dialect.SQL_SERVER) {
column += " BIT NOT NULL";
} else {
column += " BOOLEAN";
if (required) {
column += " NOT NULL";
}
}
break;
case TIMESTAMP:
if (dialect == Dialect.ORACLE) {
column += " TIMESTAMP";
if (required) {
column += " NOT NULL";
}
} else if (dialect == Dialect.SQL_SERVER) {
// sql server timestamp is an internal type, use datetime instead
column += " DATETIME2";
if (required) {
column += " NOT NULL";
}
} else if (dialect == Dialect.MYSQL) {
// mysql doesn't allow timestamp without default
column += " TIMESTAMP(6) DEFAULT NOW(6)";
} else {
column += " TIMESTAMP";
if (required) {
column += " NOT NULL";
}
}
break;
case DATE:
column += " DATE";
if (required) {
column += " NOT NULL";
}
break;
case LONG:
if (dialect == Dialect.ORACLE) {
column += " NUMBER(19)";
} else {
column += " BIGINT";
}
if (required) {
column += " NOT NULL";
}
break;
case CHARACTER:
column += " CHAR(1)";
if (required) {
column += " NOT NULL";
}
break;
case INTEGER:
if (precision.isPresent()) {
String numericName = dialect == Dialect.ORACLE ? "NUMBER" : "NUMERIC";
column += " " + numericName + "(" + precision.getAsInt() + ")";
} else if (dialect == Dialect.ORACLE) {
column += " NUMBER(10)";
} else if (dialect == Dialect.POSTGRES) {
column += " INTEGER";
} else {
column += " INT";
}
if (required) {
column += " NOT NULL";
}
break;
case BIGDECIMAL:
if (precision.isPresent()) {
if (scale.isPresent()) {
String numericName = dialect == Dialect.ORACLE ? "NUMBER" : "NUMERIC";
column += " " + numericName + "(" + precision.getAsInt() + "," + scale.getAsInt() + ")";
} else {
column += " FLOAT(" + precision.getAsInt() + ")";
}
} else if (dialect == Dialect.ORACLE) {
column += " FLOAT(126)";
} else {
column += " DECIMAL";
}
if (required) {
column += " NOT NULL";
}
break;
case FLOAT:
if (precision.isPresent()) {
if (scale.isPresent()) {
String numericName = dialect == Dialect.ORACLE ? "NUMBER" : "NUMERIC";
column += " " + numericName + "(" + precision.getAsInt() + "," + scale.getAsInt() + ")";
} else {
column += " FLOAT(" + precision.getAsInt() + ")";
}
} else if (dialect == Dialect.ORACLE || dialect == Dialect.SQL_SERVER) {
column += " FLOAT(53)";
} else if (dialect == Dialect.POSTGRES) {
column += " REAL";
} else {
column += " FLOAT";
}
if (required) {
column += " NOT NULL";
}
break;
case BYTE_ARRAY:
if (dialect == Dialect.POSTGRES) {
column += " BYTEA";
} else if (dialect == Dialect.SQL_SERVER) {
column += " VARBINARY(MAX)";
} else if (dialect == Dialect.ORACLE) {
column += " BLOB";
} else {
column += " BLOB";
}
if (required) {
column += " NOT NULL";
}
break;
case DOUBLE:
if (precision.isPresent()) {
if (scale.isPresent()) {
String numericName = dialect == Dialect.ORACLE ? "NUMBER" : "NUMERIC";
column += " " + numericName + "(" + precision.getAsInt() + "," + scale.getAsInt() + ")";
} else {
column += " FLOAT(" + precision.getAsInt() + ")";
}
} else if (dialect == Dialect.ORACLE) {
column += " FLOAT(23)";
} else if (dialect == Dialect.MYSQL || dialect == Dialect.H2) {
column += " DOUBLE";
} else {
column += " DOUBLE PRECISION";
}
if (required) {
column += " NOT NULL";
}
break;
case SHORT:
case BYTE:
if (dialect == Dialect.ORACLE) {
column += " NUMBER(5)";
} else if (dialect == Dialect.POSTGRES) {
column += " SMALLINT";
} else {
column += " TINYINT";
}
if (required) {
column += " NOT NULL";
}
break;
case JSON:
switch(dialect) {
case POSTGRES:
column += " JSONB";
break;
case SQL_SERVER:
column += " NVARCHAR(MAX)";
break;
case ORACLE:
column += " CLOB";
break;
default:
column += " JSON";
break;
}
if (required) {
column += " NOT NULL";
}
break;
case STRING_ARRAY:
case CHARACTER_ARRAY:
if (dialect == Dialect.H2) {
column += " ARRAY";
} else {
column += " VARCHAR(255) ARRAY";
}
if (required) {
column += " NOT NULL";
}
break;
case SHORT_ARRAY:
if (dialect == Dialect.H2) {
column += " ARRAY";
} else if (dialect == Dialect.POSTGRES) {
column += " SMALLINT ARRAY";
} else {
column += " TINYINT ARRAY";
}
if (required) {
column += " NOT NULL";
}
break;
case INTEGER_ARRAY:
if (dialect == Dialect.H2) {
column += " ARRAY";
} else if (dialect == Dialect.POSTGRES) {
column += " INTEGER ARRAY";
} else {
column += " INT ARRAY";
}
if (required) {
column += " NOT NULL";
}
break;
case LONG_ARRAY:
if (dialect == Dialect.H2) {
column += " ARRAY";
} else {
column += " BIGINT ARRAY";
}
if (required) {
column += " NOT NULL";
}
break;
case FLOAT_ARRAY:
if (dialect == Dialect.H2) {
column += " ARRAY";
} else if (dialect == Dialect.POSTGRES) {
column += " REAL ARRAY";
} else {
column += " FLOAT ARRAY";
}
if (required) {
column += " NOT NULL";
}
break;
case DOUBLE_ARRAY:
if (dialect == Dialect.H2) {
column += " ARRAY";
} else if (dialect == Dialect.POSTGRES) {
column += " DOUBLE PRECISION ARRAY";
} else {
column += " DOUBLE ARRAY";
}
if (required) {
column += " NOT NULL";
}
break;
case BOOLEAN_ARRAY:
if (dialect == Dialect.H2) {
column += " ARRAY";
} else {
column += " BOOLEAN ARRAY";
}
if (required) {
column += " NOT NULL";
}
break;
default:
if (prop.isEnum()) {
column += " VARCHAR(255)";
if (required) {
column += " NOT NULL";
}
break;
} else if (prop.isAssignable(Clob.class)) {
if (dialect == Dialect.POSTGRES) {
column += " TEXT";
} else {
column += " CLOB";
}
if (required) {
column += " NOT NULL";
}
break;
} else if (prop.isAssignable(Blob.class)) {
if (dialect == Dialect.POSTGRES) {
column += " BYTEA";
} else {
column += " BLOB";
}
if (required) {
column += " NOT NULL";
}
break;
} else {
throw new MappingException("Unable to create table column for property [" + prop.getName() + "] of entity [" + prop.getOwner().getName() + "] with unknown data type: " + dataType);
}
}
return column;
}
use of io.micronaut.data.exceptions.MappingException in project micronaut-data by micronaut-projects.
the class SqlQueryBuilder method buildJoin.
private void buildJoin(String joinType, StringBuilder sb, QueryState queryState, List<Association> joinAssociationsPath, String joinAlias, Association association, PersistentEntity associatedEntity, PersistentEntity associationOwner, String currentJoinAlias) {
final boolean escape = shouldEscape(associationOwner);
String mappedBy = association.getAnnotationMetadata().stringValue(Relation.class, "mappedBy").orElse(null);
if (association.getKind() == Relation.Kind.MANY_TO_MANY || association.isForeignKey() && StringUtils.isEmpty(mappedBy)) {
PersistentProperty identity = associatedEntity.getIdentity();
if (identity == null) {
throw new IllegalArgumentException("Associated entity [" + associatedEntity.getName() + "] defines no ID. Cannot join.");
}
final PersistentProperty associatedId = associationOwner.getIdentity();
if (associatedId == null) {
throw new MappingException("Cannot join on entity [" + associationOwner.getName() + "] that has no declared ID");
}
Optional<Association> inverseSide = association.getInverseSide().map(Function.identity());
Association owningAssociation = inverseSide.orElse(association);
boolean isAssociationOwner = !association.getInverseSide().isPresent();
NamingStrategy namingStrategy = associationOwner.getNamingStrategy();
AnnotationMetadata annotationMetadata = owningAssociation.getAnnotationMetadata();
List<String> ownerJoinColumns = resolveJoinTableAssociatedColumns(annotationMetadata, isAssociationOwner, associationOwner, namingStrategy);
List<String> ownerJoinTableColumns = resolveJoinTableJoinColumns(annotationMetadata, isAssociationOwner, associationOwner, namingStrategy);
List<String> associationJoinColumns = resolveJoinTableAssociatedColumns(annotationMetadata, !isAssociationOwner, associatedEntity, namingStrategy);
List<String> associationJoinTableColumns = resolveJoinTableJoinColumns(annotationMetadata, !isAssociationOwner, associatedEntity, namingStrategy);
if (escape) {
ownerJoinColumns = ownerJoinColumns.stream().map(this::quote).collect(Collectors.toList());
ownerJoinTableColumns = ownerJoinTableColumns.stream().map(this::quote).collect(Collectors.toList());
associationJoinColumns = associationJoinColumns.stream().map(this::quote).collect(Collectors.toList());
associationJoinTableColumns = associationJoinTableColumns.stream().map(this::quote).collect(Collectors.toList());
}
String joinTableName = annotationMetadata.stringValue(ANN_JOIN_TABLE, "name").orElseGet(() -> namingStrategy.mappedName(association));
String joinTableAlias = annotationMetadata.stringValue(ANN_JOIN_TABLE, "alias").orElseGet(() -> currentJoinAlias + joinTableName + "_");
join(sb, queryState.getQueryModel(), joinType, escape ? quote(joinTableName) : joinTableName, joinTableAlias, joinAlias, ownerJoinColumns, ownerJoinTableColumns);
sb.append(SPACE);
join(sb, queryState.getQueryModel(), joinType, getTableName(associatedEntity), currentJoinAlias, joinTableAlias, associationJoinTableColumns, associationJoinColumns);
} else {
if (StringUtils.isNotEmpty(mappedBy)) {
PersistentProperty ownerIdentity = associationOwner.getIdentity();
if (ownerIdentity == null) {
throw new IllegalArgumentException("Associated entity [" + associationOwner + "] defines no ID. Cannot join.");
}
PersistentPropertyPath mappedByPropertyPath = associatedEntity.getPropertyPath(mappedBy);
if (mappedByPropertyPath == null) {
throw new MappingException("Foreign key association with mappedBy references a property that doesn't exist [" + mappedBy + "] of entity: " + associatedEntity.getName());
}
join(sb, joinType, queryState, associatedEntity, associationOwner, joinAlias, currentJoinAlias, joinAssociationsPath, ownerIdentity, mappedByPropertyPath.getAssociations(), mappedByPropertyPath.getProperty());
} else {
PersistentProperty associatedProperty = association.getAssociatedEntity().getIdentity();
if (associatedProperty == null) {
throw new IllegalArgumentException("Associated entity [" + association.getAssociatedEntity().getName() + "] defines no ID. Cannot join.");
}
join(sb, joinType, queryState, associatedEntity, associationOwner, joinAlias, currentJoinAlias, joinAssociationsPath, association, Collections.emptyList(), associatedProperty);
}
}
}
use of io.micronaut.data.exceptions.MappingException in project micronaut-data by micronaut-projects.
the class SqlQueryBuilder method join.
private void join(StringBuilder sb, String joinType, QueryState queryState, PersistentEntity associatedEntity, PersistentEntity associationOwner, String leftTableAlias, String rightTableAlias, List<Association> leftPropertyAssociations, PersistentProperty leftProperty, List<Association> rightPropertyAssociations, PersistentProperty rightProperty) {
final boolean escape = shouldEscape(associationOwner);
List<String> onLeftColumns = new ArrayList<>();
List<String> onRightColumns = new ArrayList<>();
Association association = null;
if (leftProperty instanceof Association) {
association = (Association) leftProperty;
} else if (rightProperty instanceof Association) {
association = (Association) rightProperty;
}
if (association != null) {
Optional<Association> inverse = association.getInverseSide().map(Function.identity());
Association owner = inverse.orElse(association);
boolean isOwner = leftProperty == owner;
AnnotationValue<Annotation> joinColumnsHolder = owner.getAnnotationMetadata().getAnnotation(ANN_JOIN_COLUMNS);
if (joinColumnsHolder != null) {
onLeftColumns.addAll(joinColumnsHolder.getAnnotations("value").stream().map(ann -> ann.stringValue(isOwner ? "name" : "referencedColumnName").orElse(null)).filter(Objects::nonNull).collect(Collectors.toList()));
onRightColumns.addAll(joinColumnsHolder.getAnnotations("value").stream().map(ann -> ann.stringValue(isOwner ? "referencedColumnName" : "name").orElse(null)).filter(Objects::nonNull).collect(Collectors.toList()));
}
}
if (onLeftColumns.isEmpty()) {
traversePersistentProperties(leftProperty, (associations, p) -> {
String column = leftProperty.getOwner().getNamingStrategy().mappedName(merge(leftPropertyAssociations, associations), p);
onLeftColumns.add(column);
});
if (onLeftColumns.isEmpty()) {
throw new MappingException("Cannot join on entity [" + leftProperty.getOwner().getName() + "] that has no declared ID");
}
}
if (onRightColumns.isEmpty()) {
traversePersistentProperties(rightProperty, (associations, p) -> {
String column = rightProperty.getOwner().getNamingStrategy().mappedName(merge(rightPropertyAssociations, associations), p);
onRightColumns.add(column);
});
}
join(sb, queryState.getQueryModel(), joinType, getTableName(associatedEntity), rightTableAlias, leftTableAlias, escape ? onLeftColumns.stream().map(this::quote).collect(Collectors.toList()) : onLeftColumns, escape ? onRightColumns.stream().map(this::quote).collect(Collectors.toList()) : onRightColumns);
}
Aggregations