use of io.micronaut.data.annotation.Relation in project micronaut-data by micronaut-projects.
the class AbstractCascadeOperations method cascade.
/**
* Cascade on the entity instance and collect cascade operations.
*
* @param annotationMetadata The annotationMetadata
* @param repositoryType The repositoryType
* @param fkOnly Is FK only
* @param cascadeType The cascadeType
* @param ctx The cascade context
* @param persistentEntity The persistent entity
* @param entity The entity instance
* @param cascadeOps The cascade operations
* @param <T> The entity type
*/
protected <T> void cascade(AnnotationMetadata annotationMetadata, Class<?> repositoryType, boolean fkOnly, Relation.Cascade cascadeType, CascadeContext ctx, RuntimePersistentEntity<T> persistentEntity, T entity, List<CascadeOp> cascadeOps) {
for (RuntimeAssociation<T> association : persistentEntity.getAssociations()) {
BeanProperty<T, Object> beanProperty = (BeanProperty<T, Object>) association.getProperty();
Object child = beanProperty.get(entity);
if (child == null) {
continue;
}
if (association instanceof Embedded) {
cascade(annotationMetadata, repositoryType, fkOnly, cascadeType, ctx.embedded(association), (RuntimePersistentEntity) association.getAssociatedEntity(), child, cascadeOps);
continue;
}
if (association.doesCascade(cascadeType) && (fkOnly || !association.isForeignKey())) {
if (association.getInverseSide().map(assoc -> ctx.rootAssociations.contains(assoc) || ctx.associations.contains(assoc)).orElse(false)) {
continue;
}
final RuntimePersistentEntity<Object> associatedEntity = (RuntimePersistentEntity<Object>) association.getAssociatedEntity();
switch(association.getKind()) {
case ONE_TO_ONE:
case MANY_TO_ONE:
cascadeOps.add(new CascadeOneOp(annotationMetadata, repositoryType, ctx.relation(association), cascadeType, associatedEntity, child));
continue;
case ONE_TO_MANY:
case MANY_TO_MANY:
final PersistentAssociationPath inverse = association.getInversePathSide().orElse(null);
Iterable<Object> children = (Iterable<Object>) association.getProperty().get(entity);
if (children == null || !children.iterator().hasNext()) {
continue;
}
if (inverse != null && inverse.getAssociation().getKind() == Relation.Kind.MANY_TO_ONE) {
List<Object> entities = new ArrayList<>(CollectionUtils.iterableToList(children));
for (ListIterator<Object> iterator = entities.listIterator(); iterator.hasNext(); ) {
Object c = iterator.next();
Object newC = inverse.setPropertyValue(c, entity);
if (c != newC) {
iterator.set(newC);
}
}
children = entities;
}
cascadeOps.add(new CascadeManyOp(annotationMetadata, repositoryType, ctx.relation(association), cascadeType, associatedEntity, children));
continue;
default:
throw new IllegalArgumentException("Cannot cascade for relation: " + association.getKind());
}
}
}
}
use of io.micronaut.data.annotation.Relation in project micronaut-data by micronaut-projects.
the class MappedEntityVisitor method computeMappingDefaults.
private void computeMappingDefaults(NamingStrategy namingStrategy, PersistentProperty property, Map<String, DataType> dataTypes, Map<String, String> dataConverters, VisitorContext context) {
AnnotationMetadata annotationMetadata = property.getAnnotationMetadata();
SourcePersistentProperty spp = (SourcePersistentProperty) property;
PropertyElement propertyElement = spp.getPropertyElement();
boolean isRelation = propertyElement.hasStereotype(Relation.class);
DataType dataType = annotationMetadata.getValue(TypeDef.class, "type", DataType.class).orElse(null);
String converter = annotationMetadata.stringValue(MappedProperty.class, "converter").orElseGet(() -> annotationMetadata.stringValue(TypeDef.class, "converter").orElse(null));
if (Objects.equals(converter, Object.class.getName())) {
converter = null;
}
if (converter == null) {
ClassElement type = propertyElement.getGenericType();
converter = TypeUtils.resolveDataConverter(type, dataConverters);
}
if (converter != null) {
if (isRelation) {
context.fail("Relation cannot have converter specified", propertyElement);
return;
}
ClassElement persistedClassFromConverter = getPersistedClassFromConverter(converter, context);
if (persistedClassFromConverter != null) {
propertyElement.annotate(MappedProperty.class, builder -> {
builder.member("converterPersistedType", new AnnotationClassValue<>(persistedClassFromConverter.getCanonicalName()));
});
}
if (dataType == null) {
dataType = getDataTypeFromConverter(propertyElement.getGenericType(), converter, dataTypes, context);
if (dataType == null) {
context.fail("Cannot recognize proper data type. Please use @TypeDef to specify one", propertyElement);
return;
}
}
} else {
if (dataType == null && spp.getType().isEnum()) {
if (spp.getOwner().getAnnotationMetadata().hasAnnotation("javax.persistence.Entity") || spp.getOwner().getAnnotationMetadata().hasAnnotation("jakarta.persistence.Entity")) {
// JPA enums have default ORDINAL mapping for enums
dataType = DataType.INTEGER;
}
}
if (dataType == null) {
ClassElement type = propertyElement.getGenericType();
dataType = TypeUtils.resolveDataType(type, dataTypes);
}
}
if (dataType == DataType.ENTITY && !isRelation) {
propertyElement = (PropertyElement) propertyElement.annotate(Relation.class, builder -> builder.value(Relation.Kind.MANY_TO_ONE));
} else if (isRelation) {
Relation.Kind kind = propertyElement.enumValue(Relation.class, Relation.Kind.class).orElse(Relation.Kind.MANY_TO_ONE);
if (kind == Relation.Kind.EMBEDDED || kind == Relation.Kind.MANY_TO_ONE) {
if (propertyElement.stringValue(Relation.class, "mappedBy").isPresent()) {
context.fail("Relation " + kind + " doesn't support 'mappedBy'.", propertyElement);
}
}
if (kind == Relation.Kind.EMBEDDED) {
// handled embedded
SourcePersistentEntity embeddedEntity = entityResolver.apply(propertyElement.getType());
List<SourcePersistentProperty> persistentProperties = embeddedEntity.getPersistentProperties();
List<AnnotationValue<Property>> embeddedProperties = new ArrayList<>(persistentProperties.size());
for (SourcePersistentProperty embeddedProperty : persistentProperties) {
if (!(embeddedProperty instanceof Association)) {
String mappedName = embeddedProperty.stringValue(MappedProperty.class).orElseGet(() -> namingStrategy.mappedName(property.getName() + embeddedProperty.getCapitilizedName()));
AnnotationValue<Property> av = AnnotationValue.builder(Property.class).value(mappedName).member("name", embeddedProperty.getName()).build();
embeddedProperties.add(av);
}
// else {
// // TODO: handle nested embedded
// }
}
propertyElement.annotate(MappedProperty.class, builder -> builder.member(MappedProperty.EMBEDDED_PROPERTIES, embeddedProperties.toArray(new AnnotationValue[0])));
}
}
Optional<String> mapping = annotationMetadata.stringValue(MappedProperty.class);
if (mappedEntity && !mapping.isPresent()) {
propertyElement.annotate(MappedProperty.class, builder -> builder.value(namingStrategy.mappedName(spp)));
}
if (dataType != DataType.OBJECT) {
DataType finalDataType = dataType;
propertyElement.annotate(MappedProperty.class, builder -> builder.member("type", finalDataType));
}
if (converter != null) {
String finalConverter = converter;
propertyElement.annotate(MappedProperty.class, builder -> builder.member("converter", new AnnotationClassValue<>(finalConverter)));
}
}
Aggregations