Skip to content

Commit

Permalink
PR ebean-orm#3143 - FEATURE: DbJson Support for Dto-Queries
Browse files Browse the repository at this point in the history
  • Loading branch information
rPraml committed Aug 10, 2023
1 parent 924f611 commit 590cecd
Show file tree
Hide file tree
Showing 9 changed files with 325 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
* Description of a property of a bean. Includes its deployment information such
* as database column mapping information.
*/
public class DeployBeanProperty {
public class DeployBeanProperty implements DeployProperty {

private static final int ID_ORDER = 1000000;
private static final int UNIDIRECTIONAL_ORDER = 100000;
Expand Down Expand Up @@ -226,6 +226,11 @@ public DeployBeanDescriptor<?> getDesc() {
return desc;
}

@Override
public Class<?> getOwnerType() {
return desc.getBeanType();
}

/**
* Return the DB column length for character columns.
* <p>
Expand Down Expand Up @@ -258,10 +263,12 @@ public void setJsonDeserialize(boolean jsonDeserialize) {
this.jsonDeserialize = jsonDeserialize;
}

@Override
public MutationDetection getMutationDetection() {
return mutationDetection;
}

@Override
public void setMutationDetection(MutationDetection dirtyDetection) {
this.mutationDetection = dirtyDetection;
}
Expand Down Expand Up @@ -476,8 +483,9 @@ public void setGeneratedProperty(GeneratedProperty generatedValue) {
}

/**
* Return true if this property is mandatory.
* Return true if this property is not mandatory.
*/
@Override
public boolean isNullable() {
return nullable;
}
Expand Down Expand Up @@ -848,6 +856,7 @@ public Class<?> getPropertyType() {
/**
* Return the generic type for this property.
*/
@Override
public Type getGenericType() {
return genericType;
}
Expand Down Expand Up @@ -1052,6 +1061,7 @@ public <A extends Annotation> A getMetaAnnotation(Class<A> annotationType) {
return null;
}

@Override
@SuppressWarnings("unchecked")
public <A extends Annotation> List<A> getMetaAnnotations(Class<A> annotationType) {
List<A> result = new ArrayList<>();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package io.ebeaninternal.server.deploy.meta;

import io.ebean.annotation.MutationDetection;

import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.List;

/**
* Property, with basic type information (BeanProperty and DtoProperty).
*/
public interface DeployProperty {

/**
* Return the name of the property.
*/
String getName();

/**
* Return the generic type for this property.
*/
Type getGenericType();

/**
* Return the property type.
*/
Class<?> getPropertyType();

/**
* Returns the owner class of this property.
*/
Class<?> getOwnerType();

/**
* Returns the annotations on this property.
*/
<A extends Annotation> List<A> getMetaAnnotations(Class<A> annotationType);

/**
* Returns the mutation detection setting of this property.
*/
MutationDetection getMutationDetection();

/**
* Sets the mutation detection setting of this property.
*/
void setMutationDetection(MutationDetection mutationDetection);

/**
* Return true if this property is not mandatory.
*/
boolean isNullable();
}
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ private void setDbJsonType(DeployBeanProperty prop, int dbType, int dbLength, Mu
/**
* Return the JDBC type for the JSON storage type.
*/
private int dbJsonStorage(DbJsonType dbJsonType) {
public static int dbJsonStorage(DbJsonType dbJsonType) {
switch (dbJsonType) {
case JSONB:
return DbPlatformType.JSONB;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package io.ebeaninternal.server.dto;

import io.ebean.annotation.DbJson;
import io.ebean.annotation.DbJsonB;
import io.ebeaninternal.api.CoreLog;
import io.ebeaninternal.server.type.TypeManager;

Expand All @@ -21,10 +23,16 @@ final class DtoMetaBuilder {
private final Class<?> dtoType;
private final List<DtoMetaProperty> properties = new ArrayList<>();
private final Map<Integer, DtoMetaConstructor> constructorMap = new HashMap<>();
private final Set<Class<?>> annotationFilter = new HashSet<>();

DtoMetaBuilder(Class<?> dtoType, TypeManager typeManager) {
this.dtoType = dtoType;
this.typeManager = typeManager;
annotationFilter.add(DbJson.class);
annotationFilter.add(DbJsonB.class);
if (typeManager.jsonMarkerAnnotation() != null) {
annotationFilter.add(typeManager.jsonMarkerAnnotation());
}
}

DtoMeta build() {
Expand All @@ -38,7 +46,7 @@ private void readProperties() {
if (includeMethod(method)) {
try {
final String name = propertyName(method.getName());
properties.add(new DtoMetaProperty(typeManager, dtoType, method, name));
properties.add(new DtoMetaProperty(typeManager, dtoType, method, name, annotationFilter));
} catch (Exception e) {
CoreLog.log.log(DEBUG, "exclude on " + dtoType + " method " + method, e);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package io.ebeaninternal.server.dto;

import io.ebean.annotation.MutationDetection;
import io.ebeaninternal.server.deploy.meta.DeployProperty;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
* DeployProperty for Dto-Properties.
*
* @author Roland Praml, FOCONIS AG
*/
class DtoMetaDeployProperty implements DeployProperty {
private final String name;
private final Class<?> ownerType;
private final Type genericType;
private final Class<?> propertyType;
private final Set<Annotation> metaAnnotations;
private final boolean nullable;
private MutationDetection mutationDetection = MutationDetection.DEFAULT;

DtoMetaDeployProperty(String name, Class<?> ownerType, Type genericType, Class<?> propertyType, Set<Annotation> metaAnnotations, Method method) {
this.name = name;
this.ownerType = ownerType;
this.genericType = genericType;
this.nullable = !propertyType.isPrimitive();
this.propertyType = propertyType;
this.metaAnnotations = metaAnnotations;
}

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

@Override
public Type getGenericType() {
return genericType;
}

@Override
public Class<?> getPropertyType() {
return propertyType;
}

@Override
public Class<?> getOwnerType() {
return ownerType;
}

@Override
public <A extends Annotation> List<A> getMetaAnnotations(Class<A> annotationType) {
List<A> result = new ArrayList<>();
for (Annotation ann : metaAnnotations) {
if (ann.annotationType() == annotationType) {
result.add((A) ann);
}
}
return result;
}

@Override
public MutationDetection getMutationDetection() {
return mutationDetection;
}

@Override
public void setMutationDetection(MutationDetection mutationDetection) {
this.mutationDetection = mutationDetection;
}

@Override
public boolean isNullable() {
return nullable;
}
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,26 @@
package io.ebeaninternal.server.dto;

import io.ebean.annotation.DbJson;
import io.ebean.annotation.DbJsonB;
import io.ebean.config.dbplatform.DbPlatformType;
import io.ebean.core.type.DataReader;
import io.ebean.core.type.ScalarType;
import io.ebean.util.AnnotationUtil;
import io.ebeaninternal.server.deploy.meta.DeployProperty;
import io.ebeaninternal.server.deploy.parse.DeployUtil;
import io.ebeaninternal.server.type.TypeManager;

import java.lang.annotation.Annotation;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.sql.SQLException;
import java.util.Collections;
import java.util.List;
import java.util.Set;

final class DtoMetaProperty implements DtoReadSet {

Expand All @@ -20,18 +31,74 @@ final class DtoMetaProperty implements DtoReadSet {
private final MethodHandle setter;
private final ScalarType<?> scalarType;

DtoMetaProperty(TypeManager typeManager, Class<?> dtoType, Method writeMethod, String name) throws IllegalAccessException, NoSuchMethodException {
DtoMetaProperty(TypeManager typeManager, Class<?> dtoType, Method writeMethod, String name, Set<Class<?>> annotationFilter)
throws IllegalAccessException, NoSuchMethodException {
this.dtoType = dtoType;
this.name = name;
if (writeMethod != null) {
this.setter = lookupMethodHandle(dtoType, writeMethod);
this.scalarType = typeManager.type(propertyType(writeMethod), propertyClass(writeMethod));
Field field = findField(dtoType, name);
DeployProperty deployProp = new DtoMetaDeployProperty(name,
dtoType,
propertyType(writeMethod),
propertyClass(writeMethod),
field == null ? Collections.emptySet() : AnnotationUtil.metaFindAllFor(field, annotationFilter),
writeMethod);
scalarType = getScalarType(typeManager, deployProp);
} else {
this.scalarType = null;
this.setter = null;
}
}

private ScalarType<?> getScalarType(TypeManager typeManager, DeployProperty deployProp) {
final ScalarType<?> scalarType;

List<DbJson> json = deployProp.getMetaAnnotations(DbJson.class);
if (!json.isEmpty()) {
return typeManager.dbJsonType(deployProp, DeployUtil.dbJsonStorage(json.get(0).storage()), json.get(0).length());
}
List<DbJsonB> jsonB = deployProp.getMetaAnnotations(DbJsonB.class);
if (!jsonB.isEmpty()) {
return typeManager.dbJsonType(deployProp, DbPlatformType.JSONB, jsonB.get(0).length());
}
if (typeManager.jsonMarkerAnnotation() != null
&& !deployProp.getMetaAnnotations(typeManager.jsonMarkerAnnotation()).isEmpty()) {
return typeManager.dbJsonType(deployProp, DbPlatformType.JSON, 0);
}
return typeManager.type(deployProp);


}

/**
* Find all annotations on fields and methods.
*/
private Set<Annotation> findMetaAnnotations(Class<?> dtoType, Method writeMethod, String name, Set<Class<?>> annotationFilter) {
Field field = findField(dtoType, name);
if (field != null) {
Set<Annotation> metaAnnotations = AnnotationUtil.metaFindAllFor(field, annotationFilter);
metaAnnotations.addAll(AnnotationUtil.metaFindAllFor(writeMethod, annotationFilter));
return metaAnnotations;
} else {
return AnnotationUtil.metaFindAllFor(writeMethod, annotationFilter);
}
}

/**
* Find field in class with same name
*/
private Field findField(Class<?> type, String name) {
while (type != Object.class && type != null) {
try {
return dtoType.getDeclaredField(name);
} catch (NoSuchFieldException e) {
type = type.getSuperclass();
}
}
return null;
}

private static MethodHandle lookupMethodHandle(Class<?> dtoType, Method method) throws NoSuchMethodException, IllegalAccessException {
return LOOKUP.findVirtual(dtoType, method.getName(), MethodType.methodType(method.getReturnType(), method.getParameterTypes()));
}
Expand Down
Loading

0 comments on commit 590cecd

Please sign in to comment.