Updated Branches: refs/heads/master 4c410f32f -> c75adbb95
DELTASPIKE-443 Mapping API, documentation and implementation. Project: http://git-wip-us.apache.org/repos/asf/deltaspike/repo Commit: http://git-wip-us.apache.org/repos/asf/deltaspike/commit/c75adbb9 Tree: http://git-wip-us.apache.org/repos/asf/deltaspike/tree/c75adbb9 Diff: http://git-wip-us.apache.org/repos/asf/deltaspike/diff/c75adbb9 Branch: refs/heads/master Commit: c75adbb955a4cd8231bc63be1df81f981079f47e Parents: 4c410f3 Author: Thomas Hug <thomas....@ctp-consulting.com> Authored: Fri Nov 15 10:57:47 2013 +0100 Committer: Thomas Hug <thomas....@ctp-consulting.com> Committed: Wed Nov 20 11:36:26 2013 +0100 ---------------------------------------------------------------------- deltaspike/modules/data/README.adoc | 84 +++++++++- .../data/api/mapping/MappingConfig.java | 40 +++++ .../data/api/mapping/QueryInOutMapper.java | 60 +++++++ .../data/impl/builder/QueryBuilder.java | 27 ++- .../impl/builder/result/DefaultQueryResult.java | 6 +- .../impl/handler/CdiQueryInvocationContext.java | 27 +++ .../data/impl/handler/QueryHandler.java | 3 +- .../data/impl/meta/RepositoryMethod.java | 83 ++++++++++ .../data/impl/param/IndexedParameter.java | 2 +- .../data/impl/param/NamedParameter.java | 2 +- .../deltaspike/data/impl/param/Parameter.java | 22 +++ .../deltaspike/data/impl/param/Parameters.java | 9 + .../data/impl/mapping/MappedRepositoryTest.java | 163 +++++++++++++++++++ .../deltaspike/data/test/domain/Simple.java | 7 + .../data/test/domain/dto/BooleanWrapper.java | 40 +++++ .../data/test/domain/dto/SimpleDto.java | 57 +++++++ .../data/test/domain/dto/SimpleId.java | 45 +++++ .../test/service/SimpleMappedRepository.java | 48 ++++++ .../data/test/service/SimpleMapper.java | 76 +++++++++ .../data/test/service/WrappedMapper.java | 63 +++++++ .../data/test/util/TestDeployments.java | 3 + 21 files changed, 859 insertions(+), 8 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/deltaspike/blob/c75adbb9/deltaspike/modules/data/README.adoc ---------------------------------------------------------------------- diff --git a/deltaspike/modules/data/README.adoc b/deltaspike/modules/data/README.adoc index c6c6cc0..cbd887a 100644 --- a/deltaspike/modules/data/README.adoc +++ b/deltaspike/modules/data/README.adoc @@ -1,5 +1,4 @@ = DeltaSpike Data -Thomas Hug <t...@apache.org> /////////// Licensed to the Apache Software Foundation (ASF) under one @@ -695,6 +694,89 @@ like accessing the current +EntityManager+ and entity class. Note that, if you define multiple extensions with equivalent method signatures, there is no specific order in which the implementation is selected. +=== Mapping + +While repositories are primarily intended to work with Entities, it might be preferable in some +cases to have an additional mapping layer on top of them, e.g. because the Entities are quite complex +but the service layer needs only a limited view on it, or because the Entities are exposed over a +remote interface and there should not be a 1:1 view on the domain model. + +DeltaSpike Data allows to directly plugin in such a mapping mechanism without the need to specify additional +mapping methods: + +[source,java] +---- +@Repository(forEntity = Person.class) +@MappingConfig(PersonDtoMapper.class) +public interface PersonRepository +{ + + PersonDto findBySsn(String ssn); + + List<PersonDto> findByLastName(String lastName); + +} +---- + +The +PersonDtoMapper+ class has to implement the +QueryInOutMapper+ interface: + +[source,java] +---- +public class PersonDtoMapper implements QueryInOutMapper<Person> +{ + + @Override + public Object mapResult(Person result) + { + ... // converts Person into a PersonDto + } + ... + + @Override + public Object mapResultList(List<Simple> result) + { + ... // result lists can also be mapped into something different + // than a collection. + } + + @Override + public boolean mapsParameter(Object parameter) + { + return parameter != null && ( + parameter instanceof PersonDto || parameter instanceof PersonId); + } + + @Override + public Object mapParameter(Object parameter) + { + ... // converts query parameters if required + } +} +---- + +The mapper can also be used to transform query parameters. Parameters are converted before +executing queries and calling repository extensions. + +Note that those mapper classes are treated as CDI Beans, so it is possible to use injection +in those beans (you might e.g. inject an +EntityManager+ or other mappers). As the +@MappingConfig+ +refers to the mapper class directly, the mapper must be uniquely identifiable by its class. + +It's also possible to combine mappings with the base Repository classes: + +[source,java] +---- +@Repository(forEntity = Person.class) +@MappingConfig(PersonDtoMapper.class) +public interface PersonRepository extends EntityRepository<PersonDto, PersonId> +{ + ... +} +---- + +In this case, the +forEntity+ attribute in the +@Repository+ annotation is mandatory. Also it is up +to the mapper to convert parameters correctly (in this example, a conversion from a +PersonDto+ +parameter to +Person+ entity and from +PersonId+ to +Long+ is necessary). + === JPA Criteria API Support Beside automatic query generation, the DeltaSpike data module also provides a DSL-like API to create JPA 2 Criteria queries. http://git-wip-us.apache.org/repos/asf/deltaspike/blob/c75adbb9/deltaspike/modules/data/api/src/main/java/org/apache/deltaspike/data/api/mapping/MappingConfig.java ---------------------------------------------------------------------- diff --git a/deltaspike/modules/data/api/src/main/java/org/apache/deltaspike/data/api/mapping/MappingConfig.java b/deltaspike/modules/data/api/src/main/java/org/apache/deltaspike/data/api/mapping/MappingConfig.java new file mode 100644 index 0000000..07f7c77 --- /dev/null +++ b/deltaspike/modules/data/api/src/main/java/org/apache/deltaspike/data/api/mapping/MappingConfig.java @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.deltaspike.data.api.mapping; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Defines mapping configuration for query result and input parameters. + */ +@Target({ ElementType.TYPE, ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Inherited +public @interface MappingConfig +{ + + Class<? extends QueryInOutMapper<?>> value(); + +} http://git-wip-us.apache.org/repos/asf/deltaspike/blob/c75adbb9/deltaspike/modules/data/api/src/main/java/org/apache/deltaspike/data/api/mapping/QueryInOutMapper.java ---------------------------------------------------------------------- diff --git a/deltaspike/modules/data/api/src/main/java/org/apache/deltaspike/data/api/mapping/QueryInOutMapper.java b/deltaspike/modules/data/api/src/main/java/org/apache/deltaspike/data/api/mapping/QueryInOutMapper.java new file mode 100644 index 0000000..afded50 --- /dev/null +++ b/deltaspike/modules/data/api/src/main/java/org/apache/deltaspike/data/api/mapping/QueryInOutMapper.java @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.deltaspike.data.api.mapping; + +import java.util.List; + +/** + * Handels concrete mapping of query results and + * query input parameters. + */ +public interface QueryInOutMapper<E> +{ + + /** + * Map a single result query. + * @param result The query result to map. + * @return The mapped result object. + */ + Object mapResult(E result); + + /** + * Map a query result list. + * @param result The query result list to map. + * @return The mapped result. Does not have to be a collection. + */ + Object mapResultList(List<E> result); + + /** + * Check if this mapper handles a specific input parameter. + * @param parameter The parameter candidate for mapping. + * @return {@code true} if the mapper handles the parameter. + */ + boolean mapsParameter(Object parameter); + + /** + * Map a query parameter. + * @param parameter The parameter to map. It can be assumed that the + * {@link #mapsParameter(Object)} method has been + * called before with this parameter. + * @return The mapped result. + */ + Object mapParameter(Object parameter); + +} http://git-wip-us.apache.org/repos/asf/deltaspike/blob/c75adbb9/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/builder/QueryBuilder.java ---------------------------------------------------------------------- diff --git a/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/builder/QueryBuilder.java b/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/builder/QueryBuilder.java index 6e06ba6..acdc984 100644 --- a/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/builder/QueryBuilder.java +++ b/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/builder/QueryBuilder.java @@ -26,6 +26,8 @@ import javax.persistence.LockModeType; import javax.persistence.Query; import javax.persistence.QueryHint; +import org.apache.deltaspike.data.api.QueryResult; +import org.apache.deltaspike.data.api.mapping.QueryInOutMapper; import org.apache.deltaspike.data.impl.handler.CdiQueryInvocationContext; import org.apache.deltaspike.data.impl.param.Parameters; @@ -49,7 +51,24 @@ public abstract class QueryBuilder return MessageFormat.format(QUERY_COUNT, entityName); } - public abstract Object execute(CdiQueryInvocationContext ctx); + @SuppressWarnings("unchecked") + public Object executeQuery(CdiQueryInvocationContext context) + { + Object result = execute(context); + if (!isUnmappableResult(result) && context.hasQueryInOutMapper()) + { + QueryInOutMapper<Object> mapper = (QueryInOutMapper<Object>) + context.getQueryInOutMapper(); + if (result instanceof List) + { + return mapper.mapResultList((List<Object>) result); + } + return mapper.mapResult(result); + } + return result; + } + + protected abstract Object execute(CdiQueryInvocationContext ctx); protected boolean returnsList(Method method) { @@ -116,4 +135,10 @@ public abstract class QueryBuilder return query; } + private boolean isUnmappableResult(Object result) + { + return result instanceof QueryResult || + result instanceof Query; + } + } http://git-wip-us.apache.org/repos/asf/deltaspike/blob/c75adbb9/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/builder/result/DefaultQueryResult.java ---------------------------------------------------------------------- diff --git a/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/builder/result/DefaultQueryResult.java b/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/builder/result/DefaultQueryResult.java index c6b7c72..a0fafb3 100644 --- a/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/builder/result/DefaultQueryResult.java +++ b/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/builder/result/DefaultQueryResult.java @@ -175,14 +175,14 @@ public class DefaultQueryResult<T> implements QueryResult<T> @SuppressWarnings("unchecked") public List<T> getResultList() { - return ((Query) builder.execute(context)).getResultList(); + return ((Query) builder.executeQuery(context)).getResultList(); } @Override @SuppressWarnings("unchecked") public T getSingleResult() { - return (T) ((Query) builder.execute(context)).getSingleResult(); + return (T) ((Query) builder.executeQuery(context)).getSingleResult(); } @Override @@ -192,7 +192,7 @@ public class DefaultQueryResult<T> implements QueryResult<T> context.addJpaQueryPostProcessor(counter); try { - Long result = (Long) ((Query) builder.execute(context)).getSingleResult(); + Long result = (Long) ((Query) builder.executeQuery(context)).getSingleResult(); return result.intValue(); } catch (RuntimeException e) http://git-wip-us.apache.org/repos/asf/deltaspike/blob/c75adbb9/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/handler/CdiQueryInvocationContext.java ---------------------------------------------------------------------- diff --git a/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/handler/CdiQueryInvocationContext.java b/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/handler/CdiQueryInvocationContext.java index ac130f3..c4dd208 100644 --- a/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/handler/CdiQueryInvocationContext.java +++ b/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/handler/CdiQueryInvocationContext.java @@ -25,6 +25,7 @@ import java.util.List; import javax.persistence.EntityManager; import javax.persistence.Query; +import org.apache.deltaspike.data.api.mapping.QueryInOutMapper; import org.apache.deltaspike.data.impl.meta.RepositoryMethod; import org.apache.deltaspike.data.impl.param.Parameters; import org.apache.deltaspike.data.impl.util.bean.Destroyable; @@ -61,6 +62,22 @@ public class CdiQueryInvocationContext implements QueryInvocationContext this.cleanup = new LinkedList<Destroyable>(); } + public void init() + { + if (hasQueryInOutMapper()) + { + QueryInOutMapper<?> mapper = getQueryInOutMapper(); + params.applyMapper(mapper); + for (int i = 0; i < args.length; i++) + { + if (mapper.mapsParameter(args[i])) + { + args[i] = mapper.mapParameter(args[i]); + } + } + } + } + @Override public EntityManager getEntityManager() { @@ -186,4 +203,14 @@ public class CdiQueryInvocationContext implements QueryInvocationContext return queryPostProcessors; } + public boolean hasQueryInOutMapper() + { + return repoMethod.hasQueryInOutMapper(); + } + + public QueryInOutMapper<?> getQueryInOutMapper() + { + return repoMethod.getQueryInOutMapperInstance(this); + } + } http://git-wip-us.apache.org/repos/asf/deltaspike/blob/c75adbb9/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/handler/QueryHandler.java ---------------------------------------------------------------------- diff --git a/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/handler/QueryHandler.java b/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/handler/QueryHandler.java index 8b4e522..809a9a8 100755 --- a/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/handler/QueryHandler.java +++ b/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/handler/QueryHandler.java @@ -73,7 +73,7 @@ public class QueryHandler implements Serializable, InvocationHandler RepositoryMethod repoMethod = components.lookupMethod(repo.getRepositoryClass(), method); queryContext = createContext(proxy, method, args, repo, repoMethod); QueryBuilder builder = queryBuilder.build(repoMethod, queryContext); - Object result = builder.execute(queryContext); + Object result = builder.executeQuery(queryContext); return result; } catch (Exception e) @@ -96,6 +96,7 @@ public class QueryHandler implements Serializable, InvocationHandler { CdiQueryInvocationContext queryContext = new CdiQueryInvocationContext(proxy, method, args, repoMethod, entityManagerLookup.lookupFor(repo)); + queryContext.init(); context.set(queryContext); return queryContext; } http://git-wip-us.apache.org/repos/asf/deltaspike/blob/c75adbb9/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/meta/RepositoryMethod.java ---------------------------------------------------------------------- diff --git a/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/meta/RepositoryMethod.java b/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/meta/RepositoryMethod.java index 24b9a1b..ec79d3f 100644 --- a/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/meta/RepositoryMethod.java +++ b/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/meta/RepositoryMethod.java @@ -20,14 +20,26 @@ package org.apache.deltaspike.data.impl.meta; import static org.apache.deltaspike.data.impl.util.QueryUtils.isNotEmpty; +import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.util.Set; +import javax.enterprise.inject.spi.Bean; +import javax.enterprise.inject.spi.BeanManager; + +import org.apache.deltaspike.core.api.provider.BeanManagerProvider; +import org.apache.deltaspike.core.api.provider.BeanProvider; +import org.apache.deltaspike.core.api.provider.DependentProvider; import org.apache.deltaspike.data.api.Query; +import org.apache.deltaspike.data.api.mapping.MappingConfig; +import org.apache.deltaspike.data.api.mapping.QueryInOutMapper; import org.apache.deltaspike.data.impl.builder.MethodExpressionException; import org.apache.deltaspike.data.impl.builder.part.QueryRoot; import org.apache.deltaspike.data.impl.builder.result.QueryProcessor; import org.apache.deltaspike.data.impl.builder.result.QueryProcessorFactory; +import org.apache.deltaspike.data.impl.handler.CdiQueryInvocationContext; +import org.apache.deltaspike.data.impl.util.bean.DependentProviderDestroyable; /** * Stores information about a specific method of a Repository: @@ -46,6 +58,9 @@ public class RepositoryMethod private final RepositoryComponent repo; private final QueryRoot queryRoot; private final QueryProcessor queryProcessor; + private final Class<? extends QueryInOutMapper> mapper; + + private volatile Boolean queryInOutMapperIsNormalScope; public RepositoryMethod(Method method, RepositoryComponent repo) { @@ -54,6 +69,7 @@ public class RepositoryMethod this.methodType = extractMethodType(); this.queryRoot = initQueryRoot(); this.queryProcessor = QueryProcessorFactory.newInstance(method).build(); + this.mapper = extractMapper(method, repo); } public boolean returns(Class<?> returnType) @@ -61,6 +77,27 @@ public class RepositoryMethod return returnType.equals(method.getReturnType()); } + public QueryInOutMapper<?> getQueryInOutMapperInstance(CdiQueryInvocationContext context) + { + if (!hasQueryInOutMapper()) + { + return null; + } + QueryInOutMapper<?> result = null; + lazyInit(); + if (!queryInOutMapperIsNormalScope) + { + final DependentProvider<? extends QueryInOutMapper> mappedProvider = BeanProvider.getDependent(mapper); + result = mappedProvider.get(); + context.addDestroyable(new DependentProviderDestroyable(mappedProvider)); + } + else + { + result = BeanProvider.getContextualReference(mapper); + } + return result; + } + private MethodType extractMethodType() { if (isAnnotated()) @@ -115,6 +152,47 @@ public class RepositoryMethod } } + private Class<? extends QueryInOutMapper> extractMapper(Method queryMethod, RepositoryComponent repoComponent) + { + if (queryMethod.isAnnotationPresent(MappingConfig.class)) + { + return queryMethod.getAnnotation(MappingConfig.class).value(); + } + if (repoComponent.getRepositoryClass().isAnnotationPresent(MappingConfig.class)) + { + return repoComponent.getRepositoryClass().getAnnotation(MappingConfig.class).value(); + } + return null; + } + + //don't trigger this lookup during ProcessAnnotatedType + private void lazyInit() + { + if (queryInOutMapperIsNormalScope == null) + { + init(BeanManagerProvider.getInstance().getBeanManager()); + } + } + + private synchronized void init(BeanManager beanManager) + { + if (queryInOutMapperIsNormalScope != null) + { + return; + } + + if (queryInOutMapperIsNormalScope != null && beanManager != null) + { + final Set<Bean<?>> beans = beanManager.getBeans(mapper); + final Class<? extends Annotation> scope = beanManager.resolve(beans).getScope(); + queryInOutMapperIsNormalScope = beanManager.isNormalScope(scope); + } + else + { + queryInOutMapperIsNormalScope = false; + } + } + public MethodType getMethodType() { return methodType; @@ -135,4 +213,9 @@ public class RepositoryMethod return queryProcessor; } + public boolean hasQueryInOutMapper() + { + return mapper != null; + } + } http://git-wip-us.apache.org/repos/asf/deltaspike/blob/c75adbb9/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/param/IndexedParameter.java ---------------------------------------------------------------------- diff --git a/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/param/IndexedParameter.java b/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/param/IndexedParameter.java index 9c249e5..0b81cad 100644 --- a/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/param/IndexedParameter.java +++ b/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/param/IndexedParameter.java @@ -37,7 +37,7 @@ public class IndexedParameter extends Parameter @Override public void apply(Query query) { - query.setParameter(index, value); + query.setParameter(index, queryValue()); } } http://git-wip-us.apache.org/repos/asf/deltaspike/blob/c75adbb9/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/param/NamedParameter.java ---------------------------------------------------------------------- diff --git a/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/param/NamedParameter.java b/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/param/NamedParameter.java index a81e9e9..60f8504 100644 --- a/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/param/NamedParameter.java +++ b/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/param/NamedParameter.java @@ -37,7 +37,7 @@ public class NamedParameter extends Parameter @Override public void apply(Query query) { - query.setParameter(name, value); + query.setParameter(name, queryValue()); } } http://git-wip-us.apache.org/repos/asf/deltaspike/blob/c75adbb9/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/param/Parameter.java ---------------------------------------------------------------------- diff --git a/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/param/Parameter.java b/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/param/Parameter.java index ab7901f..b85b26c 100644 --- a/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/param/Parameter.java +++ b/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/param/Parameter.java @@ -18,17 +18,25 @@ */ package org.apache.deltaspike.data.impl.param; +import java.util.logging.Level; +import java.util.logging.Logger; + import javax.persistence.Query; +import org.apache.deltaspike.data.api.mapping.QueryInOutMapper; + /** * Base class for parameters. */ public abstract class Parameter { + private static final Logger LOG = Logger.getLogger(Parameter.class.getName()); protected final Object value; protected final int argIndex; + protected Object mappedValue = null; + public Parameter(Object value, int argIndex) { this.value = value; @@ -37,4 +45,18 @@ public abstract class Parameter public abstract void apply(Query query); + public void applyMapper(QueryInOutMapper<?> mapper) + { + if (mapper.mapsParameter(value)) + { + mappedValue = mapper.mapParameter(value); + LOG.log(Level.FINE, "Converting param {0} to {1}", new Object[] { value, mappedValue }); + } + } + + protected Object queryValue() + { + return mappedValue != null ? mappedValue : value; + } + } http://git-wip-us.apache.org/repos/asf/deltaspike/blob/c75adbb9/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/param/Parameters.java ---------------------------------------------------------------------- diff --git a/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/param/Parameters.java b/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/param/Parameters.java index 6804251..9e6c20a 100644 --- a/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/param/Parameters.java +++ b/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/param/Parameters.java @@ -31,6 +31,7 @@ import javax.persistence.Query; import org.apache.deltaspike.data.api.FirstResult; import org.apache.deltaspike.data.api.MaxResults; import org.apache.deltaspike.data.api.QueryParam; +import org.apache.deltaspike.data.api.mapping.QueryInOutMapper; /** * Convenience class to manage method and query parameters. @@ -90,6 +91,14 @@ public final class Parameters return new Parameters(result, max, first); } + public void applyMapper(QueryInOutMapper<?> mapper) + { + for (Parameter param : parameterList) + { + param.applyMapper(mapper); + } + } + public Query applyTo(Query query) { for (Parameter param : parameterList) http://git-wip-us.apache.org/repos/asf/deltaspike/blob/c75adbb9/deltaspike/modules/data/impl/src/test/java/org/apache/deltaspike/data/impl/mapping/MappedRepositoryTest.java ---------------------------------------------------------------------- diff --git a/deltaspike/modules/data/impl/src/test/java/org/apache/deltaspike/data/impl/mapping/MappedRepositoryTest.java b/deltaspike/modules/data/impl/src/test/java/org/apache/deltaspike/data/impl/mapping/MappedRepositoryTest.java new file mode 100644 index 0000000..38345f9 --- /dev/null +++ b/deltaspike/modules/data/impl/src/test/java/org/apache/deltaspike/data/impl/mapping/MappedRepositoryTest.java @@ -0,0 +1,163 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.deltaspike.data.impl.mapping; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.util.List; + +import javax.enterprise.inject.Produces; +import javax.inject.Inject; +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; + +import org.apache.deltaspike.data.test.TransactionalTestCase; +import org.apache.deltaspike.data.test.domain.Simple; +import org.apache.deltaspike.data.test.domain.Simple_; +import org.apache.deltaspike.data.test.domain.dto.BooleanWrapper; +import org.apache.deltaspike.data.test.domain.dto.SimpleDto; +import org.apache.deltaspike.data.test.service.SimpleMappedRepository; +import org.apache.deltaspike.data.test.service.SimpleMapper; +import org.apache.deltaspike.data.test.service.WrappedMapper; +import org.apache.deltaspike.data.test.util.TestDeployments; +import org.apache.deltaspike.test.category.WebProfileCategory; +import org.jboss.arquillian.container.test.api.Deployment; +import org.jboss.shrinkwrap.api.Archive; +import org.junit.Test; +import org.junit.experimental.categories.Category; + +@Category(WebProfileCategory.class) +public class MappedRepositoryTest extends TransactionalTestCase +{ + + @Deployment + public static Archive<?> deployment() + { + return TestDeployments.initDeployment() + .addClasses( + SimpleMappedRepository.class, + SimpleMapper.class, + WrappedMapper.class) + .addPackages(false, + Simple.class.getPackage(), + SimpleDto.class.getPackage()); + } + + @Inject + private SimpleMappedRepository repository; + + @Produces + @PersistenceContext + private EntityManager entityManager; + + @Test + public void should_map_entityrepo_methods() + { + // given + SimpleDto dto = new SimpleDto(); + dto.setName("should_map_entityrepo_methods"); + dto.setEnabled(Boolean.TRUE); + + // when + SimpleDto saved = repository.saveAndFlush(dto); + SimpleDto loadedDto = repository.findBy(saved.getId()); + Simple loaded = entityManager.find(Simple.class, saved.getId().getId()); + + // then + assertNotNull(loadedDto); + assertNotNull(loaded); + assertEquals(saved.getName(), loaded.getName()); + assertEquals(saved.getEnabled(), loaded.getEnabled()); + } + + @Test + public void should_map_method_expression() + { + // given + Simple simple = new Simple("should_map_method_expression"); + simple.setEnabled(Boolean.TRUE); + entityManager.persist(simple); + + // when + List<SimpleDto> result = repository.findByEnabled(Boolean.TRUE); + + // then + boolean found = false; + for (SimpleDto dto : result) + { + if (dto.getName().equals(simple.getName())) + { + found = true; + break; + } + } + assertTrue(found); + } + + @Test + public void should_override_class_config_with_method_config() + { + // given + Simple simple = new Simple("should_map_method_expression"); + simple.setEnabled(Boolean.TRUE); + entityManager.persist(simple); + + // when + List<SimpleDto> result = repository.findByEnabled(new BooleanWrapper(Boolean.TRUE)); + + // then + boolean found = false; + for (SimpleDto dto : result) + { + if (dto.getName().equals(simple.getName())) + { + found = true; + break; + } + } + assertTrue(found); + } + + @Test + public void should_find_with_queryresult() + { + // given + final String name = "should_find_with_queryresult"; + Simple simple = new Simple(name); + simple.setEnabled(Boolean.TRUE); + entityManager.persist(simple); + + // when + List<SimpleDto> result = repository.findByNameToo(name) + .changeOrder(Simple_.name.getName()) + .getResultList(); + + // then + assertTrue(result.size() > 0); + } + + @Override + protected EntityManager getEntityManager() + { + return entityManager; + } + +} http://git-wip-us.apache.org/repos/asf/deltaspike/blob/c75adbb9/deltaspike/modules/data/impl/src/test/java/org/apache/deltaspike/data/test/domain/Simple.java ---------------------------------------------------------------------- diff --git a/deltaspike/modules/data/impl/src/test/java/org/apache/deltaspike/data/test/domain/Simple.java b/deltaspike/modules/data/impl/src/test/java/org/apache/deltaspike/data/test/domain/Simple.java index 6e5b3b1..4f38f4d 100755 --- a/deltaspike/modules/data/impl/src/test/java/org/apache/deltaspike/data/test/domain/Simple.java +++ b/deltaspike/modules/data/impl/src/test/java/org/apache/deltaspike/data/test/domain/Simple.java @@ -137,4 +137,11 @@ public class Simple this.embedded = embedded; } + @Override + public String toString() + { + return "Simple [id=" + id + ", name=" + name + ", camelCase=" + camelCase + ", enabled=" + enabled + + ", counter=" + counter + ", temporal=" + temporal + ", embedded=" + embedded + "]"; + } + } http://git-wip-us.apache.org/repos/asf/deltaspike/blob/c75adbb9/deltaspike/modules/data/impl/src/test/java/org/apache/deltaspike/data/test/domain/dto/BooleanWrapper.java ---------------------------------------------------------------------- diff --git a/deltaspike/modules/data/impl/src/test/java/org/apache/deltaspike/data/test/domain/dto/BooleanWrapper.java b/deltaspike/modules/data/impl/src/test/java/org/apache/deltaspike/data/test/domain/dto/BooleanWrapper.java new file mode 100644 index 0000000..40652ee --- /dev/null +++ b/deltaspike/modules/data/impl/src/test/java/org/apache/deltaspike/data/test/domain/dto/BooleanWrapper.java @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.deltaspike.data.test.domain.dto; + +public class BooleanWrapper +{ + private Boolean wrapped; + + public BooleanWrapper(Boolean wrapped) + { + this.wrapped = wrapped; + } + + public Boolean getWrapped() + { + return wrapped; + } + + public void setWrapped(Boolean wrapped) + { + this.wrapped = wrapped; + } + +} http://git-wip-us.apache.org/repos/asf/deltaspike/blob/c75adbb9/deltaspike/modules/data/impl/src/test/java/org/apache/deltaspike/data/test/domain/dto/SimpleDto.java ---------------------------------------------------------------------- diff --git a/deltaspike/modules/data/impl/src/test/java/org/apache/deltaspike/data/test/domain/dto/SimpleDto.java b/deltaspike/modules/data/impl/src/test/java/org/apache/deltaspike/data/test/domain/dto/SimpleDto.java new file mode 100644 index 0000000..15fe6ce --- /dev/null +++ b/deltaspike/modules/data/impl/src/test/java/org/apache/deltaspike/data/test/domain/dto/SimpleDto.java @@ -0,0 +1,57 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.deltaspike.data.test.domain.dto; + +public class SimpleDto +{ + private SimpleId id; + private String name; + private Boolean enabled; + + public SimpleId getId() + { + return id; + } + + public void setId(SimpleId id) + { + this.id = id; + } + + public String getName() + { + return name; + } + + public void setName(String name) + { + this.name = name; + } + + public Boolean getEnabled() + { + return enabled; + } + + public void setEnabled(Boolean enabled) + { + this.enabled = enabled; + } + +} http://git-wip-us.apache.org/repos/asf/deltaspike/blob/c75adbb9/deltaspike/modules/data/impl/src/test/java/org/apache/deltaspike/data/test/domain/dto/SimpleId.java ---------------------------------------------------------------------- diff --git a/deltaspike/modules/data/impl/src/test/java/org/apache/deltaspike/data/test/domain/dto/SimpleId.java b/deltaspike/modules/data/impl/src/test/java/org/apache/deltaspike/data/test/domain/dto/SimpleId.java new file mode 100644 index 0000000..85c44b5 --- /dev/null +++ b/deltaspike/modules/data/impl/src/test/java/org/apache/deltaspike/data/test/domain/dto/SimpleId.java @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.deltaspike.data.test.domain.dto; + +import java.io.Serializable; + +public class SimpleId implements Serializable +{ + + private static final long serialVersionUID = 1L; + + private Long id; + + public SimpleId(Long id) + { + this.id = id; + } + + public Long getId() + { + return id; + } + + public void setId(Long id) + { + this.id = id; + } + +} http://git-wip-us.apache.org/repos/asf/deltaspike/blob/c75adbb9/deltaspike/modules/data/impl/src/test/java/org/apache/deltaspike/data/test/service/SimpleMappedRepository.java ---------------------------------------------------------------------- diff --git a/deltaspike/modules/data/impl/src/test/java/org/apache/deltaspike/data/test/service/SimpleMappedRepository.java b/deltaspike/modules/data/impl/src/test/java/org/apache/deltaspike/data/test/service/SimpleMappedRepository.java new file mode 100644 index 0000000..40d19f4 --- /dev/null +++ b/deltaspike/modules/data/impl/src/test/java/org/apache/deltaspike/data/test/service/SimpleMappedRepository.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.deltaspike.data.test.service; + +import java.util.List; + +import org.apache.deltaspike.data.api.EntityRepository; +import org.apache.deltaspike.data.api.Query; +import org.apache.deltaspike.data.api.QueryResult; +import org.apache.deltaspike.data.api.Repository; +import org.apache.deltaspike.data.api.mapping.MappingConfig; +import org.apache.deltaspike.data.test.domain.Simple; +import org.apache.deltaspike.data.test.domain.dto.BooleanWrapper; +import org.apache.deltaspike.data.test.domain.dto.SimpleDto; +import org.apache.deltaspike.data.test.domain.dto.SimpleId; + +@Repository(forEntity = Simple.class) +@MappingConfig(SimpleMapper.class) +public interface SimpleMappedRepository extends EntityRepository<SimpleDto, SimpleId> +{ + + SimpleDto findByName(String name); + + List<SimpleDto> findByEnabled(Boolean enabled); + + @MappingConfig(WrappedMapper.class) + List<SimpleDto> findByEnabled(BooleanWrapper enabled); + + @Query("select e from Simple e where e.name = ?1") + QueryResult<SimpleDto> findByNameToo(String name); + +} http://git-wip-us.apache.org/repos/asf/deltaspike/blob/c75adbb9/deltaspike/modules/data/impl/src/test/java/org/apache/deltaspike/data/test/service/SimpleMapper.java ---------------------------------------------------------------------- diff --git a/deltaspike/modules/data/impl/src/test/java/org/apache/deltaspike/data/test/service/SimpleMapper.java b/deltaspike/modules/data/impl/src/test/java/org/apache/deltaspike/data/test/service/SimpleMapper.java new file mode 100644 index 0000000..51baaee --- /dev/null +++ b/deltaspike/modules/data/impl/src/test/java/org/apache/deltaspike/data/test/service/SimpleMapper.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.deltaspike.data.test.service; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.deltaspike.data.api.mapping.QueryInOutMapper; +import org.apache.deltaspike.data.test.domain.Simple; +import org.apache.deltaspike.data.test.domain.dto.SimpleDto; +import org.apache.deltaspike.data.test.domain.dto.SimpleId; + +public class SimpleMapper implements QueryInOutMapper<Simple> +{ + + @Override + public Object mapResult(Simple result) + { + SimpleDto dto = new SimpleDto(); + dto.setId(new SimpleId(result.getId())); + dto.setName(result.getName()); + dto.setEnabled(result.getEnabled()); + return dto; + } + + @Override + public Object mapResultList(List<Simple> result) + { + List<SimpleDto> dtos = new ArrayList<SimpleDto>(result.size()); + for (Simple simple : result) + { + dtos.add((SimpleDto) mapResult(simple)); + } + return dtos; + } + + @Override + public boolean mapsParameter(Object parameter) + { + return parameter != null && ( + parameter instanceof SimpleDto || parameter instanceof SimpleId); + } + + @Override + public Object mapParameter(Object parameter) + { + if (parameter instanceof SimpleDto) + { + SimpleDto dto = (SimpleDto) parameter; + Simple simple = new Simple(); + simple.setId(dto.getId() != null ? dto.getId().getId() : null); + simple.setEnabled(dto.getEnabled()); + simple.setName(dto.getName()); + System.out.println("Converted " + simple); + return simple; + } + return ((SimpleId) parameter).getId(); + } + +} http://git-wip-us.apache.org/repos/asf/deltaspike/blob/c75adbb9/deltaspike/modules/data/impl/src/test/java/org/apache/deltaspike/data/test/service/WrappedMapper.java ---------------------------------------------------------------------- diff --git a/deltaspike/modules/data/impl/src/test/java/org/apache/deltaspike/data/test/service/WrappedMapper.java b/deltaspike/modules/data/impl/src/test/java/org/apache/deltaspike/data/test/service/WrappedMapper.java new file mode 100644 index 0000000..a6ef242 --- /dev/null +++ b/deltaspike/modules/data/impl/src/test/java/org/apache/deltaspike/data/test/service/WrappedMapper.java @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.deltaspike.data.test.service; + +import java.util.List; + +import org.apache.deltaspike.data.api.mapping.QueryInOutMapper; +import org.apache.deltaspike.data.test.domain.Simple; +import org.apache.deltaspike.data.test.domain.dto.BooleanWrapper; + +public class WrappedMapper implements QueryInOutMapper<Simple> +{ + private final SimpleMapper delegate = new SimpleMapper(); + + @Override + public boolean mapsParameter(Object parameter) + { + if (parameter != null && parameter instanceof BooleanWrapper) + { + return true; + } + return delegate.mapsParameter(parameter); + } + + @Override + public Object mapParameter(Object parameter) + { + if (parameter instanceof BooleanWrapper) + { + return ((BooleanWrapper) parameter).getWrapped(); + } + return delegate.mapParameter(parameter); + } + + @Override + public Object mapResult(Simple result) + { + return delegate.mapResult(result); + } + + @Override + public Object mapResultList(List<Simple> result) + { + return delegate.mapResultList(result); + } + +} http://git-wip-us.apache.org/repos/asf/deltaspike/blob/c75adbb9/deltaspike/modules/data/impl/src/test/java/org/apache/deltaspike/data/test/util/TestDeployments.java ---------------------------------------------------------------------- diff --git a/deltaspike/modules/data/impl/src/test/java/org/apache/deltaspike/data/test/util/TestDeployments.java b/deltaspike/modules/data/impl/src/test/java/org/apache/deltaspike/data/test/util/TestDeployments.java index b55b1e4..7d42d8c 100755 --- a/deltaspike/modules/data/impl/src/test/java/org/apache/deltaspike/data/test/util/TestDeployments.java +++ b/deltaspike/modules/data/impl/src/test/java/org/apache/deltaspike/data/test/util/TestDeployments.java @@ -36,6 +36,8 @@ import org.apache.deltaspike.data.api.audit.ModifiedOn; import org.apache.deltaspike.data.api.criteria.Criteria; import org.apache.deltaspike.data.api.criteria.CriteriaSupport; import org.apache.deltaspike.data.api.criteria.QuerySelection; +import org.apache.deltaspike.data.api.mapping.MappingConfig; +import org.apache.deltaspike.data.api.mapping.QueryInOutMapper; import org.apache.deltaspike.data.impl.RepositoryDefinitionException; import org.apache.deltaspike.data.impl.RepositoryExtension; import org.apache.deltaspike.data.impl.audit.AuditEntityListener; @@ -122,6 +124,7 @@ public abstract class TestDeployments EntityManagerConfig.class, EntityManagerResolver.class) .addClasses(Criteria.class, QuerySelection.class, CriteriaSupport.class) .addClasses(CreatedOn.class, CurrentUser.class, ModifiedBy.class, ModifiedOn.class) + .addClasses(MappingConfig.class, QueryInOutMapper.class) .addClasses(DelegateQueryHandler.class, QueryInvocationContext.class); }