mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
#1297 - ENH: Add DtoQuery ... such that we can map native SQL queries automatically into "DTO beans"
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
package io.ebeaninternal.server.dto;
|
||||
|
||||
import io.ebeaninternal.metric.QueryPlanCollector;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Manages the query plans for a given DTO bean type.
|
||||
*/
|
||||
public class DtoBeanDescriptor<T> {
|
||||
|
||||
private final Map<String, DtoQueryPlan> plans = new ConcurrentHashMap<>();
|
||||
|
||||
private final Class<T> dtoType;
|
||||
|
||||
private final DtoMeta meta;
|
||||
|
||||
DtoBeanDescriptor(Class<T> dtoType, DtoMeta meta) {
|
||||
this.dtoType = dtoType;
|
||||
this.meta = meta;
|
||||
}
|
||||
|
||||
public Class<T> getType() {
|
||||
return dtoType;
|
||||
}
|
||||
|
||||
public DtoQueryPlan getQueryPlan(String planKey) {
|
||||
return plans.get(planKey);
|
||||
}
|
||||
|
||||
public DtoQueryPlan buildPlan(DtoMappingRequest request) {
|
||||
return meta.match(request);
|
||||
}
|
||||
|
||||
public void putQueryPlan(String planKey, DtoQueryPlan plan) {
|
||||
plans.put(planKey, plan);
|
||||
}
|
||||
|
||||
public void collectStats(QueryPlanCollector collector) {
|
||||
for (DtoQueryPlan plan : plans.values()) {
|
||||
plan.collectStats(collector);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package io.ebeaninternal.server.dto;
|
||||
|
||||
import io.ebean.meta.MetaQueryMetric;
|
||||
import io.ebeaninternal.metric.MetricFactory;
|
||||
import io.ebeaninternal.metric.QueryPlanCollector;
|
||||
import io.ebeaninternal.server.type.TypeManager;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Manages all the DTO bean descriptors.
|
||||
*/
|
||||
public class DtoBeanManager {
|
||||
|
||||
private final TypeManager typeManager;
|
||||
|
||||
private final Map<Class, DtoBeanDescriptor> descriptorMap = new ConcurrentHashMap<>();
|
||||
|
||||
public DtoBeanManager(TypeManager typeManager) {
|
||||
this.typeManager = typeManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the descriptor for the given DTO bean class.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> DtoBeanDescriptor<T> getDescriptor(Class<T> dtoType) {
|
||||
|
||||
return descriptorMap.computeIfAbsent(dtoType, this::createDescriptor);
|
||||
}
|
||||
|
||||
private <T> DtoBeanDescriptor createDescriptor(Class<T> dtoType) {
|
||||
|
||||
try {
|
||||
DtoMeta meta = new DtoMetaBuilder(dtoType, typeManager).build();
|
||||
return new DtoBeanDescriptor<>(dtoType, meta);
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public List<MetaQueryMetric> collectStats(boolean reset) {
|
||||
|
||||
QueryPlanCollector collector = MetricFactory.get().createCollector(reset);
|
||||
|
||||
for (DtoBeanDescriptor value : descriptorMap.values()) {
|
||||
value.collectStats(collector);
|
||||
}
|
||||
return collector.complete();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package io.ebeaninternal.server.dto;
|
||||
|
||||
/**
|
||||
* A column in the resultSet that we want to map to a bean property.
|
||||
*/
|
||||
public class DtoColumn {
|
||||
|
||||
private final String label;
|
||||
|
||||
public DtoColumn(String label) {
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return label;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package io.ebeaninternal.server.dto;
|
||||
|
||||
import io.ebeaninternal.api.SpiDtoQuery;
|
||||
import io.ebeaninternal.metric.MetricFactory;
|
||||
import io.ebeaninternal.metric.QueryPlanMetric;
|
||||
|
||||
/**
|
||||
* Request to map a resultSet columns for a query into a DTO bean.
|
||||
*/
|
||||
public class DtoMappingRequest {
|
||||
|
||||
private final Class type;
|
||||
|
||||
private final String label;
|
||||
|
||||
private final String sql;
|
||||
|
||||
private final boolean relaxedMode;
|
||||
|
||||
private final DtoColumn[] columnMeta;
|
||||
|
||||
public DtoMappingRequest(SpiDtoQuery query, String sql, DtoColumn[] columnMeta) {
|
||||
this.type = query.getType();
|
||||
this.label = query.getLabel();
|
||||
this.sql = sql;
|
||||
this.relaxedMode = query.isRelaxedMode();
|
||||
this.columnMeta = columnMeta;
|
||||
}
|
||||
|
||||
public DtoColumn[] getColumnMeta() {
|
||||
return columnMeta;
|
||||
}
|
||||
|
||||
public boolean isRelaxedMode() {
|
||||
return relaxedMode;
|
||||
}
|
||||
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
public String getSql() {
|
||||
return sql;
|
||||
}
|
||||
|
||||
public QueryPlanMetric createMetric() {
|
||||
return MetricFactory.get().createQueryPlanMetric(type, label, sql);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
package io.ebeaninternal.server.dto;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Holds property and constructor meta data for a given DTO bean type.
|
||||
*
|
||||
* Uses this to map a mapping request (columns) to a 'query plan' (constructor and setters).
|
||||
*/
|
||||
class DtoMeta {
|
||||
|
||||
private final Class<?> dtoType;
|
||||
private final Map<String, DtoMetaProperty> propMap = new LinkedHashMap<>();
|
||||
|
||||
private final Map<Integer, DtoMetaConstructor> constructorMap = new LinkedHashMap<>();
|
||||
|
||||
private final DtoMetaConstructor defaultConstructor;
|
||||
private final DtoMetaConstructor maxArgConstructor;
|
||||
|
||||
DtoMeta(Class<?> dtoType, List<DtoMetaConstructor> constructors, List<DtoMetaProperty> properties) {
|
||||
this.dtoType = dtoType;
|
||||
|
||||
for (DtoMetaProperty property : properties) {
|
||||
propMap.put(property.getName().toUpperCase(), property);
|
||||
}
|
||||
|
||||
int maxArg = 0;
|
||||
|
||||
DtoMetaConstructor defaultConstructor = null;
|
||||
DtoMetaConstructor maxArgConstructor = null;
|
||||
|
||||
for (DtoMetaConstructor constructor : constructors) {
|
||||
int args = constructor.getArgCount();
|
||||
constructorMap.put(args, constructor);
|
||||
if (args == 0) {
|
||||
defaultConstructor = constructor;
|
||||
} else if (args > maxArg) {
|
||||
maxArgConstructor = constructor;
|
||||
maxArg = args;
|
||||
}
|
||||
}
|
||||
this.defaultConstructor = defaultConstructor;
|
||||
this.maxArgConstructor = maxArgConstructor;
|
||||
}
|
||||
|
||||
public DtoQueryPlan match(DtoMappingRequest request) {
|
||||
|
||||
DtoColumn[] cols = request.getColumnMeta();
|
||||
|
||||
int colLen = cols.length;
|
||||
DtoMetaConstructor constructor = constructorMap.get(colLen);
|
||||
if (constructor != null) {
|
||||
return new DtoQueryPlanConstructor(request, constructor);
|
||||
}
|
||||
if (maxArgConstructor != null && colLen > maxArgConstructor.getArgCount()) {
|
||||
// maxArgConst + setters
|
||||
return matchMaxArgPlusSetters(request);
|
||||
}
|
||||
if (defaultConstructor != null) {
|
||||
return matchSetters(request);
|
||||
}
|
||||
|
||||
String msg = "Unable to map the resultSet columns " + Arrays.toString(cols)
|
||||
+ " to the bean type ["+dtoType+"] as the number of columns in the resultSet is less than the constructor"
|
||||
+ " (and that there is no default constructor) ?";
|
||||
throw new IllegalStateException(msg);
|
||||
}
|
||||
|
||||
private DtoQueryPlanConPlus matchMaxArgPlusSetters(DtoMappingRequest request) {
|
||||
|
||||
|
||||
int firstOnes = maxArgConstructor.getArgCount();
|
||||
|
||||
DtoColumn[] cols = request.getColumnMeta();
|
||||
|
||||
DtoReadSet[] setterProps = new DtoReadSet[cols.length - firstOnes];
|
||||
|
||||
int pos = 0;
|
||||
for (int i = firstOnes; i < cols.length; i++) {
|
||||
String label = cols[i].getLabel();
|
||||
DtoReadSet property = propMap.get(label.toUpperCase());
|
||||
if (property == null || property.isReadOnly()) {
|
||||
if (request.isRelaxedMode()) {
|
||||
property = DtoReadSetColumnSkip.INSTANCE;
|
||||
} else {
|
||||
throw new IllegalStateException("Unable to map DB column " + cols[i] + " to a property with a setter method on " + dtoType);
|
||||
}
|
||||
}
|
||||
setterProps[pos++] = property;
|
||||
}
|
||||
|
||||
return new DtoQueryPlanConPlus(request, maxArgConstructor, setterProps);
|
||||
}
|
||||
|
||||
private DtoQueryPlan matchSetters(DtoMappingRequest request) {
|
||||
|
||||
DtoColumn[] cols = request.getColumnMeta();
|
||||
|
||||
DtoReadSet[] setterProps = new DtoReadSet[cols.length];
|
||||
|
||||
for (int i = 0; i < cols.length; i++) {
|
||||
String label = cols[i].getLabel();
|
||||
DtoReadSet property = propMap.get(label.toUpperCase());
|
||||
if (property == null || property.isReadOnly()) {
|
||||
if (request.isRelaxedMode()) {
|
||||
property = DtoReadSetColumnSkip.INSTANCE;
|
||||
} else {
|
||||
throw new IllegalStateException("Unable to map DB column " + cols[i] + " to a property with a setter method on " + dtoType);
|
||||
}
|
||||
}
|
||||
setterProps[i] = property;
|
||||
}
|
||||
|
||||
return new DtoQueryPlanConSetter(request, defaultConstructor, setterProps);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package io.ebeaninternal.server.dto;
|
||||
|
||||
import io.ebeaninternal.server.type.TypeManager;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.beans.BeanInfo;
|
||||
import java.beans.IntrospectionException;
|
||||
import java.beans.Introspector;
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Build the DtoMeta for a bean.
|
||||
* <p>
|
||||
* Use TypeManager to map bean property types to ScalarTypes.
|
||||
*/
|
||||
class DtoMetaBuilder {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(DtoMetaBuilder.class);
|
||||
|
||||
private final TypeManager typeManager;
|
||||
|
||||
private final Class<?> dtoType;
|
||||
|
||||
private final List<DtoMetaProperty> properties = new ArrayList<>();
|
||||
|
||||
private final List<DtoMetaConstructor> constructorList = new ArrayList<>();
|
||||
|
||||
DtoMetaBuilder(Class<?> dtoType, TypeManager typeManager) {
|
||||
this.dtoType = dtoType;
|
||||
this.typeManager = typeManager;
|
||||
}
|
||||
|
||||
public DtoMeta build() throws IntrospectionException {
|
||||
|
||||
readConstructors();
|
||||
readProperties();
|
||||
|
||||
return new DtoMeta(dtoType, constructorList, properties);
|
||||
}
|
||||
|
||||
private void readProperties() throws IntrospectionException {
|
||||
|
||||
BeanInfo beanInfo = Introspector.getBeanInfo(dtoType);
|
||||
for (PropertyDescriptor propertyDescriptor : beanInfo.getPropertyDescriptors()) {
|
||||
if (include(propertyDescriptor)) {
|
||||
try {
|
||||
properties.add(new DtoMetaProperty(typeManager, propertyDescriptor, dtoType));
|
||||
} catch (Exception e) {
|
||||
log.debug("exclude on " + dtoType + " property " + propertyDescriptor.getName(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void readConstructors() {
|
||||
|
||||
Constructor<?>[] constructors = dtoType.getConstructors();
|
||||
|
||||
for (Constructor<?> constructor : constructors) {
|
||||
try {
|
||||
constructorList.add(new DtoMetaConstructor(typeManager, constructor, dtoType));
|
||||
} catch (Exception e) {
|
||||
// we don't want that constructor
|
||||
log.debug("exclude on " + dtoType + " constructor " + constructor, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean include(PropertyDescriptor property) {
|
||||
return !property.getName().equals("class");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package io.ebeaninternal.server.dto;
|
||||
|
||||
import io.ebeaninternal.server.type.DataReader;
|
||||
import io.ebeaninternal.server.type.ScalarType;
|
||||
import io.ebeaninternal.server.type.TypeManager;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.sql.SQLException;
|
||||
|
||||
class DtoMetaConstructor {
|
||||
|
||||
private final Class<?>[] types;
|
||||
private final MethodHandle handle;
|
||||
private final ScalarType<?>[] scalarTypes;
|
||||
|
||||
DtoMetaConstructor(TypeManager typeManager, Constructor<?> constructor, Class<?> someClass) throws NoSuchMethodException, IllegalAccessException {
|
||||
|
||||
this.types = constructor.getParameterTypes();
|
||||
this.scalarTypes = new ScalarType[types.length];
|
||||
for (int i = 0; i < types.length; i++) {
|
||||
scalarTypes[i] = typeManager.getScalarType(types[i]);
|
||||
}
|
||||
|
||||
MethodHandles.Lookup lookup = MethodHandles.publicLookup();
|
||||
this.handle = lookup.findConstructor(someClass, typeFor(types));
|
||||
}
|
||||
|
||||
private MethodType typeFor(Class<?>[] types) {
|
||||
return MethodType.methodType(void.class, types);
|
||||
}
|
||||
|
||||
Class<?>[] getTypes() {
|
||||
return types;
|
||||
}
|
||||
|
||||
int getArgCount() {
|
||||
return types.length;
|
||||
}
|
||||
|
||||
Object defaultConstructor() {
|
||||
try {
|
||||
return handle.invokeWithArguments();
|
||||
} catch (Throwable e) {
|
||||
throw new RuntimeException("Unexpected error invoking constructor", e);
|
||||
}
|
||||
}
|
||||
|
||||
public Object process(DataReader dataReader) throws SQLException {
|
||||
Object[] values = new Object[scalarTypes.length];
|
||||
for (int i = 0; i < scalarTypes.length; i++) {
|
||||
values[i] = scalarTypes[i].read(dataReader);
|
||||
}
|
||||
return invoke(values);
|
||||
}
|
||||
|
||||
private Object invoke(Object... args) {
|
||||
try {
|
||||
return handle.invokeWithArguments(args);
|
||||
} catch (Throwable e) {
|
||||
throw new RuntimeException("Unexpected error invoking constructor", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package io.ebeaninternal.server.dto;
|
||||
|
||||
import io.ebeaninternal.server.type.DataReader;
|
||||
import io.ebeaninternal.server.type.ScalarType;
|
||||
import io.ebeaninternal.server.type.TypeManager;
|
||||
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.reflect.Method;
|
||||
import java.sql.SQLException;
|
||||
|
||||
class DtoMetaProperty implements DtoReadSet {
|
||||
|
||||
private final Class<?> dtoType;
|
||||
private final String name;
|
||||
private final MethodHandle setter;
|
||||
private final ScalarType<?> scalarType;
|
||||
|
||||
DtoMetaProperty(TypeManager typeManager, PropertyDescriptor descriptor, Class<?> dtoType) throws IllegalAccessException, NoSuchMethodException {
|
||||
|
||||
this.dtoType = dtoType;
|
||||
this.name = descriptor.getName();
|
||||
|
||||
Method writeMethod = descriptor.getWriteMethod();
|
||||
if (writeMethod != null) {
|
||||
|
||||
Class<?> propertyType = descriptor.getPropertyType();
|
||||
|
||||
MethodHandles.Lookup lookup = MethodHandles.publicLookup();
|
||||
this.setter = lookup.findVirtual(dtoType, writeMethod.getName(), MethodType.methodType(void.class, propertyType));
|
||||
this.scalarType = typeManager.getScalarType(propertyType);
|
||||
|
||||
} else {
|
||||
this.scalarType = null;
|
||||
this.setter = null;
|
||||
}
|
||||
}
|
||||
|
||||
String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReadOnly() {
|
||||
return scalarType == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readSet(Object bean, DataReader dataReader) throws SQLException {
|
||||
Object value = scalarType.read(dataReader);
|
||||
invoke(bean, value);
|
||||
}
|
||||
|
||||
private void invoke(Object instance, Object arg) {
|
||||
try {
|
||||
setter.invoke(instance, arg);
|
||||
} catch (Throwable e) {
|
||||
throw new RuntimeException("Error calling setter for property " + fullname() + " with arg: " + arg, e);
|
||||
}
|
||||
}
|
||||
|
||||
private String fullname() {
|
||||
return dtoType.getName() + "." + name;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package io.ebeaninternal.server.dto;
|
||||
|
||||
import io.ebeaninternal.metric.QueryPlanCollector;
|
||||
import io.ebeaninternal.server.type.DataReader;
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* Knows how to read and map rows into a Bean.
|
||||
*/
|
||||
public interface DtoQueryPlan {
|
||||
|
||||
/**
|
||||
* Read the row data and return the DTO bean.
|
||||
*/
|
||||
Object readRow(DataReader dataReader) throws SQLException;
|
||||
|
||||
/**
|
||||
* Add an event to the query execution statistics.
|
||||
*/
|
||||
void collect(long exeMicros, int rows);
|
||||
|
||||
/**
|
||||
* Collect the query plan statistics.
|
||||
*/
|
||||
void collectStats(QueryPlanCollector collector);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package io.ebeaninternal.server.dto;
|
||||
|
||||
import io.ebeaninternal.metric.QueryPlanCollector;
|
||||
import io.ebeaninternal.metric.QueryPlanMetric;
|
||||
import io.ebeaninternal.metric.TimedMetric;
|
||||
|
||||
abstract class DtoQueryPlanBase implements DtoQueryPlan {
|
||||
|
||||
private final QueryPlanMetric planMetric;
|
||||
|
||||
private final TimedMetric metric;
|
||||
|
||||
DtoQueryPlanBase(DtoMappingRequest request) {
|
||||
this.planMetric = request.createMetric();
|
||||
this.metric = planMetric.getMetric();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void collect(long exeTime, int rows) {
|
||||
metric.add(exeTime, rows);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void collectStats(QueryPlanCollector collector) {
|
||||
planMetric.collect(collector);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package io.ebeaninternal.server.dto;
|
||||
|
||||
import io.ebeaninternal.server.type.DataReader;
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* Plan based on Constructor plus some setter methods.
|
||||
*/
|
||||
class DtoQueryPlanConPlus extends DtoQueryPlanBase {
|
||||
|
||||
private final DtoMetaConstructor maxArgConstructor;
|
||||
|
||||
private final DtoReadSet[] setterProps;
|
||||
|
||||
DtoQueryPlanConPlus(DtoMappingRequest request, DtoMetaConstructor maxArgConstructor, DtoReadSet[] setterProps) {
|
||||
super(request);
|
||||
this.maxArgConstructor = maxArgConstructor;
|
||||
this.setterProps = setterProps;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object readRow(DataReader dataReader) throws SQLException {
|
||||
|
||||
Object bean = maxArgConstructor.process(dataReader);
|
||||
for (DtoReadSet setterProp : setterProps) {
|
||||
setterProp.readSet(bean, dataReader);
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package io.ebeaninternal.server.dto;
|
||||
|
||||
import io.ebeaninternal.server.type.DataReader;
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* Plan based on default constructor and setter methods.
|
||||
*/
|
||||
class DtoQueryPlanConSetter extends DtoQueryPlanBase {
|
||||
|
||||
private final DtoMetaConstructor defaultConstructor;
|
||||
|
||||
private final DtoReadSet[] setterProps;
|
||||
|
||||
DtoQueryPlanConSetter(DtoMappingRequest request, DtoMetaConstructor defaultConstructor, DtoReadSet[] setterProps) {
|
||||
super(request);
|
||||
this.defaultConstructor = defaultConstructor;
|
||||
this.setterProps = setterProps;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object readRow(DataReader dataReader) throws SQLException {
|
||||
|
||||
Object bean = defaultConstructor.defaultConstructor();
|
||||
for (DtoReadSet setterProp : setterProps) {
|
||||
setterProp.readSet(bean, dataReader);
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package io.ebeaninternal.server.dto;
|
||||
|
||||
import io.ebeaninternal.server.type.DataReader;
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* Plan based on mapping via single constructor only.
|
||||
*/
|
||||
class DtoQueryPlanConstructor extends DtoQueryPlanBase {
|
||||
|
||||
private final DtoMetaConstructor constructor;
|
||||
|
||||
DtoQueryPlanConstructor(DtoMappingRequest request, DtoMetaConstructor constructor) {
|
||||
super(request);
|
||||
this.constructor = constructor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object readRow(DataReader dataReader) throws SQLException {
|
||||
return constructor.process(dataReader);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package io.ebeaninternal.server.dto;
|
||||
|
||||
import io.ebeaninternal.server.type.DataReader;
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* Read and set a property value.
|
||||
*/
|
||||
public interface DtoReadSet {
|
||||
|
||||
/**
|
||||
* Read the value from the dataReader and set it to the bean.
|
||||
*/
|
||||
void readSet(Object bean, DataReader dataReader) throws SQLException;
|
||||
|
||||
/**
|
||||
* Return true if this maps to a read only property (no setter method).
|
||||
*/
|
||||
boolean isReadOnly();
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package io.ebeaninternal.server.dto;
|
||||
|
||||
import io.ebeaninternal.server.type.DataReader;
|
||||
|
||||
/**
|
||||
* Placeholder to skip reading a column that isn't mapped to a bean property.
|
||||
*/
|
||||
class DtoReadSetColumnSkip implements DtoReadSet {
|
||||
|
||||
static final DtoReadSet INSTANCE = new DtoReadSetColumnSkip();
|
||||
|
||||
@Override
|
||||
public void readSet(Object bean, DataReader dataReader) {
|
||||
dataReader.incrementPos(1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReadOnly() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user