use of io.micronaut.data.model.query.JoinPath in project micronaut-data by micronaut-projects.
the class DefaultStoredQuery method getJoinFetchPaths.
@NonNull
@Override
public Set<JoinPath> getJoinFetchPaths() {
if (joinFetchPaths == null) {
Set<JoinPath> set = method.getAnnotationValuesByType(Join.class).stream().filter(this::isJoinFetch).map(av -> {
String path = av.stringValue().orElseThrow(() -> new IllegalStateException("Should not include annotations without a value definition"));
String alias = av.stringValue("alias").orElse(null);
// only the alias and path is needed, don't materialize the rest
return new JoinPath(path, new Association[0], Join.Type.DEFAULT, alias);
}).collect(Collectors.toSet());
this.joinFetchPaths = set.isEmpty() ? Collections.emptySet() : Collections.unmodifiableSet(set);
}
return joinFetchPaths;
}
use of io.micronaut.data.model.query.JoinPath 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.model.query.JoinPath in project micronaut-data by micronaut-projects.
the class AbstractSqlLikeQueryBuilder method buildQuery.
@Override
public QueryResult buildQuery(@NonNull AnnotationMetadata annotationMetadata, @NonNull QueryModel query) {
ArgumentUtils.requireNonNull("annotationMetadata", annotationMetadata);
ArgumentUtils.requireNonNull("query", query);
QueryState queryState = newQueryState(query, true, true);
List<JoinPath> joinPaths = new ArrayList<>(query.getJoinPaths());
joinPaths.sort((o1, o2) -> Comparator.comparingInt(String::length).thenComparing(String::compareTo).compare(o1.getPath(), o2.getPath()));
for (JoinPath joinPath : joinPaths) {
queryState.applyJoin(joinPath);
}
StringBuilder select = new StringBuilder(SELECT_CLAUSE);
buildSelectClause(query, queryState, select);
appendForUpdate(QueryPosition.AFTER_TABLE_NAME, query, select);
queryState.getQuery().insert(0, select);
QueryModel.Junction criteria = query.getCriteria();
if (!criteria.isEmpty() || annotationMetadata.hasStereotype(WhereSpecifications.class) || queryState.getEntity().getAnnotationMetadata().hasStereotype(WhereSpecifications.class)) {
buildWhereClause(annotationMetadata, criteria, queryState);
}
appendOrder(query, queryState);
appendForUpdate(QueryPosition.END_OF_QUERY, query, queryState.getQuery());
return QueryResult.of(queryState.getFinalQuery(), queryState.getQueryParts(), queryState.getParameterBindings(), queryState.getAdditionalRequiredParameters(), query.getMax(), query.getOffset());
}
use of io.micronaut.data.model.query.JoinPath in project micronaut-data by micronaut-projects.
the class JpaQueryBuilder method buildJoin.
@Override
protected String[] buildJoin(String alias, JoinPath joinPath, String joinType, StringBuilder target, Map<String, String> appliedJoinPaths, QueryState queryState) {
Association[] associationPath = joinPath.getAssociationPath();
String[] joinAliases;
if (ArrayUtils.isEmpty(associationPath)) {
throw new IllegalArgumentException("Invalid association path [" + joinPath.getPath() + "]");
}
List<Association> joinAssociationsPath = new ArrayList<>(associationPath.length);
joinAliases = new String[associationPath.length];
StringJoiner pathSoFar = new StringJoiner(".");
List<String> aliases = new ArrayList<>();
for (int i = 0; i < associationPath.length; i++) {
Association association = associationPath[i];
pathSoFar.add(association.getName());
if (association instanceof Embedded) {
joinAssociationsPath.add(association);
continue;
}
String currentPath = pathSoFar.toString();
String existingAlias = appliedJoinPaths.get(currentPath);
if (existingAlias != null) {
joinAliases[i] = existingAlias;
aliases.add(existingAlias);
} else {
int finalI = i;
JoinPath joinPathToUse = queryState.getQueryModel().getJoinPath(currentPath).orElseGet(() -> new JoinPath(currentPath, Arrays.copyOfRange(associationPath, 0, finalI + 1), joinPath.getJoinType(), joinPath.getAlias().orElse(null)));
String currentAlias = getAliasName(joinPathToUse);
joinAliases[i] = currentAlias;
String lastJoinAlias = aliases.isEmpty() ? alias : CollectionUtils.last(aliases);
target.append(joinType).append(lastJoinAlias).append(DOT).append(association.getName()).append(SPACE).append(joinAliases[i]);
aliases.add(currentAlias);
}
joinAssociationsPath.clear();
}
return joinAliases;
}
use of io.micronaut.data.model.query.JoinPath in project micronaut-data by micronaut-projects.
the class DefaultJdbcRepositoryOperations method findOne.
@Nullable
@Override
public <T, R> R findOne(@NonNull PreparedQuery<T, R> preparedQuery) {
return executeRead(connection -> {
RuntimePersistentEntity<T> persistentEntity = getEntity(preparedQuery.getRootEntity());
try (PreparedStatement ps = prepareStatement(connection, connection::prepareStatement, preparedQuery, false, true)) {
try (ResultSet rs = ps.executeQuery()) {
Class<R> resultType = preparedQuery.getResultType();
if (preparedQuery.getResultDataType() == DataType.ENTITY) {
RuntimePersistentEntity<R> resultPersistentEntity = getEntity(resultType);
final Set<JoinPath> joinFetchPaths = preparedQuery.getJoinFetchPaths();
SqlResultEntityTypeMapper<ResultSet, R> mapper = new SqlResultEntityTypeMapper<>(resultPersistentEntity, columnNameResultSetReader, joinFetchPaths, jsonCodec, (loadedEntity, o) -> {
if (loadedEntity.hasPostLoadEventListeners()) {
return triggerPostLoad(o, loadedEntity, preparedQuery.getAnnotationMetadata());
} else {
return o;
}
}, conversionService);
SqlResultEntityTypeMapper.PushingMapper<ResultSet, R> oneMapper = mapper.readOneWithJoins();
if (rs.next()) {
oneMapper.processRow(rs);
}
while (!joinFetchPaths.isEmpty() && rs.next()) {
oneMapper.processRow(rs);
}
R result = oneMapper.getResult();
if (preparedQuery.hasResultConsumer()) {
preparedQuery.getParameterInRole(SqlResultConsumer.ROLE, SqlResultConsumer.class).ifPresent(consumer -> consumer.accept(result, newMappingContext(rs)));
}
return result;
} else if (rs.next()) {
if (preparedQuery.isDtoProjection()) {
TypeMapper<ResultSet, R> introspectedDataMapper = new DTOMapper<>(persistentEntity, columnNameResultSetReader, jsonCodec, conversionService);
return introspectedDataMapper.map(rs, resultType);
} else {
Object v = columnIndexResultSetReader.readDynamic(rs, 1, preparedQuery.getResultDataType());
if (v == null) {
return null;
} else if (resultType.isInstance(v)) {
return (R) v;
} else {
return columnIndexResultSetReader.convertRequired(v, resultType);
}
}
}
}
} catch (SQLException e) {
throw new DataAccessException("Error executing SQL Query: " + e.getMessage(), e);
}
return null;
});
}
Aggregations