From 27a7b0bc29540f60e8a44548b99d539337efc4b2 Mon Sep 17 00:00:00 2001 From: Remo Date: Thu, 6 Oct 2016 11:43:43 +0200 Subject: [PATCH] #15 JPA adapter extensibility (#145) - DTO mapping support with JpaModule.addMappedEntityClass - JpaMapper class to map entities to DTO and back - JpaMapper makes use of Tuple API to support computed attributes - virtual attributes renamed to computed attributes - JpaRepositoryFilter to intercept most stages within the jpa repository to allow further customization - JpaModule.addFilter(...) to register filters - test case showning use of computed attributes and dto mappers - subquery comparable fix/workaround for computed attrs - JpaModule constructors replaced by static create methods pending: - optimize reuse of computed attributes between select, filter and sort --- .../internal/BaseResponseDeserializer.java | 474 +++++----- .../controller/resource/ResourceUpsert.java | 543 +++++------ .../resource/mock/models/Project.java | 196 ++-- .../jpa/DefaultJpaRepositoryFactory.java | 53 +- .../io/katharsis/jpa/JpaEntityRepository.java | 274 +++--- .../main/java/io/katharsis/jpa/JpaModule.java | 850 +++++++++++------- .../jpa/JpaRelationshipRepository.java | 489 +++++----- .../katharsis/jpa/JpaRepositoryFactory.java | 78 +- .../io/katharsis/jpa/JpaRepositoryFilter.java | 70 ++ .../jpa/JpaRepositoryFilterBase.java | 44 + .../jpa/internal/JpaQueryFactoryBase.java | 54 +- .../jpa/internal/JpaRepositoryBase.java | 99 ++ .../jpa/internal/JpaRepositoryUtils.java | 122 +-- .../impl/AbstractMetaEntityAttributeImpl.java | 282 +++--- .../meta/impl/AbstractMetaEntityImpl.java | 164 ++-- .../internal/meta/impl/MetaArrayTypeImpl.java | 52 +- .../internal/meta/impl/MetaAttributeImpl.java | 250 +++--- .../meta/impl/MetaCollectionTypeImpl.java | 82 +- .../meta/impl/MetaDataObjectImpl.java | 754 ++++++++-------- .../internal/meta/impl/MetaElementImpl.java | 148 +-- .../impl/MetaEmbeddableAttributeImpl.java | 38 +- .../meta/impl/MetaEmbeddableImpl.java | 84 +- .../meta/impl/MetaEntityAttributeImpl.java | 20 +- .../internal/meta/impl/MetaEntityImpl.java | 24 +- .../jpa/internal/meta/impl/MetaKeyImpl.java | 276 +++--- .../internal/meta/impl/MetaListTypeImpl.java | 26 +- .../meta/impl/MetaMapAttributeImpl.java | 222 ++--- .../impl/MetaMapAttributeProjectionImpl.java | 48 +- .../internal/meta/impl/MetaMapTypeImpl.java | 68 +- .../meta/impl/MetaMappedSuperclassImpl.java | 22 +- .../internal/meta/impl/MetaPrimitiveType.java | 134 +-- .../internal/meta/impl/MetaResourceImpl.java | 104 ++- .../internal/meta/impl/MetaSetTypeImpl.java | 26 +- .../jpa/internal/meta/impl/MetaTypeImpl.java | 172 ++-- .../internal/query/AbstractJpaQueryImpl.java | 428 ++++----- ...ava => ComputedAttributeRegistryImpl.java} | 119 +-- ...ribute.java => MetaComputedAttribute.java} | 66 +- .../jpa/internal/query/QueryBuilder.java | 262 +++--- .../internal/query/QueryFilterBuilder.java | 306 +++---- .../criteria/JpaCriteriaQueryBackend.java | 674 +++++++------- .../JpaCriteriaQueryExecutorImpl.java | 212 ++--- .../criteria/JpaCriteriaQueryImpl.java | 90 +- .../querydsl/QuerydslExecutorImpl.java | 154 ++-- .../querydsl/QuerydslQueryBackend.java | 784 ++++++++-------- .../backend/querydsl/QuerydslQueryImpl.java | 98 +- .../querydsl/SingleObjectTupleImpl.java | 71 ++ .../katharsis/jpa/mapping/IdentityMapper.java | 24 + .../io/katharsis/jpa/mapping/JpaMapper.java | 30 + .../jpa/query/ComputedAttributeRegistry.java | 16 + .../katharsis/jpa/query/JpaQueryFactory.java | 41 +- .../criteria/JpaCriteriaQueryFactory.java | 75 +- .../query/querydsl/QuerydslQueryFactory.java | 76 +- .../katharsis/jpa/AbstractJpaJerseyTest.java | 214 ++--- .../jpa/JpaPartialEntityExposureTest.java | 163 ++-- .../jpa/JpaQueryParamsEndToEndTest.java | 480 +++++----- .../jpa/JpaQuerySpecEndToEndTest.java | 778 ++++++++-------- .../katharsis/jpa/mapping/DtoMappingTest.java | 265 ++++++ .../jpa/mapping/RelatedDTOMapper.java | 36 + .../katharsis/jpa/mapping/TestDTOMapper.java | 45 + .../katharsis/jpa/model/dto/RelatedDTO.java | 29 + .../io/katharsis/jpa/model/dto/TestDTO.java | 82 ++ .../katharsis/jpa/query/AbstractJpaTest.java | 378 ++++---- ...se.java => ComputedAttributeTestBase.java} | 94 +- ...ava => ComputedAttributeCriteriaTest.java} | 66 +- ...ava => ComputedAttributeQuerydslTest.java} | 60 +- .../jpa/repository/JpaListenerTest.java | 95 ++ 66 files changed, 7023 insertions(+), 5630 deletions(-) create mode 100644 katharsis-jpa/src/main/java/io/katharsis/jpa/JpaRepositoryFilter.java create mode 100644 katharsis-jpa/src/main/java/io/katharsis/jpa/JpaRepositoryFilterBase.java create mode 100644 katharsis-jpa/src/main/java/io/katharsis/jpa/internal/JpaRepositoryBase.java rename katharsis-jpa/src/main/java/io/katharsis/jpa/internal/query/{VirtualAttributeRegistry.java => ComputedAttributeRegistryImpl.java} (58%) rename katharsis-jpa/src/main/java/io/katharsis/jpa/internal/query/{MetaVirtualAttribute.java => MetaComputedAttribute.java} (78%) create mode 100644 katharsis-jpa/src/main/java/io/katharsis/jpa/internal/query/backend/querydsl/SingleObjectTupleImpl.java create mode 100644 katharsis-jpa/src/main/java/io/katharsis/jpa/mapping/IdentityMapper.java create mode 100644 katharsis-jpa/src/main/java/io/katharsis/jpa/mapping/JpaMapper.java create mode 100644 katharsis-jpa/src/main/java/io/katharsis/jpa/query/ComputedAttributeRegistry.java create mode 100644 katharsis-jpa/src/test/java/io/katharsis/jpa/mapping/DtoMappingTest.java create mode 100644 katharsis-jpa/src/test/java/io/katharsis/jpa/mapping/RelatedDTOMapper.java create mode 100644 katharsis-jpa/src/test/java/io/katharsis/jpa/mapping/TestDTOMapper.java create mode 100644 katharsis-jpa/src/test/java/io/katharsis/jpa/model/dto/RelatedDTO.java create mode 100644 katharsis-jpa/src/test/java/io/katharsis/jpa/model/dto/TestDTO.java rename katharsis-jpa/src/test/java/io/katharsis/jpa/query/{VirtualAttributeTestBase.java => ComputedAttributeTestBase.java} (91%) rename katharsis-jpa/src/test/java/io/katharsis/jpa/query/criteria/{VirtualAttributeCriteriaTest.java => ComputedAttributeCriteriaTest.java} (79%) rename katharsis-jpa/src/test/java/io/katharsis/jpa/query/querydsl/{VirtualAttributeQuerydslTest.java => ComputedAttributeQuerydslTest.java} (73%) create mode 100644 katharsis-jpa/src/test/java/io/katharsis/jpa/repository/JpaListenerTest.java diff --git a/katharsis-client/src/main/java/io/katharsis/client/internal/BaseResponseDeserializer.java b/katharsis-client/src/main/java/io/katharsis/client/internal/BaseResponseDeserializer.java index 7f4223a9..29831076 100644 --- a/katharsis-client/src/main/java/io/katharsis/client/internal/BaseResponseDeserializer.java +++ b/katharsis-client/src/main/java/io/katharsis/client/internal/BaseResponseDeserializer.java @@ -1,219 +1,255 @@ -package io.katharsis.client.internal; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; - -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; - -import io.katharsis.client.response.JsonLinksInformation; -import io.katharsis.client.response.JsonMetaInformation; -import io.katharsis.dispatcher.controller.resource.ResourceUpsert; -import io.katharsis.jackson.exception.JsonDeserializationException; -import io.katharsis.queryspec.internal.QueryAdapter; -import io.katharsis.repository.RepositoryMethodParameterProvider; -import io.katharsis.request.dto.DataBody; -import io.katharsis.request.dto.RequestBody; -import io.katharsis.request.path.JsonPath; -import io.katharsis.resource.information.ResourceInformation; -import io.katharsis.resource.registry.RegistryEntry; -import io.katharsis.resource.registry.ResourceRegistry; -import io.katharsis.response.BaseResponseContext; -import io.katharsis.response.CollectionResponseContext; -import io.katharsis.response.JsonApiResponse; -import io.katharsis.response.LinksInformation; -import io.katharsis.response.MetaInformation; -import io.katharsis.response.ResourceResponseContext; -import io.katharsis.utils.parser.TypeParser; - -/** - * Deerializes top-level JSON object and provides ability to include compound documents - * - * TODO could this be merged with RequestBodyDeserializer? - */ -public class BaseResponseDeserializer extends JsonDeserializer { - - private static final String INCLUDED_FIELD_NAME = "included"; - private static final String DATA_FIELD_NAME = "data"; - private static final String META_FIELD_NAME = "meta"; - private static final String LINKS_FIELD_NAME = "links"; - - private ResourceRegistry resourceRegistry; - private ObjectMapper objectMapper; - - private TypeParser typeParser = new TypeParser(); - - public BaseResponseDeserializer(ResourceRegistry resourceRegistry, ObjectMapper objectMapper) { - this.resourceRegistry = resourceRegistry; - this.objectMapper = objectMapper; - } - - @Override - public BaseResponseContext deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException { - - JsonNode node = jp.readValueAsTree(); - if (node == null) { - return null; - } - - JsonNode data = node.get(DATA_FIELD_NAME); - JsonNode included = node.get(INCLUDED_FIELD_NAME); - LinksInformation links = readLinks(node); - MetaInformation meta = readMeta(node); - if(data == null){ - throw new IllegalStateException("no data received"); - } - - ClientResourceUpsert upsert = new ClientResourceUpsert(resourceRegistry, typeParser, objectMapper); - - ResourceBodies dataBodies = upsert.parse(data, jp); - ResourceBodies includedBodies = upsert.parse(included, jp); - - upsert.allocateResources(dataBodies); - upsert.allocateResources(includedBodies); - - upsert.setRelations(dataBodies); - upsert.setRelations(includedBodies); - - JsonApiResponse response = new JsonApiResponse(); - response.setLinksInformation(links); - response.setMetaInformation(meta); - if (dataBodies.isCollection) { - response.setEntity(dataBodies.resources); - return new CollectionResponseContext(response, null, null); - } else { - if (dataBodies.resources.size() == 1) { - response.setEntity(dataBodies.resources.get(0)); - } else { - response.setEntity(null); - } - return new ResourceResponseContext(response, -1); - } - } - - private LinksInformation readLinks(JsonNode node) { - JsonNode data = node.get(LINKS_FIELD_NAME); - if(data != null){ - return new JsonLinksInformation(data); - }else{ - return null; - } - } - - private MetaInformation readMeta(JsonNode node) { - JsonNode data = node.get(META_FIELD_NAME); - if(data != null){ - return new JsonMetaInformation(data); - }else{ - return null; - } - } - - class ClientResourceUpsert extends ResourceUpsert{ - - private HashMap resourceMap = new HashMap<>(); - - public ClientResourceUpsert(ResourceRegistry resourceRegistry, TypeParser typeParser, - ObjectMapper objectMapper) { - super(resourceRegistry, typeParser, objectMapper); - } - - public String getUID(DataBody body){ - return body.getType() + "#" + body.getId(); - } - - public void setRelations(ResourceBodies dataBodies) { - for(DataBody body : dataBodies.dataBodies){ - String uid = getUID(body); - Object resource = resourceMap.get(uid); - - RegistryEntry registryEntry = resourceRegistry.getEntry(body.getType()); - QueryAdapter queryAdapter = null; - RepositoryMethodParameterProvider parameterProvider = null; - - // FIXME read includes - setRelations(resource, registryEntry, body, queryAdapter, parameterProvider); - } - } - - public void allocateResources(ResourceBodies dataBodies) { - for(DataBody body : dataBodies.dataBodies){ - - RegistryEntry registryEntry = resourceRegistry.getEntry(body.getType()); - ResourceInformation resourceInformation = registryEntry.getResourceInformation(); - - Object resource = newResource(resourceInformation, body); - setId(body, resource, resourceInformation); - setAttributes(body, resource, resourceInformation); - dataBodies.resources.add(resource); - - String uid = getUID(body); - resourceMap.put(uid, resource); - } - } - - @Override - public boolean isAcceptable(JsonPath jsonPath, String requestType) { - throw new IllegalStateException(); - } - - @Override - public BaseResponseContext handle(JsonPath jsonPath, QueryAdapter queryAdapter, - RepositoryMethodParameterProvider parameterProvider, RequestBody requestBody) { - throw new IllegalStateException(); - } - - public ResourceBodies parse(JsonNode node, JsonParser jp) throws JsonProcessingException { - ResourceBodies bodies = new ResourceBodies(); - if (node != null) { - if (node.isArray()) { - Iterator nodeIterator = node.iterator(); - while (nodeIterator.hasNext()) { - DataBody newLinkage = jp.getCodec().treeToValue(nodeIterator.next(), ClientDataBody.class); - bodies.dataBodies.add(newLinkage); - } - bodies.isCollection = true; - } else if (node.isObject()) { - bodies.dataBodies.add(jp.getCodec().treeToValue(node, ClientDataBody.class)); - } else if (!node.isNull()) { - throw new JsonDeserializationException("data field has wrong type: " + node.toString()); - } - } - return bodies; - } - }; - - public static class ClientDataBody extends DataBody{ - - // TODO support processing of those fields - private JsonNode links; - private JsonNode meta; - - public JsonNode getLinks() { - return links; - } - public void setLinks(JsonNode links) { - this.links = links; - } - public JsonNode getMeta() { - return meta; - } - public void setMeta(JsonNode meta) { - this.meta = meta; - } - } - - - class ResourceBodies{ - ArrayList resources = new ArrayList<>(); - ArrayList dataBodies = new ArrayList<>(); - boolean isCollection = false; - } - -} +package io.katharsis.client.internal; + +import java.io.IOException; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; + +import io.katharsis.client.response.JsonLinksInformation; +import io.katharsis.client.response.JsonMetaInformation; +import io.katharsis.dispatcher.controller.resource.ResourceUpsert; +import io.katharsis.jackson.exception.JsonDeserializationException; +import io.katharsis.queryspec.internal.QueryAdapter; +import io.katharsis.repository.RepositoryMethodParameterProvider; +import io.katharsis.request.dto.DataBody; +import io.katharsis.request.dto.RequestBody; +import io.katharsis.request.path.JsonPath; +import io.katharsis.resource.information.ResourceInformation; +import io.katharsis.resource.registry.RegistryEntry; +import io.katharsis.resource.registry.ResourceRegistry; +import io.katharsis.response.BaseResponseContext; +import io.katharsis.response.CollectionResponseContext; +import io.katharsis.response.JsonApiResponse; +import io.katharsis.response.LinksInformation; +import io.katharsis.response.MetaInformation; +import io.katharsis.response.ResourceResponseContext; +import io.katharsis.utils.parser.TypeParser; + +/** + * Deerializes top-level JSON object and provides ability to include compound documents + * + * TODO could this be merged with RequestBodyDeserializer? + */ +public class BaseResponseDeserializer extends JsonDeserializer { + + private static final String INCLUDED_FIELD_NAME = "included"; + + private static final String DATA_FIELD_NAME = "data"; + + private static final String META_FIELD_NAME = "meta"; + + private static final String LINKS_FIELD_NAME = "links"; + + private ResourceRegistry resourceRegistry; + + private ObjectMapper objectMapper; + + private TypeParser typeParser = new TypeParser(); + + public BaseResponseDeserializer(ResourceRegistry resourceRegistry, ObjectMapper objectMapper) { + this.resourceRegistry = resourceRegistry; + this.objectMapper = objectMapper; + } + + @Override + public BaseResponseContext deserialize(JsonParser jp, DeserializationContext ctxt) + throws IOException, JsonProcessingException { + + JsonNode node = jp.readValueAsTree(); + if (node == null) { + return null; + } + + JsonNode data = node.get(DATA_FIELD_NAME); + JsonNode included = node.get(INCLUDED_FIELD_NAME); + LinksInformation links = readLinks(node); + MetaInformation meta = readMeta(node); + if (data == null) { + throw new IllegalStateException("no data received"); + } + + ClientResourceUpsert upsert = new ClientResourceUpsert(resourceRegistry, typeParser, objectMapper); + + ResourceBodies dataBodies = upsert.parse(data, jp); + ResourceBodies includedBodies = upsert.parse(included, jp); + + upsert.allocateResources(dataBodies); + upsert.allocateResources(includedBodies); + + upsert.setRelations(dataBodies); + upsert.setRelations(includedBodies); + + JsonApiResponse response = new JsonApiResponse(); + response.setLinksInformation(links); + response.setMetaInformation(meta); + if (dataBodies.isCollection) { + response.setEntity(dataBodies.resources); + return new CollectionResponseContext(response, null, null); + } + else { + if (dataBodies.resources.size() == 1) { + response.setEntity(dataBodies.resources.get(0)); + } + else { + response.setEntity(null); + } + return new ResourceResponseContext(response, -1); + } + } + + private LinksInformation readLinks(JsonNode node) { + JsonNode data = node.get(LINKS_FIELD_NAME); + if (data != null) { + return new JsonLinksInformation(data); + } + else { + return null; + } + } + + private MetaInformation readMeta(JsonNode node) { + JsonNode data = node.get(META_FIELD_NAME); + if (data != null) { + return new JsonMetaInformation(data); + } + else { + return null; + } + } + + class ClientResourceUpsert extends ResourceUpsert { + + private HashMap resourceMap = new HashMap<>(); + + public ClientResourceUpsert(ResourceRegistry resourceRegistry, TypeParser typeParser, ObjectMapper objectMapper) { + super(resourceRegistry, typeParser, objectMapper); + } + + public String getUID(DataBody body) { + return body.getType() + "#" + body.getId(); + } + + public String getUID(RegistryEntry entry, Serializable id) { + return entry.getResourceInformation().getResourceType() + "#" + id; + } + + public void setRelations(ResourceBodies dataBodies) { + for (DataBody body : dataBodies.dataBodies) { + String uid = getUID(body); + Object resource = resourceMap.get(uid); + + RegistryEntry registryEntry = resourceRegistry.getEntry(body.getType()); + QueryAdapter queryAdapter = null; + RepositoryMethodParameterProvider parameterProvider = null; + + setRelations(resource, registryEntry, body, queryAdapter, parameterProvider); + } + } + + /** + * Get relations from includes section or create a remote proxy + */ + @Override + protected Object fetchRelatedObject(RegistryEntry entry, Serializable relationId, + RepositoryMethodParameterProvider parameterProvider, QueryAdapter queryAdapter) { + + String uid = getUID(entry, relationId); + Object relatedResource = resourceMap.get(uid); + if(relatedResource != null){ + return relatedResource; + }else{ + return null; // TODO create remote proxy + } + } + + public void allocateResources(ResourceBodies dataBodies) { + for (DataBody body : dataBodies.dataBodies) { + + RegistryEntry registryEntry = resourceRegistry.getEntry(body.getType()); + ResourceInformation resourceInformation = registryEntry.getResourceInformation(); + + Object resource = newResource(resourceInformation, body); + setId(body, resource, resourceInformation); + setAttributes(body, resource, resourceInformation); + dataBodies.resources.add(resource); + + String uid = getUID(body); + resourceMap.put(uid, resource); + } + } + + @Override + public boolean isAcceptable(JsonPath jsonPath, String requestType) { + throw new IllegalStateException(); + } + + @Override + public BaseResponseContext handle(JsonPath jsonPath, QueryAdapter queryAdapter, + RepositoryMethodParameterProvider parameterProvider, RequestBody requestBody) { + throw new IllegalStateException(); + } + + public ResourceBodies parse(JsonNode node, JsonParser jp) throws JsonProcessingException { + ResourceBodies bodies = new ResourceBodies(); + if (node != null) { + if (node.isArray()) { + Iterator nodeIterator = node.iterator(); + while (nodeIterator.hasNext()) { + DataBody newLinkage = jp.getCodec().treeToValue(nodeIterator.next(), ClientDataBody.class); + bodies.dataBodies.add(newLinkage); + } + bodies.isCollection = true; + } + else if (node.isObject()) { + bodies.dataBodies.add(jp.getCodec().treeToValue(node, ClientDataBody.class)); + } + else if (!node.isNull()) { + throw new JsonDeserializationException("data field has wrong type: " + node.toString()); + } + } + return bodies; + } + }; + + public static class ClientDataBody extends DataBody { + + // TODO support processing of those fields + private JsonNode links; + + private JsonNode meta; + + public JsonNode getLinks() { + return links; + } + + public void setLinks(JsonNode links) { + this.links = links; + } + + public JsonNode getMeta() { + return meta; + } + + public void setMeta(JsonNode meta) { + this.meta = meta; + } + } + + class ResourceBodies { + + ArrayList resources = new ArrayList<>(); + + ArrayList dataBodies = new ArrayList<>(); + + boolean isCollection = false; + } + +} diff --git a/katharsis-core/src/main/java/io/katharsis/dispatcher/controller/resource/ResourceUpsert.java b/katharsis-core/src/main/java/io/katharsis/dispatcher/controller/resource/ResourceUpsert.java index 3918688f..02d755c9 100644 --- a/katharsis-core/src/main/java/io/katharsis/dispatcher/controller/resource/ResourceUpsert.java +++ b/katharsis-core/src/main/java/io/katharsis/dispatcher/controller/resource/ResourceUpsert.java @@ -1,270 +1,273 @@ -package io.katharsis.dispatcher.controller.resource; - -import java.io.Serializable; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Objects; - -import com.fasterxml.jackson.databind.ObjectMapper; - -import io.katharsis.dispatcher.controller.BaseController; -import io.katharsis.queryspec.internal.QueryAdapter; -import io.katharsis.repository.RepositoryMethodParameterProvider; -import io.katharsis.request.dto.DataBody; -import io.katharsis.request.dto.LinkageData; -import io.katharsis.resource.exception.ResourceException; -import io.katharsis.resource.exception.ResourceNotFoundException; -import io.katharsis.resource.field.ResourceAttributesBridge; -import io.katharsis.resource.field.ResourceField; -import io.katharsis.resource.information.ResourceInformation; -import io.katharsis.resource.information.ResourceInstanceBuilder; -import io.katharsis.resource.registry.RegistryEntry; -import io.katharsis.resource.registry.ResourceRegistry; -import io.katharsis.resource.registry.responseRepository.RelationshipRepositoryAdapter; -import io.katharsis.utils.Generics; -import io.katharsis.utils.PropertyUtils; -import io.katharsis.utils.parser.TypeParser; - -public abstract class ResourceUpsert extends BaseController { - final ResourceRegistry resourceRegistry; - final TypeParser typeParser; - protected final ObjectMapper objectMapper; - - public ResourceUpsert(ResourceRegistry resourceRegistry, TypeParser typeParser, ObjectMapper objectMapper) { - this.resourceRegistry = resourceRegistry; - this.typeParser = typeParser; - this.objectMapper = objectMapper; - } - - protected Object newResource(ResourceInformation resourceInformation, DataBody dataBody) { - ResourceInstanceBuilder builder = resourceInformation.getInstanceBuilder(); - return builder.buildResource(dataBody); - } - - protected void setId(DataBody dataBody, Object instance, ResourceInformation resourceInformation) { - if (dataBody.getId() != null) { - String id = dataBody.getId(); - - Serializable castedId = resourceInformation.parseIdString(id); - PropertyUtils.setProperty(instance, resourceInformation.getIdField() - .getUnderlyingName(), castedId); - } - } - - protected void setAttributes(DataBody dataBody, Object instance, ResourceInformation resourceInformation) { - if (dataBody.getAttributes() != null) { - ResourceAttributesBridge resourceAttributesBridge = resourceInformation.getAttributeFields(); - resourceAttributesBridge.setProperties(objectMapper, instance, dataBody.getAttributes()); - } - } - - protected void saveRelations(QueryAdapter queryAdapter, Object savedResource, RegistryEntry registryEntry, DataBody dataBody, - RepositoryMethodParameterProvider parameterProvider) { - if (dataBody.getRelationships() != null) { - Map additionalProperties = dataBody.getRelationships() - .getAdditionalProperties(); - for (Map.Entry property : additionalProperties.entrySet()) { - if (Iterable.class.isAssignableFrom(property.getValue() - .getClass())) { - //noinspection unchecked - saveRelationsField(queryAdapter, savedResource, registryEntry, (Map.Entry) property, registryEntry - .getResourceInformation(), parameterProvider); - } else { - //noinspection unchecked - saveRelationField(queryAdapter, savedResource, registryEntry, (Map.Entry) property, registryEntry - .getResourceInformation(), parameterProvider); - } - - } - } - } - - private void saveRelationsField(QueryAdapter queryAdapter, Object savedResource, RegistryEntry registryEntry, - Map.Entry> property, - ResourceInformation resourceInformation, - RepositoryMethodParameterProvider parameterProvider) { - if (!allTypesTheSame(property.getValue())) { - throw new ResourceException("Not all types are the same for linkage: " + property.getKey()); - } - - String type = getLinkageType(property.getValue()); - RegistryEntry relationRegistryEntry = getRelationRegistryEntry(type); - @SuppressWarnings("unchecked") - Class relationshipIdClass = (Class) relationRegistryEntry - .getResourceInformation() - .getIdField() - .getType(); - List castedRelationIds = new LinkedList<>(); - - for (LinkageData linkageData : property.getValue()) { - Serializable castedRelationshipId = typeParser.parse(linkageData.getId(), relationshipIdClass); - castedRelationIds.add(castedRelationshipId); - } - - Class relationshipClass = relationRegistryEntry.getResourceInformation() - .getResourceClass(); - RelationshipRepositoryAdapter relationshipRepository = registryEntry - .getRelationshipRepositoryForClass(relationshipClass, parameterProvider); - ResourceField relationshipField = resourceInformation.findRelationshipFieldByName(property.getKey()); - //noinspection unchecked - relationshipRepository.setRelations(savedResource, castedRelationIds, - relationshipField.getUnderlyingName(), queryAdapter); - } - - private static boolean allTypesTheSame(Iterable linkages) { - String type = linkages.iterator() - .hasNext() ? linkages.iterator() - .next() - .getType() : null; - for (LinkageData linkageData : linkages) { - if (!Objects.equals(type, linkageData.getType())) { - return false; - } - } - return true; - } - - protected String getLinkageType(Iterable linkages) { - return linkages.iterator() - .hasNext() ? linkages.iterator() - .next() - .getType() : null; - } - - private void saveRelationField(QueryAdapter queryAdapter, Object savedResource, RegistryEntry registryEntry, - Map.Entry property, ResourceInformation resourceInformation, - RepositoryMethodParameterProvider parameterProvider) { - RegistryEntry relationRegistryEntry = getRelationRegistryEntry(property.getValue() - .getType()); - - @SuppressWarnings("unchecked") - Class relationshipIdClass = (Class) relationRegistryEntry - .getResourceInformation() - .getIdField() - .getType(); - Serializable castedRelationshipId = typeParser.parse(property.getValue() - .getId(), relationshipIdClass); - - Class relationshipClass = relationRegistryEntry.getResourceInformation() - .getResourceClass(); - RelationshipRepositoryAdapter relationshipRepository = registryEntry - .getRelationshipRepositoryForClass(relationshipClass, parameterProvider); - ResourceField relationshipField = resourceInformation.findRelationshipFieldByName(property.getKey()); - //noinspection unchecked - relationshipRepository.setRelation(savedResource, castedRelationshipId, relationshipField.getUnderlyingName(), - queryAdapter); - } - - private RegistryEntry getRelationRegistryEntry(String type) { - RegistryEntry relationRegistryEntry = resourceRegistry.getEntry(type); - if (relationRegistryEntry == null) { - throw new ResourceNotFoundException(type); - } - return relationRegistryEntry; - } - - Object buildNewResource(RegistryEntry registryEntry, DataBody dataBody, String resourceName) { - if (dataBody == null) { - throw new ResourceException("No data field in the body."); - } - if (!resourceName.equals(dataBody.getType())) { - throw new ResourceException(String.format("Inconsistent type definition between path and body: body type: " + - "%s, request type: %s", - dataBody.getType(), - resourceName)); - } - try { - return registryEntry.getResourceInformation() - .getResourceClass() - .newInstance(); - } catch (InstantiationException | IllegalAccessException e) { - throw new ResourceException( - String.format("couldn't create a new instance of %s", registryEntry.getResourceInformation() - .getResourceClass())); - } - } - - protected void setRelations(Object newResource, RegistryEntry registryEntry, DataBody dataBody, QueryAdapter - queryAdapter, - RepositoryMethodParameterProvider parameterProvider) { - if (dataBody.getRelationships() != null) { - Map additionalProperties = dataBody.getRelationships() - .getAdditionalProperties(); - for (Map.Entry property : additionalProperties.entrySet()) { - if (property.getValue() != null && Iterable.class.isAssignableFrom(property.getValue() - .getClass())) { - //noinspection unchecked - setRelationsField(newResource, - registryEntry, - (Map.Entry) property, - queryAdapter, - parameterProvider); - } else { - //noinspection unchecked - setRelationField(newResource, registryEntry, (Map.Entry) property, queryAdapter, parameterProvider); - } - - } - } - } - - private void setRelationsField(Object newResource, RegistryEntry registryEntry, - Map.Entry> property, QueryAdapter queryAdapter, - RepositoryMethodParameterProvider parameterProvider) { - String propertyName = property.getKey(); - ResourceField relationshipField = registryEntry.getResourceInformation() - .findRelationshipFieldByName(propertyName); - Class relationshipFieldClass = Generics.getResourceClass(relationshipField.getGenericType(), - relationshipField.getType()); - RegistryEntry entry = null; - Class idFieldType = null; - List relationships = new LinkedList<>(); - boolean first = true; - for (LinkageData linkageData : property.getValue()) { - if (first) { - entry = resourceRegistry.getEntry(linkageData.getType(), relationshipFieldClass); - idFieldType = entry.getResourceInformation() - .getIdField() - .getType(); - first = false; - } - Serializable castedRelationshipId = typeParser.parse(linkageData.getId(), idFieldType); - Object relationObject = entry.getResourceRepository(parameterProvider) - .findOne(castedRelationshipId, queryAdapter) - .getEntity(); - relationships.add(relationObject); - } - PropertyUtils.setProperty(newResource, relationshipField.getUnderlyingName(), relationships); - } - - private void setRelationField(Object newResource, RegistryEntry registryEntry, - Map.Entry property, QueryAdapter queryAdapter, - RepositoryMethodParameterProvider parameterProvider) { - - ResourceField relationshipFieldByName = registryEntry.getResourceInformation() - .findRelationshipFieldByName(property.getKey()); - - if (relationshipFieldByName == null) { - throw new ResourceException(String.format("Invalid relationship name: %s", property.getKey())); - } - - Object relationObject; - if (property.getValue() != null) { - RegistryEntry entry = resourceRegistry.getEntry(property.getValue().getType(), - relationshipFieldByName.getType()); - Class idFieldType = entry.getResourceInformation() - .getIdField() - .getType(); - Serializable castedRelationshipId = typeParser.parse(property.getValue() - .getId(), idFieldType); - relationObject = entry.getResourceRepository(parameterProvider) - .findOne(castedRelationshipId, queryAdapter) - .getEntity(); - } else { - relationObject = null; - } - - PropertyUtils.setProperty(newResource, relationshipFieldByName.getUnderlyingName(), relationObject); - } -} +package io.katharsis.dispatcher.controller.resource; + +import java.io.Serializable; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import io.katharsis.dispatcher.controller.BaseController; +import io.katharsis.queryspec.internal.QueryAdapter; +import io.katharsis.repository.RepositoryMethodParameterProvider; +import io.katharsis.request.dto.DataBody; +import io.katharsis.request.dto.LinkageData; +import io.katharsis.resource.exception.ResourceException; +import io.katharsis.resource.exception.ResourceNotFoundException; +import io.katharsis.resource.field.ResourceAttributesBridge; +import io.katharsis.resource.field.ResourceField; +import io.katharsis.resource.information.ResourceInformation; +import io.katharsis.resource.information.ResourceInstanceBuilder; +import io.katharsis.resource.registry.RegistryEntry; +import io.katharsis.resource.registry.ResourceRegistry; +import io.katharsis.resource.registry.responseRepository.RelationshipRepositoryAdapter; +import io.katharsis.utils.Generics; +import io.katharsis.utils.PropertyUtils; +import io.katharsis.utils.parser.TypeParser; + +public abstract class ResourceUpsert extends BaseController { + final ResourceRegistry resourceRegistry; + final TypeParser typeParser; + protected final ObjectMapper objectMapper; + + public ResourceUpsert(ResourceRegistry resourceRegistry, TypeParser typeParser, ObjectMapper objectMapper) { + this.resourceRegistry = resourceRegistry; + this.typeParser = typeParser; + this.objectMapper = objectMapper; + } + + protected Object newResource(ResourceInformation resourceInformation, DataBody dataBody) { + ResourceInstanceBuilder builder = resourceInformation.getInstanceBuilder(); + return builder.buildResource(dataBody); + } + + protected void setId(DataBody dataBody, Object instance, ResourceInformation resourceInformation) { + if (dataBody.getId() != null) { + String id = dataBody.getId(); + + Serializable castedId = resourceInformation.parseIdString(id); + PropertyUtils.setProperty(instance, resourceInformation.getIdField() + .getUnderlyingName(), castedId); + } + } + + protected void setAttributes(DataBody dataBody, Object instance, ResourceInformation resourceInformation) { + if (dataBody.getAttributes() != null) { + ResourceAttributesBridge resourceAttributesBridge = resourceInformation.getAttributeFields(); + resourceAttributesBridge.setProperties(objectMapper, instance, dataBody.getAttributes()); + } + } + + protected void saveRelations(QueryAdapter queryAdapter, Object savedResource, RegistryEntry registryEntry, DataBody dataBody, + RepositoryMethodParameterProvider parameterProvider) { + if (dataBody.getRelationships() != null) { + Map additionalProperties = dataBody.getRelationships() + .getAdditionalProperties(); + for (Map.Entry property : additionalProperties.entrySet()) { + if (Iterable.class.isAssignableFrom(property.getValue() + .getClass())) { + //noinspection unchecked + saveRelationsField(queryAdapter, savedResource, registryEntry, (Map.Entry) property, registryEntry + .getResourceInformation(), parameterProvider); + } else { + //noinspection unchecked + saveRelationField(queryAdapter, savedResource, registryEntry, (Map.Entry) property, registryEntry + .getResourceInformation(), parameterProvider); + } + + } + } + } + + private void saveRelationsField(QueryAdapter queryAdapter, Object savedResource, RegistryEntry registryEntry, + Map.Entry> property, + ResourceInformation resourceInformation, + RepositoryMethodParameterProvider parameterProvider) { + if (!allTypesTheSame(property.getValue())) { + throw new ResourceException("Not all types are the same for linkage: " + property.getKey()); + } + + String type = getLinkageType(property.getValue()); + RegistryEntry relationRegistryEntry = getRelationRegistryEntry(type); + @SuppressWarnings("unchecked") + Class relationshipIdClass = (Class) relationRegistryEntry + .getResourceInformation() + .getIdField() + .getType(); + List castedRelationIds = new LinkedList<>(); + + for (LinkageData linkageData : property.getValue()) { + Serializable castedRelationshipId = typeParser.parse(linkageData.getId(), relationshipIdClass); + castedRelationIds.add(castedRelationshipId); + } + + Class relationshipClass = relationRegistryEntry.getResourceInformation() + .getResourceClass(); + RelationshipRepositoryAdapter relationshipRepository = registryEntry + .getRelationshipRepositoryForClass(relationshipClass, parameterProvider); + ResourceField relationshipField = resourceInformation.findRelationshipFieldByName(property.getKey()); + //noinspection unchecked + relationshipRepository.setRelations(savedResource, castedRelationIds, + relationshipField.getUnderlyingName(), queryAdapter); + } + + private static boolean allTypesTheSame(Iterable linkages) { + String type = linkages.iterator() + .hasNext() ? linkages.iterator() + .next() + .getType() : null; + for (LinkageData linkageData : linkages) { + if (!Objects.equals(type, linkageData.getType())) { + return false; + } + } + return true; + } + + protected String getLinkageType(Iterable linkages) { + return linkages.iterator() + .hasNext() ? linkages.iterator() + .next() + .getType() : null; + } + + private void saveRelationField(QueryAdapter queryAdapter, Object savedResource, RegistryEntry registryEntry, + Map.Entry property, ResourceInformation resourceInformation, + RepositoryMethodParameterProvider parameterProvider) { + RegistryEntry relationRegistryEntry = getRelationRegistryEntry(property.getValue() + .getType()); + + @SuppressWarnings("unchecked") + Class relationshipIdClass = (Class) relationRegistryEntry + .getResourceInformation() + .getIdField() + .getType(); + Serializable castedRelationshipId = typeParser.parse(property.getValue() + .getId(), relationshipIdClass); + + Class relationshipClass = relationRegistryEntry.getResourceInformation() + .getResourceClass(); + RelationshipRepositoryAdapter relationshipRepository = registryEntry + .getRelationshipRepositoryForClass(relationshipClass, parameterProvider); + ResourceField relationshipField = resourceInformation.findRelationshipFieldByName(property.getKey()); + //noinspection unchecked + relationshipRepository.setRelation(savedResource, castedRelationshipId, relationshipField.getUnderlyingName(), + queryAdapter); + } + + private RegistryEntry getRelationRegistryEntry(String type) { + RegistryEntry relationRegistryEntry = resourceRegistry.getEntry(type); + if (relationRegistryEntry == null) { + throw new ResourceNotFoundException(type); + } + return relationRegistryEntry; + } + + Object buildNewResource(RegistryEntry registryEntry, DataBody dataBody, String resourceName) { + if (dataBody == null) { + throw new ResourceException("No data field in the body."); + } + if (!resourceName.equals(dataBody.getType())) { + throw new ResourceException(String.format("Inconsistent type definition between path and body: body type: " + + "%s, request type: %s", + dataBody.getType(), + resourceName)); + } + try { + return registryEntry.getResourceInformation() + .getResourceClass() + .newInstance(); + } catch (InstantiationException | IllegalAccessException e) { + throw new ResourceException( + String.format("couldn't create a new instance of %s", registryEntry.getResourceInformation() + .getResourceClass())); + } + } + + protected void setRelations(Object newResource, RegistryEntry registryEntry, DataBody dataBody, QueryAdapter + queryAdapter, + RepositoryMethodParameterProvider parameterProvider) { + if (dataBody.getRelationships() != null) { + Map additionalProperties = dataBody.getRelationships() + .getAdditionalProperties(); + for (Map.Entry property : additionalProperties.entrySet()) { + if (property.getValue() != null && Iterable.class.isAssignableFrom(property.getValue() + .getClass())) { + //noinspection unchecked + setRelationsField(newResource, + registryEntry, + (Map.Entry) property, + queryAdapter, + parameterProvider); + } else { + //noinspection unchecked + setRelationField(newResource, registryEntry, (Map.Entry) property, queryAdapter, parameterProvider); + } + + } + } + } + + private void setRelationsField(Object newResource, RegistryEntry registryEntry, + Map.Entry> property, QueryAdapter queryAdapter, + RepositoryMethodParameterProvider parameterProvider) { + String propertyName = property.getKey(); + ResourceField relationshipField = registryEntry.getResourceInformation() + .findRelationshipFieldByName(propertyName); + Class relationshipFieldClass = Generics.getResourceClass(relationshipField.getGenericType(), + relationshipField.getType()); + RegistryEntry entry = null; + Class idFieldType = null; + List relationships = new LinkedList<>(); + boolean first = true; + for (LinkageData linkageData : property.getValue()) { + if (first) { + entry = resourceRegistry.getEntry(linkageData.getType(), relationshipFieldClass); + idFieldType = entry.getResourceInformation() + .getIdField() + .getType(); + first = false; + } + Serializable castedRelationshipId = typeParser.parse(linkageData.getId(), idFieldType); + + Object relationObject = fetchRelatedObject(entry, castedRelationshipId, parameterProvider, queryAdapter); + + relationships.add(relationObject); + } + PropertyUtils.setProperty(newResource, relationshipField.getUnderlyingName(), relationships); + } + + protected void setRelationField(Object newResource, RegistryEntry registryEntry, + Map.Entry property, QueryAdapter queryAdapter, + RepositoryMethodParameterProvider parameterProvider) { + + ResourceField relationshipFieldByName = registryEntry.getResourceInformation() + .findRelationshipFieldByName(property.getKey()); + + if (relationshipFieldByName == null) { + throw new ResourceException(String.format("Invalid relationship name: %s", property.getKey())); + } + + Object relationObject; + if (property.getValue() != null) { + RegistryEntry entry = resourceRegistry.getEntry(property.getValue().getType(), + relationshipFieldByName.getType()); + Class idFieldType = entry.getResourceInformation() + .getIdField() + .getType(); + Serializable castedRelationshipId = typeParser.parse(property.getValue().getId(), idFieldType); + + relationObject = fetchRelatedObject(entry, castedRelationshipId, parameterProvider, queryAdapter); + } else { + relationObject = null; + } + + PropertyUtils.setProperty(newResource, relationshipFieldByName.getUnderlyingName(), relationObject); + } + + protected Object fetchRelatedObject(RegistryEntry entry, Serializable relationId, RepositoryMethodParameterProvider parameterProvider, + QueryAdapter queryAdapter) { + return entry.getResourceRepository(parameterProvider).findOne(relationId, queryAdapter).getEntity(); + } +} diff --git a/katharsis-core/src/test/java/io/katharsis/resource/mock/models/Project.java b/katharsis-core/src/test/java/io/katharsis/resource/mock/models/Project.java index 83d5ba39..b7afe32d 100644 --- a/katharsis-core/src/test/java/io/katharsis/resource/mock/models/Project.java +++ b/katharsis-core/src/test/java/io/katharsis/resource/mock/models/Project.java @@ -1,98 +1,98 @@ -package io.katharsis.resource.mock.models; - -import io.katharsis.resource.annotations.*; - -import java.util.ArrayList; -import java.util.List; - -@JsonApiResource(type = "projects") -public class Project { - - @JsonApiId - private Long id; - - private String name; - - private String description; - - private ProjectData data; - - @JsonApiToMany - private List tasks = new ArrayList<>(); - - @JsonApiToOne - private Task task; - - @JsonApiToOne - @JsonApiIncludeByDefault - private ProjectEager projectEager; - - @JsonApiToMany - @JsonApiIncludeByDefault - private List projectEagerList = new ArrayList<>(); - - public Long getId() { - return id; - } - - public Project setId(Long id) { - this.id = id; - return this; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getDescription() { - return description; - } - - public void setDescription(@SuppressWarnings("SameParameterValue") String description) { - this.description = description; - } - - public ProjectData getData() { - return data; - } - - public void setData(ProjectData data) { - this.data = data; - } - - public List getTasks() { - return tasks; - } - - public void setTasks(List tasks) { - this.tasks = tasks; - } - - public Task getTask() { - return task; - } - - public void setTask(Task task) { - this.task = task; - } - - public ProjectEager getProjectEager() { - return projectEager; - } - - public void setProjectEager(ProjectEager projectEager) { - this.projectEager = projectEager; - } - - public List getProjectEagerList() { - return projectEagerList; - } - - public void setProjectEagerList(List projectEagerList) { - this.projectEagerList = projectEagerList; - } -} +package io.katharsis.resource.mock.models; + +import io.katharsis.resource.annotations.*; + +import java.util.ArrayList; +import java.util.List; + +@JsonApiResource(type = "projects") +public class Project { + + @JsonApiId + private Long id; + + private String name; + + private String description; + + private ProjectData data; + + @JsonApiToMany + private List tasks = new ArrayList<>(); + + @JsonApiToOne + private Task task; + + @JsonApiToOne + @JsonApiIncludeByDefault + private ProjectEager projectEager; + + @JsonApiToMany + @JsonApiIncludeByDefault + private List projectEagerList = new ArrayList<>(); + + public Long getId() { + return id; + } + + public Project setId(Long id) { + this.id = id; + return this; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDescription() { + return description; + } + + public void setDescription(@SuppressWarnings("SameParameterValue") String description) { + this.description = description; + } + + public ProjectData getData() { + return data; + } + + public void setData(ProjectData data) { + this.data = data; + } + + public List getTasks() { + return tasks; + } + + public void setTasks(List tasks) { + this.tasks = tasks; + } + + public Task getTask() { + return task; + } + + public void setTask(Task task) { + this.task = task; + } + + public ProjectEager getProjectEager() { + return projectEager; + } + + public void setProjectEager(ProjectEager projectEager) { + this.projectEager = projectEager; + } + + public List getProjectEagerList() { + return projectEagerList; + } + + public void setProjectEagerList(List projectEagerList) { + this.projectEagerList = projectEagerList; + } +} diff --git a/katharsis-jpa/src/main/java/io/katharsis/jpa/DefaultJpaRepositoryFactory.java b/katharsis-jpa/src/main/java/io/katharsis/jpa/DefaultJpaRepositoryFactory.java index be5d770d..0d9d9ef7 100644 --- a/katharsis-jpa/src/main/java/io/katharsis/jpa/DefaultJpaRepositoryFactory.java +++ b/katharsis-jpa/src/main/java/io/katharsis/jpa/DefaultJpaRepositoryFactory.java @@ -1,19 +1,34 @@ -package io.katharsis.jpa; - -import java.io.Serializable; - -public class DefaultJpaRepositoryFactory implements JpaRepositoryFactory { - - @Override - public JpaEntityRepository createEntityRepository(JpaModule module, - Class entityClass) { - return new JpaEntityRepository<>(module, entityClass); - } - - @Override - public JpaRelationshipRepository createRelationshipRepository( - JpaModule module, Class entityClass, Class relatedEntityClass) { - return new JpaRelationshipRepository<>(module, entityClass, relatedEntityClass); - } - -} +package io.katharsis.jpa; + +import java.io.Serializable; + +import io.katharsis.jpa.mapping.JpaMapper; + +public class DefaultJpaRepositoryFactory implements JpaRepositoryFactory { + + @Override + public JpaEntityRepository createEntityRepository(JpaModule module, Class entityClass) { + return new JpaEntityRepository<>(module, entityClass); + } + + @Override + public JpaRelationshipRepository createRelationshipRepository( + JpaModule module, Class entityClass, Class relatedEntityClass) { + return new JpaRelationshipRepository<>(module, entityClass, relatedEntityClass); + } + + @Override + public JpaEntityRepository createMappedEntityRepository(JpaModule module, + Class entityClass, Class dtoClass, JpaMapper mapper) { + return new JpaEntityRepository<>(module, entityClass, dtoClass, mapper); + } + + @Override + public JpaRelationshipRepository createMappedRelationshipRepository( + JpaModule module, Class sourceEntityClass, Class sourceResourceClass, Class targetEntityClass, + Class targetResourceClass, JpaMapper sourceMapper, JpaMapper targetMapper) { + return new JpaRelationshipRepository<>(module, sourceEntityClass, sourceResourceClass, targetEntityClass, + targetResourceClass, sourceMapper, targetMapper); + } + +} diff --git a/katharsis-jpa/src/main/java/io/katharsis/jpa/JpaEntityRepository.java b/katharsis-jpa/src/main/java/io/katharsis/jpa/JpaEntityRepository.java index 33cfa472..ad11abf6 100644 --- a/katharsis-jpa/src/main/java/io/katharsis/jpa/JpaEntityRepository.java +++ b/katharsis-jpa/src/main/java/io/katharsis/jpa/JpaEntityRepository.java @@ -1,124 +1,150 @@ -package io.katharsis.jpa; - -import java.io.Serializable; -import java.util.ArrayList; -import java.util.List; - -import javax.persistence.EntityManager; - -import io.katharsis.jpa.internal.JpaRepositoryUtils; -import io.katharsis.jpa.internal.meta.MetaAttribute; -import io.katharsis.jpa.internal.meta.MetaEntity; -import io.katharsis.jpa.internal.paging.DefaultPagedLinksInformation; -import io.katharsis.jpa.internal.paging.DefaultPagedMetaInformation; -import io.katharsis.jpa.internal.paging.PagedLinksInformation; -import io.katharsis.jpa.internal.paging.PagedMetaInformation; -import io.katharsis.jpa.internal.paging.PagedRepositoryBase; -import io.katharsis.jpa.internal.paging.PagedResultList; -import io.katharsis.jpa.query.JpaQuery; -import io.katharsis.jpa.query.JpaQueryExecutor; -import io.katharsis.jpa.query.JpaQueryFactory; -import io.katharsis.queryspec.FilterOperator; -import io.katharsis.queryspec.QuerySpec; -import io.katharsis.queryspec.QuerySpecResourceRepository; - -/** - * Exposes a JPA entity as ResourceRepository. - */ -public class JpaEntityRepository extends PagedRepositoryBase - implements QuerySpecResourceRepository { - - private Class entityType; - - private MetaEntity meta; - - private MetaAttribute primaryKeyAttr; - - private JpaModule module; - - public JpaEntityRepository(JpaModule module, Class entityType) { - this.entityType = entityType; - this.meta = module.getMetaLookup().getMeta(entityType).asEntity(); - this.primaryKeyAttr = JpaRepositoryUtils.getPrimaryKeyAttr(meta); - this.module = module; - } - - @Override - public T findOne(I id, QuerySpec querySpec) { - JpaQueryFactory queryFactory = module.getQueryFactory(); - JpaQuery query = queryFactory.query(entityType); - JpaRepositoryUtils.prepareQuery(query, querySpec); - query.addFilter(primaryKeyAttr.getName(), FilterOperator.EQ, id); - JpaQueryExecutor executor = query.buildExecutor(); - JpaRepositoryUtils.prepareExecutor(executor, querySpec); - return executor.getUniqueResult(true); - } - - @Override - public List findAll(QuerySpec querySpec) { - JpaQueryFactory queryFactory = module.getQueryFactory(); - JpaQuery query = queryFactory.query(entityType); - JpaRepositoryUtils.prepareQuery(query, querySpec); - JpaQueryExecutor executor = query.buildExecutor(); - JpaRepositoryUtils.prepareExecutor(executor, querySpec); - List list = executor.getResultList(); - if (querySpec.getLimit() != null) { - long totalRowCount = executor.getTotalRowCount(); - return new PagedResultList<>(list, totalRowCount); - } - else { - return list; - } - } - - @Override - public List findAll(Iterable ids, QuerySpec querySpec) { - JpaQueryFactory queryFactory = module.getQueryFactory(); - - ArrayList idList = new ArrayList<>(); - for (I id : ids) { - idList.add(id); - } - - JpaQuery query = queryFactory.query(entityType); - query.addFilter(primaryKeyAttr.getName(), FilterOperator.EQ, ids); - - JpaRepositoryUtils.prepareQuery(query, querySpec); - JpaQueryExecutor executor = query.buildExecutor(); - JpaRepositoryUtils.prepareExecutor(executor, querySpec); - - return executor.getResultList(); - } - - @Override - public S save(S entity) { - EntityManager em = module.getEntityManager(); - em.persist(entity); - return entity; - } - - @Override - public void delete(I id) { - EntityManager em = module.getEntityManager(); - - T object = em.find(entityType, id); - if (object != null) { - em.remove(object); - } - } - - @Override - public Class getResourceClass() { - return entityType; - } - - @Override - protected PagedMetaInformation newPagedMetaInformation() { - return new DefaultPagedMetaInformation(); - } - - @Override - protected PagedLinksInformation newPagedLinksInformation() { - return new DefaultPagedLinksInformation(); - } -} +package io.katharsis.jpa; + +import java.io.Serializable; +import java.util.Arrays; +import java.util.List; +import java.util.Set; + +import javax.persistence.EntityManager; + +import io.katharsis.jpa.internal.JpaRepositoryBase; +import io.katharsis.jpa.internal.JpaRepositoryUtils; +import io.katharsis.jpa.internal.meta.MetaAttribute; +import io.katharsis.jpa.internal.meta.MetaEntity; +import io.katharsis.jpa.internal.paging.DefaultPagedLinksInformation; +import io.katharsis.jpa.internal.paging.DefaultPagedMetaInformation; +import io.katharsis.jpa.internal.paging.PagedLinksInformation; +import io.katharsis.jpa.internal.paging.PagedMetaInformation; +import io.katharsis.jpa.internal.paging.PagedResultList; +import io.katharsis.jpa.mapping.IdentityMapper; +import io.katharsis.jpa.mapping.JpaMapper; +import io.katharsis.jpa.query.ComputedAttributeRegistry; +import io.katharsis.jpa.query.JpaQuery; +import io.katharsis.jpa.query.JpaQueryExecutor; +import io.katharsis.jpa.query.JpaQueryFactory; +import io.katharsis.jpa.query.Tuple; +import io.katharsis.queryspec.FilterOperator; +import io.katharsis.queryspec.FilterSpec; +import io.katharsis.queryspec.QuerySpec; +import io.katharsis.queryspec.QuerySpecResourceRepository; +import io.katharsis.utils.PropertyUtils; + +/** + * Exposes a JPA entity as ResourceRepository. + */ +public class JpaEntityRepository extends JpaRepositoryBase + implements QuerySpecResourceRepository { + + /** + * In case of a mapping the entityType differents from the resourceType + */ + private Class entityType; + + private MetaEntity meta; + + private MetaAttribute primaryKeyAttr; + + public JpaEntityRepository(JpaModule module, Class entityType) { + super(module, entityType, IdentityMapper.newInstance()); + this.entityType = entityType; + this.meta = module.getMetaLookup().getMeta(entityType).asEntity(); + this.primaryKeyAttr = JpaRepositoryUtils.getPrimaryKeyAttr(meta); + } + + public JpaEntityRepository(JpaModule module, Class entityType, Class resourceType, JpaMapper mapper) { + super(module, resourceType, mapper); + this.entityType = entityType; + this.meta = module.getMetaLookup().getMeta(entityType).asEntity(); + this.primaryKeyAttr = JpaRepositoryUtils.getPrimaryKeyAttr(meta); + } + + @Override + public final T findOne(I id, QuerySpec querySpec) { + QuerySpec idQuerySpec = querySpec.duplicate(); + idQuerySpec.addFilter(new FilterSpec(Arrays.asList(primaryKeyAttr.getName()), FilterOperator.EQ, id)); + List results = findAll(idQuerySpec); + return getUniqueOrNull(results); + } + + @Override + public final List findAll(Iterable ids, QuerySpec querySpec) { + QuerySpec idQuerySpec = querySpec.duplicate(); + idQuerySpec.addFilter(new FilterSpec(Arrays.asList(primaryKeyAttr.getName()), FilterOperator.EQ, ids)); + return findAll(querySpec); + } + + @Override + public List findAll(QuerySpec querySpec) { + QuerySpec filteredQuerySpec = filterQuerySpec(querySpec); + JpaQueryFactory queryFactory = module.getQueryFactory(); + JpaQuery query = queryFactory.query(entityType); + + ComputedAttributeRegistry computedAttributesRegistry = queryFactory.getComputedAttributes(); + Set computedAttrs = computedAttributesRegistry.getForType(entityType); + + JpaRepositoryUtils.prepareQuery(query, filteredQuerySpec, computedAttrs); + query = filterQuery(filteredQuerySpec, query); + JpaQueryExecutor executor = query.buildExecutor(); + JpaRepositoryUtils.prepareExecutor(executor, filteredQuerySpec); + executor = filterExecutor(filteredQuerySpec, executor); + + List tuples = executor.getResultTuples(); + tuples = filterTuples(filteredQuerySpec, tuples); + List resources = map(tuples); + resources = filterResults(filteredQuerySpec, resources); + if (filteredQuerySpec.getLimit() != null) { + long totalRowCount = executor.getTotalRowCount(); + return new PagedResultList<>(resources, totalRowCount); + } + else { + return resources; + } + } + + @SuppressWarnings("unchecked") + @Override + public S save(S resource) { + Object entity = mapper.unmap(resource); + + EntityManager em = module.getEntityManager(); + em.persist(entity); + em.flush(); + + // fetch again since we may have to fetch tuple data and do DTO mapping + QuerySpec querySpec = new QuerySpec(resourceClass); + I id = (I) PropertyUtils.getProperty(resource, primaryKeyAttr.getName()); + if (id == null) { + throw new IllegalStateException("id not available for entity " + id); + } + return (S) findOne(id, querySpec); + } + + @Override + public void delete(I id) { + EntityManager em = module.getEntityManager(); + + Object object = em.find(entityType, id); + if (object != null) { + em.remove(object); + } + } + + @Override + public Class getResourceClass() { + return resourceClass; + } + + public Class getEntityClass() { + return entityType; + } + + @Override + protected PagedMetaInformation newPagedMetaInformation() { + return new DefaultPagedMetaInformation(); + } + + @Override + protected PagedLinksInformation newPagedLinksInformation() { + return new DefaultPagedLinksInformation(); + } +} diff --git a/katharsis-jpa/src/main/java/io/katharsis/jpa/JpaModule.java b/katharsis-jpa/src/main/java/io/katharsis/jpa/JpaModule.java index 27b45e08..0a4ce067 100644 --- a/katharsis-jpa/src/main/java/io/katharsis/jpa/JpaModule.java +++ b/katharsis-jpa/src/main/java/io/katharsis/jpa/JpaModule.java @@ -1,315 +1,535 @@ -package io.katharsis.jpa; - -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; -import java.util.concurrent.Callable; - -import javax.persistence.Entity; -import javax.persistence.EntityManager; -import javax.persistence.EntityManagerFactory; -import javax.persistence.metamodel.ManagedType; - -import org.reflections.Reflections; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.katharsis.dispatcher.filter.AbstractFilter; -import io.katharsis.dispatcher.filter.FilterChain; -import io.katharsis.dispatcher.filter.FilterRequestContext; -import io.katharsis.jpa.internal.JpaResourceInformationBuilder; -import io.katharsis.jpa.internal.OptimisticLockExceptionMapper; -import io.katharsis.jpa.internal.meta.MetaAttribute; -import io.katharsis.jpa.internal.meta.MetaElement; -import io.katharsis.jpa.internal.meta.MetaEntity; -import io.katharsis.jpa.internal.meta.MetaLookup; -import io.katharsis.jpa.internal.util.KatharsisAssert; -import io.katharsis.jpa.query.JpaQueryFactory; -import io.katharsis.jpa.query.criteria.JpaCriteriaQueryFactory; -import io.katharsis.module.Module; -import io.katharsis.resource.information.ResourceInformationBuilder; -import io.katharsis.resource.registry.ResourceLookup; -import io.katharsis.response.BaseResponseContext; - -/** - * Katharsis module that adds support to expose JPA entities as repositories. It - * supports: - * - *
    - *
  • Sorting - * ?sort[task][name]=asc - *
  • - *
  • Filtering - * ?filter[task][name]=MyTask - *
  • - *
  • Access to relationships for any operation (sorting, filtering, etc.) - * - * ?filter[task][project][name]=MyProject - *
  • - *
  • Includes for relationships - * ?include[task]=project - *
  • - *
  • Paging - * ?page[offset]=20&page[limit]=10 - *
  • - *
- * - * - * Not supported so far: - * - *
    - *
  • Selection of fields, always all fields are returned. - * ?fields[task]=id,name - *
  • - *
  • Sorting and filtering on related resources. Consider doing separate - * requests on the relations where necessary. - * /tasks/?sort[project][name]=asc - *
  • - *
- * - */ -public class JpaModule implements Module { - - private Logger logger = LoggerFactory.getLogger(JpaModule.class); - - private static final String MODULE_NAME = "jpa"; - - private String resourceSearchPackage; - - private EntityManagerFactory emFactory; - private EntityManager em; - - private JpaQueryFactory queryFactory; - private ResourceInformationBuilder resourceInformationBuilder; - private TransactionRunner transactionRunner; - - private ModuleContext context; - - private MetaLookup metaLookup = new MetaLookup(); - private HashSet> entityClasses; - - private JpaRepositoryFactory repositoryFactory; - - /** - * Constructor used on client side. - */ - public JpaModule(String resourceSearchPackage) { - this.resourceSearchPackage = resourceSearchPackage; - - Reflections reflections; - if (resourceSearchPackage != null) { - String[] packageNames = resourceSearchPackage.split(","); - Object[] objPackageNames = new Object[packageNames.length]; - System.arraycopy(packageNames, 0, objPackageNames, 0, packageNames.length); - reflections = new Reflections(objPackageNames); - } else { - reflections = new Reflections(resourceSearchPackage); - } - this.entityClasses = new HashSet<>(); - this.entityClasses.addAll(reflections.getTypesAnnotatedWith(Entity.class)); - } - - /** - * Constructor used on server side. - */ - public JpaModule(EntityManagerFactory emFactory, EntityManager em, TransactionRunner transactionRunner) { - this.emFactory = emFactory; - this.em = em; - this.transactionRunner = transactionRunner; - this.queryFactory = JpaCriteriaQueryFactory.newInstance(metaLookup, em); - - this.entityClasses = new HashSet<>(); - Set> managedTypes = emFactory.getMetamodel().getManagedTypes(); - for (ManagedType managedType : managedTypes) { - Class managedJavaType = managedType.getJavaType(); - MetaElement meta = metaLookup.getMeta(managedJavaType); - if (meta instanceof MetaEntity) { - entityClasses.add(managedJavaType); - } - } - this.setRepositoryFactory(new DefaultJpaRepositoryFactory()); - } - - public MetaLookup getMetaLookup() { - return metaLookup; - } - - public void setRepositoryFactory(JpaRepositoryFactory repositoryFactory) { - checkNotInitialized(); - this.repositoryFactory = repositoryFactory; - } - - /** - * @return set of entity classes made available as repository. - */ - public Set> getEntityClasses() { - return Collections.unmodifiableSet(entityClasses); - } - - /** - * Adds the given entity class to expose the entity as repository. - * - * @param entityClass - */ - public void addEntityClass(Class entityClass) { - checkNotInitialized(); - entityClasses.add(entityClass); - } - - /** - * Removes the given entity class to not expose the entity as repository. - * - * @param entityClass - */ - public void removeEntityClass(Class entityClass) { - checkNotInitialized(); - entityClasses.remove(entityClass); - } - - @Override - public String getModuleName() { - return MODULE_NAME; - } - - private void checkNotInitialized() { - KatharsisAssert.assertNull("module is already initialized, no further changes can be performed", context); - } - - @Override - public void setupModule(ModuleContext context) { - this.context = context; - - context.addResourceInformationBuilder(getResourceInformationBuilder()); - context.addExceptionMapper(new OptimisticLockExceptionMapper()); - - if (resourceSearchPackage != null) { - setupClientResourceLookup(); - } else { - setupServerRepositories(); - setupTransactionMgmt(); - } - } - - protected void setupTransactionMgmt() { - context.addFilter(new AbstractFilter() { - - @Override - public BaseResponseContext filter(final FilterRequestContext context, final FilterChain chain) { - return transactionRunner.doInTransaction(new Callable() { - - @Override - public BaseResponseContext call() throws Exception { - return chain.doFilter(context); - } - }); - } - }); - } - - private void setupClientResourceLookup() { - context.addResourceLookup(new JpaEntityResourceLookup(resourceSearchPackage)); - } - - public class JpaEntityResourceLookup implements ResourceLookup { - - public JpaEntityResourceLookup(String packageName) { - } - - @Override - public Set> getResourceClasses() { - return entityClasses; - } - - @Override - public Set> getResourceRepositoryClasses() { - return Collections.emptySet(); - } - } - - private void setupServerRepositories() { - for (Class entityClass : entityClasses) { - MetaElement meta = metaLookup.getMeta(entityClass); - setupRepository(meta); - } - } - - @SuppressWarnings({ "rawtypes" }) - private void setupRepository(MetaElement meta) { - if (!(meta instanceof MetaEntity)) - return; - MetaEntity metaEntity = meta.asEntity(); - if (metaEntity.getPrimaryKey() == null) { - logger.warn("{} has no primary key and will be ignored", metaEntity.getName()); - return; - } - if (metaEntity.getPrimaryKey().getElements().size() > 1) { - logger.warn("{} has a compound primary key and will be ignored", metaEntity.getName()); - return; - } - - Class resourceClass = metaEntity.getImplementationClass(); - JpaEntityRepository repository = repositoryFactory.createEntityRepository(this, resourceClass); - context.addRepository(resourceClass, repository); - - Set> relatedResourceClasses = getRelatedResource(metaEntity); - for (Class relatedResourceClass : relatedResourceClasses) { - JpaRelationshipRepository relationshipRepository = repositoryFactory.createRelationshipRepository(this, - resourceClass, relatedResourceClass); - context.addRepository(resourceClass, relatedResourceClass, relationshipRepository); - } - } - - private Set> getRelatedResource(MetaEntity metaEntity) { - Set> relatedResourceClasses = new HashSet<>(); - for (MetaAttribute attr : metaEntity.getAttributes()) { - if (attr.isAssociation()) { - Class relType = null; - if(attr.getType().isCollection()) { - relType = attr.getType().getElementType().getImplementationClass(); - } - else { - relType = attr.getType().getImplementationClass(); - } - // only include relations that are exposed as repositories - if (entityClasses.contains(relType)) { - relatedResourceClasses.add(relType); - } - } - } - return relatedResourceClasses; - } - - /** - * ResourceInformationBuilder used to describe JPA entity classes. - */ - public ResourceInformationBuilder getResourceInformationBuilder() { - if (resourceInformationBuilder == null) - resourceInformationBuilder = new JpaResourceInformationBuilder(metaLookup, em, entityClasses); - return resourceInformationBuilder; - } - - /** - * {@link JpaQueryFactory}} implementation used to create JPA queries. - */ - public JpaQueryFactory getQueryFactory() { - return queryFactory; - } - - public void setQueryFactory(JpaQueryFactory queryFactory) { - this.queryFactory = queryFactory; - } - - /** - * {@link EntityManager}} in use. - */ - public EntityManager getEntityManager() { - return em; - } - - /** - * {@link EntityManagerFactory}} in use. - */ - public EntityManagerFactory getEntityManagerFactory() { - return emFactory; - } -} +package io.katharsis.jpa; + +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.Callable; +import java.util.concurrent.CopyOnWriteArrayList; + +import javax.persistence.Entity; +import javax.persistence.EntityManager; +import javax.persistence.EntityManagerFactory; +import javax.persistence.metamodel.ManagedType; + +import org.reflections.Reflections; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.katharsis.dispatcher.filter.AbstractFilter; +import io.katharsis.dispatcher.filter.FilterChain; +import io.katharsis.dispatcher.filter.FilterRequestContext; +import io.katharsis.jpa.internal.JpaResourceInformationBuilder; +import io.katharsis.jpa.internal.OptimisticLockExceptionMapper; +import io.katharsis.jpa.internal.meta.MetaAttribute; +import io.katharsis.jpa.internal.meta.MetaDataObject; +import io.katharsis.jpa.internal.meta.MetaElement; +import io.katharsis.jpa.internal.meta.MetaEntity; +import io.katharsis.jpa.internal.meta.MetaLookup; +import io.katharsis.jpa.internal.meta.MetaType; +import io.katharsis.jpa.internal.meta.impl.MetaResourceImpl; +import io.katharsis.jpa.internal.util.KatharsisAssert; +import io.katharsis.jpa.mapping.IdentityMapper; +import io.katharsis.jpa.mapping.JpaMapper; +import io.katharsis.jpa.query.JpaQueryFactory; +import io.katharsis.jpa.query.criteria.JpaCriteriaQueryFactory; +import io.katharsis.module.Module; +import io.katharsis.resource.information.ResourceInformationBuilder; +import io.katharsis.resource.registry.ResourceLookup; +import io.katharsis.response.BaseResponseContext; + +/** + * Katharsis module that adds support to expose JPA entities as repositories. It + * supports: + * + *
    + *
  • Sorting + * ?sort[task][name]=asc + *
  • + *
  • Filtering + * ?filter[task][name]=MyTask + *
  • + *
  • Access to relationships for any operation (sorting, filtering, etc.) + * + * ?filter[task][project][name]=MyProject + *
  • + *
  • Includes for relationships + * ?include[task]=project + *
  • + *
  • Paging + * ?page[offset]=20&page[limit]=10 + *
  • + *
+ * + * + * Not supported so far: + * + *
    + *
  • Selection of fields, always all fields are returned. + * ?fields[task]=id,name + *
  • + *
  • Sorting and filtering on related resources. Consider doing separate + * requests on the relations where necessary. + * /tasks/?sort[project][name]=asc + *
  • + *
+ * + */ +public class JpaModule implements Module { + + private Logger logger = LoggerFactory.getLogger(JpaModule.class); + + private static final String MODULE_NAME = "jpa"; + + private String resourceSearchPackage; + + private EntityManagerFactory emFactory; + + private EntityManager em; + + private JpaQueryFactory queryFactory; + + private ResourceInformationBuilder resourceInformationBuilder; + + private TransactionRunner transactionRunner; + + private ModuleContext context; + + private MetaLookup metaLookup = new MetaLookup(); + + private HashSet> entityClasses = new HashSet<>(); + + private Map, MappedRegistration> mappings = new HashMap<>(); + + private JpaRepositoryFactory repositoryFactory; + + private List filters = new CopyOnWriteArrayList<>(); + + /** + * Constructor used on client side. + */ + private JpaModule(String resourceSearchPackage) { + this.resourceSearchPackage = resourceSearchPackage; + + Reflections reflections; + if (resourceSearchPackage != null) { + String[] packageNames = resourceSearchPackage.split(","); + Object[] objPackageNames = new Object[packageNames.length]; + System.arraycopy(packageNames, 0, objPackageNames, 0, packageNames.length); + reflections = new Reflections(objPackageNames); + } + else { + reflections = new Reflections(resourceSearchPackage); + } + this.entityClasses.addAll(reflections.getTypesAnnotatedWith(Entity.class)); + } + + /** + * Constructor used on server side. + */ + private JpaModule(EntityManagerFactory emFactory, EntityManager em, TransactionRunner transactionRunner) { + this.emFactory = emFactory; + this.em = em; + this.transactionRunner = transactionRunner; + this.queryFactory = JpaCriteriaQueryFactory.newInstance(metaLookup, em); + + if (emFactory != null) { + Set> managedTypes = emFactory.getMetamodel().getManagedTypes(); + for (ManagedType managedType : managedTypes) { + Class managedJavaType = managedType.getJavaType(); + MetaElement meta = metaLookup.getMeta(managedJavaType); + if (meta instanceof MetaEntity) { + entityClasses.add(managedJavaType); + } + } + this.setRepositoryFactory(new DefaultJpaRepositoryFactory()); + } + } + + /** + * Creates a new JpaModule for a Katharsis client. + * + * @param resourceSearchPackage where to find the entity classes. + * @return module + */ + public static JpaModule newClientModule(String resourceSearchPackage) { + return new JpaModule(resourceSearchPackage); + } + + /** + * Creates a new JpaModule for a Katharsis server. No entities are + * by default exposed as JSON API resources. Make use of + * {@link #addEntityClass(Class)} andd {@link #addMappedEntityClass(Class, Class, JpaMapper)} + * to add resources. + * + * @param emFactory + * @param entityManager + * @param transactionRunner + * @return module + */ + public static JpaModule newServerModule(EntityManager em, TransactionRunner transactionRunner) { + return new JpaModule(null, em, transactionRunner); + } + + /** + * Creates a new JpaModule for a Katharsis server. All entities managed by + * the provided EntityManagerFactory are registered to the module + * and exposed as JSON API resources if not later configured otherwise. + * + * @param emFactory + * @param entityManager + * @param transactionRunner + * @return module + */ + public static JpaModule newServerModule(EntityManagerFactory emFactory, EntityManager em, + TransactionRunner transactionRunner) { + return new JpaModule(emFactory, em, transactionRunner); + } + + /** + * Adds the given filter to this module. Filter will be used by all repositories managed by this module. + * + * @param filter + */ + public void addFilter(JpaRepositoryFilter filter) { + filters.add(filter); + } + + /** + * Removes the given filter to this module. + * + * @param filter + */ + public void removeFilter(JpaRepositoryFilter filter) { + filters.remove(filter); + } + + /** + * @return all filters + */ + public List getFilters() { + return filters; + } + + public MetaLookup getMetaLookup() { + return metaLookup; + } + + public void setRepositoryFactory(JpaRepositoryFactory repositoryFactory) { + checkNotInitialized(); + this.repositoryFactory = repositoryFactory; + } + + /** + * @return set of entity classes made available as repository. + */ + public Set> getEntityClasses() { + return Collections.unmodifiableSet(entityClasses); + } + + /** + * Adds the given entity class to expose the entity as repository. + * + * @param entityClass + */ + public void addEntityClass(Class entityClass) { + checkNotInitialized(); + entityClasses.add(entityClass); + } + + /** + * Adds the given entity class which is mapped to a DTO with the provided mapper. + * + * @param entityClass + * @param dtoClass + * @param mapper + */ + public void addMappedEntityClass(Class entityClass, Class dtoClass, JpaMapper mapper) { + checkNotInitialized(); + if (mappings.containsKey(dtoClass)) { + throw new IllegalArgumentException(dtoClass.getName() + " is already registered"); + } + mappings.put(dtoClass, new MappedRegistration<>(entityClass, dtoClass, mapper)); + } + + /** + * Adds the given entity class which is mapped to a DTO with the provided mapper. + * + * @param entityClass + * @param dtoClass + * @param mapper + */ + public void removeMappedEntityClass(Class dtoClass) { + checkNotInitialized(); + mappings.remove(dtoClass); + } + + private static class MappedRegistration { + + Class entityClass; + + Class dtoClass; + + JpaMapper mapper; + + MappedRegistration(Class entityClass, Class dtoClass, JpaMapper mapper) { + this.entityClass = entityClass; + this.dtoClass = dtoClass; + this.mapper = mapper; + } + + public Class getEntityClass() { + return entityClass; + } + + public Class getDtoClass() { + return dtoClass; + } + + public JpaMapper getMapper() { + return mapper; + } + } + + /** + * Removes the given entity class to not expose the entity as repository. + * + * @param entityClass + */ + public void removeEntityClass(Class entityClass) { + checkNotInitialized(); + entityClasses.remove(entityClass); + } + + /** + * Removes all entity classes registered by default. Use {@link #addEntityClass(Class)} or + * {@link #addMappedEntityClass(Class, Class, JpaMapper)} to register classes manually. + */ + public void removeAllEntityClasses() { + checkNotInitialized(); + entityClasses.clear(); + } + + @Override + public String getModuleName() { + return MODULE_NAME; + } + + private void checkNotInitialized() { + KatharsisAssert.assertNull("module is already initialized, no further changes can be performed", context); + } + + @Override + public void setupModule(ModuleContext context) { + this.context = context; + + context.addResourceInformationBuilder(getResourceInformationBuilder()); + context.addExceptionMapper(new OptimisticLockExceptionMapper()); + + if (resourceSearchPackage != null) { + setupClientResourceLookup(); + } + else { + context.addResourceLookup(new MappingsResourceLookup()); + setupServerRepositories(); + setupTransactionMgmt(); + } + } + + /** + * Makes all the mapped DTO classes available to katharsis. + */ + private class MappingsResourceLookup implements ResourceLookup { + + @Override + public Set> getResourceClasses() { + return Collections.unmodifiableSet(mappings.keySet()); + } + + @Override + public Set> getResourceRepositoryClasses() { + return Collections.emptySet(); + } + } + + protected void setupTransactionMgmt() { + context.addFilter(new AbstractFilter() { + + @Override + public BaseResponseContext filter(final FilterRequestContext context, final FilterChain chain) { + return transactionRunner.doInTransaction(new Callable() { + + @Override + public BaseResponseContext call() throws Exception { + return chain.doFilter(context); + } + }); + } + }); + } + + private void setupClientResourceLookup() { + context.addResourceLookup(new JpaEntityResourceLookup(resourceSearchPackage)); + } + + public class JpaEntityResourceLookup implements ResourceLookup { + + public JpaEntityResourceLookup(String packageName) { + } + + @Override + public Set> getResourceClasses() { + return entityClasses; + } + + @Override + public Set> getResourceRepositoryClasses() { + return Collections.emptySet(); + } + } + + private void setupServerRepositories() { + for (Class entityClass : entityClasses) { + MetaElement meta = metaLookup.getMeta(entityClass); + setupRepository(meta); + } + + for (MappedRegistration mapping : mappings.values()) { + setupMappedRepository(mapping); + } + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + private void setupMappedRepository(MappedRegistration mapping) { + MetaEntity metaEntity = metaLookup.getMeta(mapping.getEntityClass()).asEntity(); + if (isValidEntity(metaEntity)) { + JpaEntityRepository repository = repositoryFactory.createMappedEntityRepository(this, mapping.getEntityClass(), + mapping.getDtoClass(), (JpaMapper) mapping.getMapper()); + context.addRepository(mapping.getDtoClass(), repository); + + setupRelationshipRepositories(mapping.getDtoClass()); + } + } + + @SuppressWarnings({ "rawtypes" }) + private void setupRepository(MetaElement meta) { + if (!(meta instanceof MetaEntity)) + return; + MetaEntity metaEntity = meta.asEntity(); + if (isValidEntity(metaEntity)) { + Class resourceClass = metaEntity.getImplementationClass(); + JpaEntityRepository repository = repositoryFactory.createEntityRepository(this, resourceClass); + context.addRepository(resourceClass, repository); + + setupRelationshipRepositories(resourceClass); + + } + } + + /** + * Sets up relationship repositories for the given resource class. In case of a mapper + * the resource class might not correspond to the entity class. + */ + @SuppressWarnings({ "rawtypes", "unchecked" }) + private void setupRelationshipRepositories(Class resourceClass) { + MetaDataObject meta = metaLookup.getMeta(resourceClass).asDataObject(); + + for (MetaAttribute attr : meta.getAttributes()) { + if (!attr.isAssociation()) { + continue; + } + MetaType attrType = attr.getType().getElementType(); + if (attrType instanceof MetaEntity) { + // normal entity association + Class attrImplClass = attr.getType().getElementType().getImplementationClass(); + + // only include relations that are exposed as repositories + if (entityClasses.contains(attrImplClass)) { + JpaRelationshipRepository relationshipRepository = repositoryFactory + .createRelationshipRepository(this, resourceClass, attrImplClass); + context.addRepository(resourceClass, attrImplClass, relationshipRepository); + } + } + else if (attrType instanceof MetaResourceImpl) { + Class attrImplClass = attrType.getImplementationClass(); + if (!mappings.containsKey(attrImplClass)) { + throw new IllegalStateException( + "no mapped entity for " + attrType.getName() + " reference by " + attr.getId() + " registered"); + } + MappedRegistration targetMapping = mappings.get(attrImplClass); + Class targetEntityClass = targetMapping.getEntityClass(); + Class targetDtoClass = targetMapping.getDtoClass(); + JpaMapper targetMapper = targetMapping.getMapper(); + + Class sourceEntityClass; + JpaMapper sourceMapper; + if (meta instanceof MetaEntity) { + sourceEntityClass = resourceClass; + sourceMapper = IdentityMapper.newInstance(); + } + else { + MappedRegistration sourceMapping = mappings.get(resourceClass); + sourceEntityClass = sourceMapping.getEntityClass(); + sourceMapper = sourceMapping.getMapper(); + } + + JpaRelationshipRepository relationshipRepository = repositoryFactory + .createMappedRelationshipRepository(this, sourceEntityClass, resourceClass, targetEntityClass, targetDtoClass, + sourceMapper, targetMapper); + context.addRepository(resourceClass, targetDtoClass, relationshipRepository); + } + else { + throw new IllegalStateException( + "unable to process relation: " + attr.getId() + ", neither a entity nor a mapped entity is referenced"); + } + } + } + + private boolean isValidEntity(MetaEntity metaEntity) { + if (metaEntity.getPrimaryKey() == null) { + logger.warn("{} has no primary key and will be ignored", metaEntity.getName()); + return false; + } + if (metaEntity.getPrimaryKey().getElements().size() > 1) { + logger.warn("{} has a compound primary key and will be ignored", metaEntity.getName()); + return false; + } + return true; + } + + /** + * ResourceInformationBuilder used to describe JPA entity classes. + */ + public ResourceInformationBuilder getResourceInformationBuilder() { + if (resourceInformationBuilder == null) + resourceInformationBuilder = new JpaResourceInformationBuilder(metaLookup, em, entityClasses); + return resourceInformationBuilder; + } + + /** + * {@link JpaQueryFactory}} implementation used to create JPA queries. + */ + public JpaQueryFactory getQueryFactory() { + return queryFactory; + } + + public void setQueryFactory(JpaQueryFactory queryFactory) { + this.queryFactory = queryFactory; + } + + /** + * {@link EntityManager}} in use. + */ + public EntityManager getEntityManager() { + return em; + } + + /** + * {@link EntityManagerFactory}} in use. + */ + public EntityManagerFactory getEntityManagerFactory() { + return emFactory; + } +} diff --git a/katharsis-jpa/src/main/java/io/katharsis/jpa/JpaRelationshipRepository.java b/katharsis-jpa/src/main/java/io/katharsis/jpa/JpaRelationshipRepository.java index 56a7b4d5..45ef4884 100644 --- a/katharsis-jpa/src/main/java/io/katharsis/jpa/JpaRelationshipRepository.java +++ b/katharsis-jpa/src/main/java/io/katharsis/jpa/JpaRelationshipRepository.java @@ -1,213 +1,276 @@ -package io.katharsis.jpa; - -import java.io.Serializable; -import java.util.Arrays; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; - -import javax.persistence.EntityManager; - -import io.katharsis.jpa.internal.JpaRepositoryUtils; -import io.katharsis.jpa.internal.meta.MetaAttribute; -import io.katharsis.jpa.internal.meta.MetaEntity; -import io.katharsis.jpa.internal.meta.MetaType; -import io.katharsis.jpa.internal.paging.DefaultPagedLinksInformation; -import io.katharsis.jpa.internal.paging.DefaultPagedMetaInformation; -import io.katharsis.jpa.internal.paging.PagedLinksInformation; -import io.katharsis.jpa.internal.paging.PagedMetaInformation; -import io.katharsis.jpa.internal.paging.PagedRepositoryBase; -import io.katharsis.jpa.internal.paging.PagedResultList; -import io.katharsis.jpa.query.JpaQuery; -import io.katharsis.jpa.query.JpaQueryExecutor; -import io.katharsis.jpa.query.JpaQueryFactory; -import io.katharsis.queryspec.QuerySpec; -import io.katharsis.queryspec.QuerySpecRelationshipRepository; - -public class JpaRelationshipRepository extends PagedRepositoryBase - implements QuerySpecRelationshipRepository { - - private JpaModule module; - - private Class entityClass; - - private Class relatedEntityClass; - - private MetaEntity entityMeta; - - public JpaRelationshipRepository(JpaModule module, Class entityClass, Class relatedEntityClass) { - this.module = module; - this.entityClass = entityClass; - this.entityMeta = module.getMetaLookup().getMeta(entityClass).asEntity(); - this.relatedEntityClass = relatedEntityClass; - } - - @Override - public void setRelation(T source, J targetId, String fieldName) { - MetaAttribute attrMeta = entityMeta.getAttribute(fieldName); - MetaAttribute oppositeAttrMeta = attrMeta.getOppositeAttribute(); - Class targetType = getElementType(attrMeta); - - EntityManager em = module.getEntityManager(); - Object target = targetId != null ? em.find(targetType, targetId) : null; - attrMeta.setValue(source, target); - - if (target != null && oppositeAttrMeta != null) { - if (oppositeAttrMeta.getType().isCollection()) { - oppositeAttrMeta.addValue(target, source); - } - else { - oppositeAttrMeta.setValue(target, source); - } - em.persist(target); - } - } - - @Override - public void setRelations(T source, Iterable targetIds, String fieldName) { - MetaAttribute attrMeta = entityMeta.getAttribute(fieldName); - MetaAttribute oppositeAttrMeta = attrMeta.getOppositeAttribute(); - Class targetType = getElementType(attrMeta); - - EntityManager em = module.getEntityManager(); - Collection targets = attrMeta.getType().asCollection().newInstance(); - for (J targetId : targetIds) { - Object target = em.find(targetType, targetId); - targets.add(target); - } - - // detach current - if (oppositeAttrMeta != null) { - Collection col = (Collection) attrMeta.getValue(source); - Iterator iterator = col.iterator(); - while (iterator.hasNext()) { - Object prevTarget = iterator.next(); - iterator.remove(); - if (oppositeAttrMeta.getType().isCollection()) { - oppositeAttrMeta.removeValue(prevTarget, source); - } - else { - oppositeAttrMeta.setValue(prevTarget, null); - } - } - } - - // attach new targets - for (Object target : targets) { - if (oppositeAttrMeta != null) { - if (oppositeAttrMeta.getType().isCollection()) { - oppositeAttrMeta.addValue(target, source); - } - else { - oppositeAttrMeta.setValue(target, source); - } - em.persist(target); - } - } - attrMeta.setValue(source, targets); - } - - private Class getElementType(MetaAttribute attrMeta) { - MetaType type = attrMeta.getType(); - if (type.isCollection()) - return type.asCollection().getElementType().getImplementationClass(); - else - return type.getImplementationClass(); - } - - @Override - public void addRelations(T source, Iterable targetIds, String fieldName) { - MetaAttribute attrMeta = entityMeta.getAttribute(fieldName); - MetaAttribute oppositeAttrMeta = attrMeta.getOppositeAttribute(); - Class targetType = getElementType(attrMeta); - - EntityManager em = module.getEntityManager(); - for (J targetId : targetIds) { - Object target = em.find(targetType, targetId); - attrMeta.addValue(source, target); - - if (oppositeAttrMeta != null) { - if (oppositeAttrMeta.getType().isCollection()) { - oppositeAttrMeta.addValue(target, source); - } - else { - oppositeAttrMeta.setValue(target, source); - } - em.persist(target); - } - } - em.persist(source); - } - - @Override - public void removeRelations(T source, Iterable targetIds, String fieldName) { - MetaAttribute attrMeta = entityMeta.getAttribute(fieldName); - MetaAttribute oppositeAttrMeta = attrMeta.getOppositeAttribute(); - Class targetType = getElementType(attrMeta); - - EntityManager em = module.getEntityManager(); - for (J targetId : targetIds) { - Object target = em.find(targetType, targetId); - attrMeta.removeValue(source, target); - - if (target != null && oppositeAttrMeta != null) { - if (oppositeAttrMeta.getType().isCollection()) { - oppositeAttrMeta.removeValue(target, source); - } - else { - oppositeAttrMeta.setValue(target, null); - } - } - } - } - - @SuppressWarnings("unchecked") - @Override - public D findOneTarget(I sourceId, String fieldName, QuerySpec querySpec) { - JpaQueryExecutor executor = getExecutor(sourceId, fieldName, querySpec); - return (D) executor.getUniqueResult(true); - } - - @SuppressWarnings("unchecked") - @Override - public List findManyTargets(I sourceId, String fieldName, QuerySpec querySpec) { - JpaQueryExecutor executor = getExecutor(sourceId, fieldName, querySpec); - List list = (List) executor.getResultList(); - if (querySpec.getLimit() != null) { - long totalRowCount = executor.getTotalRowCount(); - return new PagedResultList<>(list, totalRowCount, entityClass, sourceId, fieldName); - } - else { - return list; - } - } - - private JpaQueryExecutor getExecutor(I sourceId, String fieldName, QuerySpec querySpec) { - JpaQueryFactory queryBuilderFactory = module.getQueryFactory(); - JpaQuery query = queryBuilderFactory.query(entityClass, fieldName, Arrays.asList(sourceId)); - JpaRepositoryUtils.prepareQuery(query, querySpec); - JpaQueryExecutor executor = query.buildExecutor(); - JpaRepositoryUtils.prepareExecutor(executor, querySpec); - return executor; - } - - @Override - public Class getSourceResourceClass() { - return entityClass; - } - - @Override - public Class getTargetResourceClass() { - return relatedEntityClass; - } - - @Override - protected PagedMetaInformation newPagedMetaInformation() { - return new DefaultPagedMetaInformation(); - } - - @Override - protected PagedLinksInformation newPagedLinksInformation() { - return new DefaultPagedLinksInformation(); - } -} +package io.katharsis.jpa; + +import java.io.Serializable; +import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +import javax.persistence.EntityManager; + +import io.katharsis.jpa.internal.JpaRepositoryBase; +import io.katharsis.jpa.internal.JpaRepositoryUtils; +import io.katharsis.jpa.internal.meta.MetaAttribute; +import io.katharsis.jpa.internal.meta.MetaEntity; +import io.katharsis.jpa.internal.meta.MetaType; +import io.katharsis.jpa.internal.paging.DefaultPagedLinksInformation; +import io.katharsis.jpa.internal.paging.DefaultPagedMetaInformation; +import io.katharsis.jpa.internal.paging.PagedLinksInformation; +import io.katharsis.jpa.internal.paging.PagedMetaInformation; +import io.katharsis.jpa.internal.paging.PagedResultList; +import io.katharsis.jpa.mapping.IdentityMapper; +import io.katharsis.jpa.mapping.JpaMapper; +import io.katharsis.jpa.query.ComputedAttributeRegistry; +import io.katharsis.jpa.query.JpaQuery; +import io.katharsis.jpa.query.JpaQueryExecutor; +import io.katharsis.jpa.query.JpaQueryFactory; +import io.katharsis.jpa.query.Tuple; +import io.katharsis.queryspec.QuerySpec; +import io.katharsis.queryspec.QuerySpecRelationshipRepository; + +public class JpaRelationshipRepository extends JpaRepositoryBase + implements QuerySpecRelationshipRepository { + + private Class sourceResourceClass; + + private Class sourceEntityClass; + + private Class targetEntityClass; + + private MetaEntity entityMeta; + + private JpaMapper sourceMapper; + + /** + * JPA relationship directly exposed as repository + * + * @param module + * @param sourceEntityClass + * @param relatedEntityClass + */ + public JpaRelationshipRepository(JpaModule module, Class sourceEntityClass, Class relatedEntityClass) { + super(module, relatedEntityClass, IdentityMapper.newInstance()); + this.sourceEntityClass = sourceEntityClass; + this.sourceResourceClass = sourceEntityClass; + this.entityMeta = module.getMetaLookup().getMeta(sourceEntityClass).asEntity(); + this.targetEntityClass = relatedEntityClass; + this.sourceMapper = IdentityMapper.newInstance(); + } + + /** + * JPA relationship mapped to a DTO relationship and exposed as repository + * + * @param module + * @param sourceEntityClass + * @param sourceResourceClass + * @param relatedEntityClass + * @param relatedResourceClass + * @param sourceMapper + * @param targetMapper + */ + public JpaRelationshipRepository(JpaModule module, Class sourceEntityClass, Class sourceResourceClass, + Class relatedEntityClass, Class relatedResourceClass, JpaMapper sourceMapper, + JpaMapper targetMapper) { + super(module, relatedResourceClass, targetMapper); + this.sourceResourceClass = sourceResourceClass; + this.sourceEntityClass = sourceEntityClass; + this.entityMeta = module.getMetaLookup().getMeta(sourceEntityClass).asEntity(); + this.targetEntityClass = relatedEntityClass; + this.sourceMapper = sourceMapper; + } + + @Override + public void setRelation(S source, J targetId, String fieldName) { + MetaAttribute attrMeta = entityMeta.getAttribute(fieldName); + MetaAttribute oppositeAttrMeta = attrMeta.getOppositeAttribute(); + Class targetType = getElementType(attrMeta); + + Object sourceEntity = sourceMapper.unmap(source); + + EntityManager em = module.getEntityManager(); + Object target = targetId != null ? em.find(targetType, targetId) : null; + attrMeta.setValue(sourceEntity, target); + + if (target != null && oppositeAttrMeta != null) { + if (oppositeAttrMeta.getType().isCollection()) { + oppositeAttrMeta.addValue(target, sourceEntity); + } + else { + oppositeAttrMeta.setValue(target, sourceEntity); + } + em.persist(target); + } + } + + @Override + public void setRelations(S source, Iterable targetIds, String fieldName) { + MetaAttribute attrMeta = entityMeta.getAttribute(fieldName); + MetaAttribute oppositeAttrMeta = attrMeta.getOppositeAttribute(); + Class targetType = getElementType(attrMeta); + + Object sourceEntity = sourceMapper.unmap(source); + + EntityManager em = module.getEntityManager(); + Collection targets = attrMeta.getType().asCollection().newInstance(); + for (J targetId : targetIds) { + Object target = em.find(targetType, targetId); + targets.add(target); + } + + // detach current + if (oppositeAttrMeta != null) { + Collection col = (Collection) attrMeta.getValue(sourceEntity); + Iterator iterator = col.iterator(); + while (iterator.hasNext()) { + Object prevTarget = iterator.next(); + iterator.remove(); + if (oppositeAttrMeta.getType().isCollection()) { + oppositeAttrMeta.removeValue(prevTarget, sourceEntity); + } + else { + oppositeAttrMeta.setValue(prevTarget, null); + } + } + } + + // attach new targets + for (Object target : targets) { + if (oppositeAttrMeta != null) { + if (oppositeAttrMeta.getType().isCollection()) { + oppositeAttrMeta.addValue(target, sourceEntity); + } + else { + oppositeAttrMeta.setValue(target, sourceEntity); + } + em.persist(target); + } + } + attrMeta.setValue(sourceEntity, targets); + } + + private Class getElementType(MetaAttribute attrMeta) { + MetaType type = attrMeta.getType(); + if (type.isCollection()) + return type.asCollection().getElementType().getImplementationClass(); + else + return type.getImplementationClass(); + } + + @Override + public void addRelations(S source, Iterable targetIds, String fieldName) { + MetaAttribute attrMeta = entityMeta.getAttribute(fieldName); + MetaAttribute oppositeAttrMeta = attrMeta.getOppositeAttribute(); + Class targetType = getElementType(attrMeta); + + Object sourceEntity = sourceMapper.unmap(source); + + EntityManager em = module.getEntityManager(); + for (J targetId : targetIds) { + Object target = em.find(targetType, targetId); + attrMeta.addValue(sourceEntity, target); + + if (oppositeAttrMeta != null) { + if (oppositeAttrMeta.getType().isCollection()) { + oppositeAttrMeta.addValue(target, sourceEntity); + } + else { + oppositeAttrMeta.setValue(target, sourceEntity); + } + em.persist(target); + } + } + em.persist(sourceEntity); + } + + @Override + public void removeRelations(S source, Iterable targetIds, String fieldName) { + MetaAttribute attrMeta = entityMeta.getAttribute(fieldName); + MetaAttribute oppositeAttrMeta = attrMeta.getOppositeAttribute(); + Class targetType = getElementType(attrMeta); + + Object sourceEntity = sourceMapper.unmap(source); + + EntityManager em = module.getEntityManager(); + for (J targetId : targetIds) { + Object target = em.find(targetType, targetId); + attrMeta.removeValue(sourceEntity, target); + + if (target != null && oppositeAttrMeta != null) { + if (oppositeAttrMeta.getType().isCollection()) { + oppositeAttrMeta.removeValue(target, sourceEntity); + } + else { + oppositeAttrMeta.setValue(target, null); + } + } + } + } + + @Override + public T findOneTarget(I sourceId, String fieldName, QuerySpec querySpec) { + return getUniqueOrNull(getResults(sourceId, fieldName, querySpec)); + } + + @Override + public List findManyTargets(I sourceId, String fieldName, QuerySpec querySpec) { + return getResults(sourceId, fieldName, querySpec); + + } + + private List getResults(I sourceId, String fieldName, QuerySpec querySpec) { + QuerySpec filteredQuerySpec = filterQuerySpec(querySpec); + + JpaQueryFactory queryFactory = module.getQueryFactory(); + JpaQuery query = queryFactory.query(sourceEntityClass, fieldName, Arrays.asList(sourceId)); + query = filterQuery(filteredQuerySpec, query); + + ComputedAttributeRegistry computedAttributesRegistry = queryFactory.getComputedAttributes(); + Set computedAttrs = computedAttributesRegistry.getForType(targetEntityClass); + + JpaRepositoryUtils.prepareQuery(query, filteredQuerySpec, computedAttrs); + + JpaQueryExecutor executor = query.buildExecutor(); + JpaRepositoryUtils.prepareExecutor(executor, filteredQuerySpec); + executor = filterExecutor(filteredQuerySpec, executor); + + List tuples = executor.getResultTuples(); + tuples = filterTuples(querySpec, tuples); + + List list = map(tuples); + + // compute total row count if necessary to do proper paging + if (querySpec.getLimit() != null) { + long totalRowCount = executor.getTotalRowCount(); + list = new PagedResultList<>(list, totalRowCount, sourceResourceClass, sourceId, fieldName); + } + else { + return list; + } + return filterResults(querySpec, list); + } + + @Override + public Class getSourceResourceClass() { + return sourceResourceClass; + } + + @Override + public Class getTargetResourceClass() { + return resourceClass; + } + + public Class getTargetEntityClass() { + return targetEntityClass; + } + + @Override + protected PagedMetaInformation newPagedMetaInformation() { + return new DefaultPagedMetaInformation(); + } + + @Override + protected PagedLinksInformation newPagedLinksInformation() { + return new DefaultPagedLinksInformation(); + } +} diff --git a/katharsis-jpa/src/main/java/io/katharsis/jpa/JpaRepositoryFactory.java b/katharsis-jpa/src/main/java/io/katharsis/jpa/JpaRepositoryFactory.java index e89d370c..90afcc81 100644 --- a/katharsis-jpa/src/main/java/io/katharsis/jpa/JpaRepositoryFactory.java +++ b/katharsis-jpa/src/main/java/io/katharsis/jpa/JpaRepositoryFactory.java @@ -1,16 +1,62 @@ -package io.katharsis.jpa; - -import java.io.Serializable; - -/** - * Used to create resource and relationship repositories for the provided - * classes. By default {@link DefaultJpaRepositoryFactory}} is used. - */ -public interface JpaRepositoryFactory { - - public JpaEntityRepository createEntityRepository(JpaModule module, - Class entityClass); - - public JpaRelationshipRepository createRelationshipRepository( - JpaModule module, Class entityClass, Class relatedEntityClass); -} +package io.katharsis.jpa; + +import java.io.Serializable; + +import io.katharsis.jpa.mapping.JpaMapper; + +/** + * Used to create resource and relationship repositories for the provided + * classes. By default {@link DefaultJpaRepositoryFactory}} is used. + */ +public interface JpaRepositoryFactory { + + /** + * Creates a resource repository that maps an entity directly to a JSON API endpoint. + * + * @param module + * @param entityClass + * @return repository + */ + public JpaEntityRepository createEntityRepository(JpaModule module, Class entityClass); + + /** + * Creates a relationship repository that maps an entity relationship directly to a JSON API endpoint. + * + * @param module + * @param entityClass + * @param relatedEntityClass + * @return repository + */ + public JpaRelationshipRepository createRelationshipRepository( + JpaModule module, Class entityClass, Class relatedEntityClass); + + /** + * Creates a resource repository that maps entities to DTOs and makes the DTOs available as json api endpoints. + * + * @param jpaModule + * @param entityClass + * @param dtoClass + * @param mapper + * @return repository + */ + public JpaEntityRepository createMappedEntityRepository(JpaModule module, + Class entityClass, Class dtoClass, JpaMapper mapper); + + /** + * Creates a relationship repository that maps entity relations to DTO relations and makes those available as json api endpoints. + * + * @param module + * @param sourceEntityClass + * @param sourceResourceClass + * @param targetEntityClass + * @param targetResourceClass + * @param sourceMapper + * @param targetMapper + * @return repository + */ + public JpaRelationshipRepository createMappedRelationshipRepository( + JpaModule module, Class sourceEntityClass, Class sourceResourceClass, Class targetEntityClass, Class targetResourceClass, + JpaMapper sourceMapper, JpaMapper targetMapper); + + +} diff --git a/katharsis-jpa/src/main/java/io/katharsis/jpa/JpaRepositoryFilter.java b/katharsis-jpa/src/main/java/io/katharsis/jpa/JpaRepositoryFilter.java new file mode 100644 index 00000000..c346eb9f --- /dev/null +++ b/katharsis-jpa/src/main/java/io/katharsis/jpa/JpaRepositoryFilter.java @@ -0,0 +1,70 @@ +package io.katharsis.jpa; + +import java.util.List; + +import io.katharsis.jpa.query.JpaQuery; +import io.katharsis.jpa.query.JpaQueryExecutor; +import io.katharsis.jpa.query.Tuple; +import io.katharsis.queryspec.QuerySpec; + +/** + * Can be registered with the JpaModule and gets notified about all kinds of repository events. + * The filter then has to possiblity to do all kinds of changes. + */ +public interface JpaRepositoryFilter { + + /** + * Specifies whether any of the filter methods should be executed for the given resourceType.; + * + * @param resourceType + * @return true if filter should be used for the given resouceType. + */ + boolean accept(Class resourceType); + + /** + * Allows to customize the querySpec before creating the query. + * + * @param repository + * @param querySpec + */ + QuerySpec filterQuerySpec(Object repository, QuerySpec querySpec); + + /** + * Allows to customize the query. + * + * @param repository + * @param querySpec + * @param query + */ + JpaQuery filterQuery(Object repository, QuerySpec querySpec, JpaQuery query); + + /** + * Allows to customize the query executor. + * + * @param repository + * @param querySpec + * @param executor + */ + JpaQueryExecutor filterExecutor(Object repository, QuerySpec querySpec, JpaQueryExecutor executor); + + /** + * Allows to filter tuples and return the filtered slistet. + * + * @param repository + * @param querySpec + * @param tuples + * @return filtered list of tuples + */ + List filterTuples(Object repository, QuerySpec querySpec, List tuples); + + /** + * Allows to filter resources and return the filtered list. + * + * @param repository + * @param querySpec + * @param resources + * @return filtered list of resources + */ + List filterResults(Object repository, QuerySpec querySpec, List resources); + +} diff --git a/katharsis-jpa/src/main/java/io/katharsis/jpa/JpaRepositoryFilterBase.java b/katharsis-jpa/src/main/java/io/katharsis/jpa/JpaRepositoryFilterBase.java new file mode 100644 index 00000000..0e5e6255 --- /dev/null +++ b/katharsis-jpa/src/main/java/io/katharsis/jpa/JpaRepositoryFilterBase.java @@ -0,0 +1,44 @@ +package io.katharsis.jpa; + +import java.util.List; + +import io.katharsis.jpa.query.JpaQuery; +import io.katharsis.jpa.query.JpaQueryExecutor; +import io.katharsis.jpa.query.Tuple; +import io.katharsis.queryspec.QuerySpec; + +/** + * Empty default implementation for {@link JpaRepositoryFilter}. + */ +public class JpaRepositoryFilterBase implements JpaRepositoryFilter { + + @Override + public boolean accept(Class resourceType) { + return true; + } + + @Override + public QuerySpec filterQuerySpec(Object repository, QuerySpec querySpec) { + return querySpec; + } + + @Override + public JpaQuery filterQuery(Object repository, QuerySpec querySpec, JpaQuery query) { + return query; + } + + @Override + public JpaQueryExecutor filterExecutor(Object repository, QuerySpec querySpec, JpaQueryExecutor executor) { + return executor; + } + + @Override + public List filterTuples(Object repository, QuerySpec querySpec, List tuples) { + return tuples; + } + + @Override + public List filterResults(Object repository, QuerySpec querySpec, List resources) { + return resources; + } +} diff --git a/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/JpaQueryFactoryBase.java b/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/JpaQueryFactoryBase.java index cda4adee..a34f2b99 100644 --- a/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/JpaQueryFactoryBase.java +++ b/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/JpaQueryFactoryBase.java @@ -1,24 +1,30 @@ -package io.katharsis.jpa.internal; - -import javax.persistence.EntityManager; - -import io.katharsis.jpa.internal.meta.MetaLookup; -import io.katharsis.jpa.internal.query.VirtualAttributeRegistry; - -public abstract class JpaQueryFactoryBase { - - protected EntityManager em; - protected VirtualAttributeRegistry virtualAttrs; - protected MetaLookup metaLookup; - - protected JpaQueryFactoryBase(MetaLookup metaLookup, EntityManager em) { - this.em = em; - this.metaLookup = metaLookup; - this.virtualAttrs = new VirtualAttributeRegistry(metaLookup); - } - - public EntityManager getEntityManager() { - return em; - } - -} +package io.katharsis.jpa.internal; + +import javax.persistence.EntityManager; + +import io.katharsis.jpa.internal.meta.MetaLookup; +import io.katharsis.jpa.internal.query.ComputedAttributeRegistryImpl; +import io.katharsis.jpa.query.ComputedAttributeRegistry; + +public abstract class JpaQueryFactoryBase { + + protected EntityManager em; + + protected ComputedAttributeRegistryImpl computedAttrs; + + protected MetaLookup metaLookup; + + protected JpaQueryFactoryBase(MetaLookup metaLookup, EntityManager em) { + this.em = em; + this.metaLookup = metaLookup; + this.computedAttrs = new ComputedAttributeRegistryImpl(metaLookup); + } + + public EntityManager getEntityManager() { + return em; + } + + public ComputedAttributeRegistry getComputedAttributes() { + return computedAttrs; + } +} diff --git a/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/JpaRepositoryBase.java b/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/JpaRepositoryBase.java new file mode 100644 index 00000000..b19dcdc7 --- /dev/null +++ b/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/JpaRepositoryBase.java @@ -0,0 +1,99 @@ +package io.katharsis.jpa.internal; + +import java.util.ArrayList; +import java.util.List; + +import io.katharsis.jpa.JpaModule; +import io.katharsis.jpa.JpaRepositoryFilter; +import io.katharsis.jpa.internal.paging.PagedRepositoryBase; +import io.katharsis.jpa.mapping.JpaMapper; +import io.katharsis.jpa.query.JpaQuery; +import io.katharsis.jpa.query.JpaQueryExecutor; +import io.katharsis.jpa.query.Tuple; +import io.katharsis.queryspec.QuerySpec; + +public abstract class JpaRepositoryBase extends PagedRepositoryBase { + + protected JpaModule module; + + protected Class resourceClass; + + protected JpaMapper mapper; + + @SuppressWarnings({ "rawtypes", "unchecked" }) + protected JpaRepositoryBase(JpaModule module, Class resourceType, JpaMapper mapper) { + this.module = module; + this.resourceClass = resourceType; + this.mapper = mapper; + } + + protected static D getUniqueOrNull(List list) { + if (list.isEmpty()) { + return null; + } + else if (list.size() == 1) { + return list.get(0); + } + else { + throw new IllegalStateException("unique result expected"); + } + } + + protected QuerySpec filterQuerySpec(QuerySpec querySpec) { + QuerySpec filteredQuerySpec = querySpec; + for (JpaRepositoryFilter filter : module.getFilters()) { + if (filter.accept(resourceClass)) { + filteredQuerySpec = filter.filterQuerySpec(this, filteredQuerySpec); + } + } + return filteredQuerySpec; + } + + protected JpaQuery filterQuery(QuerySpec querySpec, JpaQuery query) { + JpaQuery filteredQuery = query; + for (JpaRepositoryFilter filter : module.getFilters()) { + if (filter.accept(resourceClass)) { + filteredQuery = filter.filterQuery(this, querySpec, filteredQuery); + } + } + return filteredQuery; + } + + protected JpaQueryExecutor filterExecutor(QuerySpec querySpec, JpaQueryExecutor executor) { + JpaQueryExecutor filteredExecutor = executor; + for (JpaRepositoryFilter filter : module.getFilters()) { + if (filter.accept(resourceClass)) { + filteredExecutor = filter.filterExecutor(this, querySpec, filteredExecutor); + } + } + return filteredExecutor; + } + + protected List filterTuples(QuerySpec querySpec, List tuples) { + List filteredTuples = tuples; + for (JpaRepositoryFilter filter : module.getFilters()) { + if (filter.accept(resourceClass)) { + filteredTuples = filter.filterTuples(this, querySpec, filteredTuples); + } + } + return filteredTuples; + } + + protected List filterResults(QuerySpec querySpec, List resources) { + List filteredResources = resources; + for (JpaRepositoryFilter filter : module.getFilters()) { + if (filter.accept(resourceClass)) { + filteredResources = filter.filterResults(this, querySpec, filteredResources); + } + } + return filteredResources; + } + + protected List map(List tuples) { + List resources = new ArrayList<>(); + for (Tuple tuple : tuples) { + resources.add(mapper.map(tuple)); + } + return resources; + } +} diff --git a/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/JpaRepositoryUtils.java b/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/JpaRepositoryUtils.java index 68ea3866..28733fb2 100644 --- a/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/JpaRepositoryUtils.java +++ b/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/JpaRepositoryUtils.java @@ -1,57 +1,65 @@ -package io.katharsis.jpa.internal; - -import io.katharsis.jpa.internal.meta.MetaAttribute; -import io.katharsis.jpa.internal.meta.MetaEntity; -import io.katharsis.jpa.internal.meta.MetaKey; -import io.katharsis.jpa.internal.util.KatharsisAssert; -import io.katharsis.jpa.query.JpaQuery; -import io.katharsis.jpa.query.JpaQueryExecutor; -import io.katharsis.queryspec.FilterSpec; -import io.katharsis.queryspec.IncludeSpec; -import io.katharsis.queryspec.QuerySpec; -import io.katharsis.queryspec.SortSpec; - -public class JpaRepositoryUtils { - - private JpaRepositoryUtils() { - } - - /** - * Gets the primary key attribute of the given entity. Assumes a primary key - * is available and no compound primary keys are supported. - */ - public static MetaAttribute getPrimaryKeyAttr(MetaEntity meta) { - MetaKey primaryKey = meta.getPrimaryKey(); - KatharsisAssert.assertNotNull(primaryKey); - KatharsisAssert.assertEquals(1, primaryKey.getElements().size()); - return primaryKey.getElements().get(0); - } - - public static void prepareQuery(JpaQuery query, QuerySpec querySpec) { - for (FilterSpec filter : querySpec.getFilters()) { - query.addFilter(filter); - } - for (SortSpec sortSpec : querySpec.getSort()) { - query.addSortBy(sortSpec); - } - if (!querySpec.getIncludedFields().isEmpty()) { - throw new UnsupportedOperationException("includeFields not yet supported"); - } - } - - public static void prepareExecutor(JpaQueryExecutor executor, QuerySpec querySpec) { - for (IncludeSpec included : querySpec.getIncludedRelations()) { - executor.fetch(included.getAttributePath()); - } - executor.setOffset((int) querySpec.getOffset()); - if (querySpec.getOffset() > Integer.MAX_VALUE) { - throw new IllegalArgumentException("offset cannot be larger than Integer.MAX_VALUE"); - } - if (querySpec.getLimit() != null) { - if (querySpec.getLimit() > Integer.MAX_VALUE) { - throw new IllegalArgumentException("limit cannot be larger than Integer.MAX_VALUE"); - } - executor.setLimit((int) querySpec.getLimit().longValue()); - } - } -} +package io.katharsis.jpa.internal; + +import java.util.Arrays; +import java.util.Set; + +import io.katharsis.jpa.internal.meta.MetaAttribute; +import io.katharsis.jpa.internal.meta.MetaEntity; +import io.katharsis.jpa.internal.meta.MetaKey; +import io.katharsis.jpa.internal.util.KatharsisAssert; +import io.katharsis.jpa.query.JpaQuery; +import io.katharsis.jpa.query.JpaQueryExecutor; +import io.katharsis.queryspec.FilterSpec; +import io.katharsis.queryspec.IncludeSpec; +import io.katharsis.queryspec.QuerySpec; +import io.katharsis.queryspec.SortSpec; + +public class JpaRepositoryUtils { + + private JpaRepositoryUtils() { + } + + /** + * Gets the primary key attribute of the given entity. Assumes a primary key + * is available and no compound primary keys are supported. + */ + public static MetaAttribute getPrimaryKeyAttr(MetaEntity meta) { + MetaKey primaryKey = meta.getPrimaryKey(); + KatharsisAssert.assertNotNull(primaryKey); + KatharsisAssert.assertEquals(1, primaryKey.getElements().size()); + return primaryKey.getElements().get(0); + } + + public static void prepareQuery(JpaQuery query, QuerySpec querySpec, Set computedAttrs) { + + for (String computedAttr : computedAttrs) { + query.addSelection(Arrays.asList(computedAttr)); + } + + for (FilterSpec filter : querySpec.getFilters()) { + query.addFilter(filter); + } + for (SortSpec sortSpec : querySpec.getSort()) { + query.addSortBy(sortSpec); + } + if (!querySpec.getIncludedFields().isEmpty()) { + throw new UnsupportedOperationException("includeFields not yet supported"); + } + } + + public static void prepareExecutor(JpaQueryExecutor executor, QuerySpec querySpec) { + for (IncludeSpec included : querySpec.getIncludedRelations()) { + executor.fetch(included.getAttributePath()); + } + executor.setOffset((int) querySpec.getOffset()); + if (querySpec.getOffset() > Integer.MAX_VALUE) { + throw new IllegalArgumentException("offset cannot be larger than Integer.MAX_VALUE"); + } + if (querySpec.getLimit() != null) { + if (querySpec.getLimit() > Integer.MAX_VALUE) { + throw new IllegalArgumentException("limit cannot be larger than Integer.MAX_VALUE"); + } + executor.setLimit((int) querySpec.getLimit().longValue()); + } + } +} diff --git a/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/AbstractMetaEntityAttributeImpl.java b/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/AbstractMetaEntityAttributeImpl.java index c2a505f8..a7f0dd6b 100644 --- a/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/AbstractMetaEntityAttributeImpl.java +++ b/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/AbstractMetaEntityAttributeImpl.java @@ -1,142 +1,142 @@ -package io.katharsis.jpa.internal.meta.impl; - -import java.beans.PropertyDescriptor; -import java.lang.reflect.Field; - -import javax.persistence.ElementCollection; -import javax.persistence.EmbeddedId; -import javax.persistence.FetchType; -import javax.persistence.Id; -import javax.persistence.ManyToMany; -import javax.persistence.ManyToOne; -import javax.persistence.OneToMany; -import javax.persistence.OneToOne; -import javax.persistence.Version; - -import io.katharsis.jpa.internal.meta.MetaAttribute; -import io.katharsis.jpa.internal.meta.MetaEntity; -import io.katharsis.jpa.internal.meta.MetaLookup; -import io.katharsis.jpa.internal.meta.MetaType; - -public class AbstractMetaEntityAttributeImpl extends MetaAttributeImpl { - - protected Field field; - - private boolean derived; - - private boolean lazy = false; - - private String mappedBy = null; - - private MetaAttribute oppositeAttr; - - private boolean version = false; - - private boolean idField; - - public AbstractMetaEntityAttributeImpl(MetaDataObjectImpl parent, PropertyDescriptor desc) { - super(parent, desc); - - field = getField(parent, desc); - if (field != null) { - readAnnotations(field); - } - else { - derived = true; - } - } - - private static Field getField(MetaDataObjectImpl parent, PropertyDescriptor desc) { - try { - Field field = parent.getImplementationClass().getDeclaredField(desc.getName()); - field.setAccessible(true); - return field; - } - catch (NoSuchFieldException e) { // NOSONAR - return null; - } - } - - private void readAnnotations(Field field) { // NOSONAR - ManyToMany manyManyAnnotation = field.getAnnotation(ManyToMany.class); - ManyToOne manyOneAnnotation = field.getAnnotation(ManyToOne.class); - OneToMany oneManyAnnotation = field.getAnnotation(OneToMany.class); - OneToOne oneOneAnnotation = field.getAnnotation(OneToOne.class); - Version versionAnnotation = field.getAnnotation(Version.class); - ElementCollection elemCollectionAnnotation = field.getAnnotation(ElementCollection.class); - - version = versionAnnotation != null; - - FetchType fetchType = null; - if (manyManyAnnotation != null) { - mappedBy = manyManyAnnotation.mappedBy(); - fetchType = manyManyAnnotation.fetch(); - } - if (oneManyAnnotation != null) { - mappedBy = oneManyAnnotation.mappedBy(); - fetchType = oneManyAnnotation.fetch(); - } - if (oneOneAnnotation != null) { - mappedBy = oneOneAnnotation.mappedBy(); - fetchType = oneOneAnnotation.fetch(); - } - - if (mappedBy != null && mappedBy.length() == 0) { - mappedBy = null; - } - - setAssociation( - manyManyAnnotation != null || manyOneAnnotation != null || oneManyAnnotation != null || oneOneAnnotation != null); - - boolean lazyCollection = elemCollectionAnnotation != null && elemCollectionAnnotation.fetch() != FetchType.EAGER; - boolean lazyAssociation = isAssociation() && (fetchType == null || fetchType == FetchType.LAZY); - - lazy = lazyCollection || lazyAssociation; - - idField = field.getAnnotation(EmbeddedId.class) != null || field.getAnnotation(Id.class) != null; - } - - @Override - public boolean isId() { - return idField; - } - - @Override - public MetaAttribute getOppositeAttribute() { - return oppositeAttr; - } - - @Override - public void init(MetaLookup lookup) { - super.init(lookup); - if (mappedBy != null) { - MetaType mappedType = getType(); - if (mappedType.isCollection()) { - mappedType = mappedType.asCollection().getElementType(); - } - MetaEntity type = mappedType.asEntity(); - oppositeAttr = type.getAttribute(mappedBy); - ((AbstractMetaEntityAttributeImpl) oppositeAttr).oppositeAttr = this; - } - } - - @Override - public boolean isLazy() { - return lazy; - } - - @Override - public String getId() { - return getParent() + "." + getName(); - } - - @Override - public boolean isDerived() { - return derived; - } - - @Override - public boolean isVersion() { - return version; - } +package io.katharsis.jpa.internal.meta.impl; + +import java.beans.PropertyDescriptor; +import java.lang.reflect.Field; + +import javax.persistence.ElementCollection; +import javax.persistence.EmbeddedId; +import javax.persistence.FetchType; +import javax.persistence.Id; +import javax.persistence.ManyToMany; +import javax.persistence.ManyToOne; +import javax.persistence.OneToMany; +import javax.persistence.OneToOne; +import javax.persistence.Version; + +import io.katharsis.jpa.internal.meta.MetaAttribute; +import io.katharsis.jpa.internal.meta.MetaEntity; +import io.katharsis.jpa.internal.meta.MetaLookup; +import io.katharsis.jpa.internal.meta.MetaType; + +public class AbstractMetaEntityAttributeImpl extends MetaAttributeImpl { + + protected Field field; + + private boolean derived; + + private boolean lazy = false; + + private String mappedBy = null; + + private MetaAttribute oppositeAttr; + + private boolean version = false; + + private boolean idField; + + public AbstractMetaEntityAttributeImpl(MetaDataObjectImpl parent, PropertyDescriptor desc) { + super(parent, desc); + + field = getField(parent, desc); + if (field != null) { + readAnnotations(field); + } + else { + derived = true; + } + } + + private static Field getField(MetaDataObjectImpl parent, PropertyDescriptor desc) { + try { + Field field = parent.getImplementationClass().getDeclaredField(desc.getName()); + field.setAccessible(true); + return field; + } + catch (NoSuchFieldException e) { // NOSONAR + return null; + } + } + + private void readAnnotations(Field field) { // NOSONAR + ManyToMany manyManyAnnotation = field.getAnnotation(ManyToMany.class); + ManyToOne manyOneAnnotation = field.getAnnotation(ManyToOne.class); + OneToMany oneManyAnnotation = field.getAnnotation(OneToMany.class); + OneToOne oneOneAnnotation = field.getAnnotation(OneToOne.class); + Version versionAnnotation = field.getAnnotation(Version.class); + ElementCollection elemCollectionAnnotation = field.getAnnotation(ElementCollection.class); + + version = versionAnnotation != null; + + FetchType fetchType = null; + if (manyManyAnnotation != null) { + mappedBy = manyManyAnnotation.mappedBy(); + fetchType = manyManyAnnotation.fetch(); + } + if (oneManyAnnotation != null) { + mappedBy = oneManyAnnotation.mappedBy(); + fetchType = oneManyAnnotation.fetch(); + } + if (oneOneAnnotation != null) { + mappedBy = oneOneAnnotation.mappedBy(); + fetchType = oneOneAnnotation.fetch(); + } + + if (mappedBy != null && mappedBy.length() == 0) { + mappedBy = null; + } + + setAssociation( + manyManyAnnotation != null || manyOneAnnotation != null || oneManyAnnotation != null || oneOneAnnotation != null); + + boolean lazyCollection = elemCollectionAnnotation != null && elemCollectionAnnotation.fetch() != FetchType.EAGER; + boolean lazyAssociation = isAssociation() && (fetchType == null || fetchType == FetchType.LAZY); + + lazy = lazyCollection || lazyAssociation; + + idField = field.getAnnotation(EmbeddedId.class) != null || field.getAnnotation(Id.class) != null; + } + + @Override + public boolean isId() { + return idField; + } + + @Override + public MetaAttribute getOppositeAttribute() { + return oppositeAttr; + } + + @Override + public void init(MetaLookup lookup) { + super.init(lookup); + if (mappedBy != null) { + MetaType mappedType = getType(); + if (mappedType.isCollection()) { + mappedType = mappedType.asCollection().getElementType(); + } + MetaEntity type = mappedType.asEntity(); + oppositeAttr = type.getAttribute(mappedBy); + ((AbstractMetaEntityAttributeImpl) oppositeAttr).oppositeAttr = this; + } + } + + @Override + public boolean isLazy() { + return lazy; + } + + @Override + public String getId() { + return getParent() + "." + getName(); + } + + @Override + public boolean isDerived() { + return derived; + } + + @Override + public boolean isVersion() { + return version; + } } \ No newline at end of file diff --git a/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/AbstractMetaEntityImpl.java b/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/AbstractMetaEntityImpl.java index 7c1aebf4..929e36ab 100644 --- a/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/AbstractMetaEntityImpl.java +++ b/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/AbstractMetaEntityImpl.java @@ -1,82 +1,82 @@ -package io.katharsis.jpa.internal.meta.impl; - -import java.beans.PropertyDescriptor; -import java.lang.reflect.Type; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.Set; - -import io.katharsis.jpa.internal.meta.MetaAttribute; -import io.katharsis.jpa.internal.meta.MetaEntity; -import io.katharsis.jpa.internal.meta.MetaKey; -import io.katharsis.jpa.internal.meta.MetaLookup; -import io.katharsis.jpa.internal.meta.MetaType; - -public class AbstractMetaEntityImpl extends MetaDataObjectImpl { - - private static final String PK_NAME = "_primaryKey"; - - private MetaKey primaryKey; - - private Set declaredKeys; - - public AbstractMetaEntityImpl(Class implClass, Type implType, MetaDataObjectImpl superType) { - super(implClass, implType, superType); - } - - @Override - protected MetaEntityAttributeImpl newAttributeAttribute(MetaDataObjectImpl metaDataObject, PropertyDescriptor desc) { - return new MetaEntityAttributeImpl(this, desc); - } - - @Override - public String getName() { - return getImplementationClass().getSimpleName(); - } - - @Override - public MetaKey getPrimaryKey() { - if (getSuperType() instanceof MetaEntity) - return getSuperType().getPrimaryKey(); - return primaryKey; - } - - @Override - public void init(MetaLookup lookup) { - super.init(lookup); - if (declaredKeys == null) { - ArrayList pkElements = new ArrayList<>(); - for (MetaAttribute attr : getAttributes()) { - if (attr.isId()) { - pkElements.add(attr); - } - } - if (!pkElements.isEmpty()) { - MetaType type; - if (pkElements.size() == 1) { - type = pkElements.get(0).getType(); - } - else { - throw new IllegalStateException("not supported"); - } - - primaryKey = new MetaKeyImpl(this, PK_NAME, pkElements, true, true, type); - } - - // TODO parse key annotations - declaredKeys = new HashSet<>(); - } - - } - - @Override - public Set getKeys() { - return declaredKeys; - } - - @Override - public Object fromString(String values) { - throw new UnsupportedOperationException("no yet implemented"); - } - -} +package io.katharsis.jpa.internal.meta.impl; + +import java.beans.PropertyDescriptor; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Set; + +import io.katharsis.jpa.internal.meta.MetaAttribute; +import io.katharsis.jpa.internal.meta.MetaEntity; +import io.katharsis.jpa.internal.meta.MetaKey; +import io.katharsis.jpa.internal.meta.MetaLookup; +import io.katharsis.jpa.internal.meta.MetaType; + +public class AbstractMetaEntityImpl extends MetaDataObjectImpl { + + private static final String PK_NAME = "_primaryKey"; + + private MetaKey primaryKey; + + private Set declaredKeys; + + public AbstractMetaEntityImpl(Class implClass, Type implType, MetaDataObjectImpl superType) { + super(implClass, implType, superType); + } + + @Override + protected MetaEntityAttributeImpl newAttributeAttribute(MetaDataObjectImpl metaDataObject, PropertyDescriptor desc) { + return new MetaEntityAttributeImpl(this, desc); + } + + @Override + public String getName() { + return getImplementationClass().getSimpleName(); + } + + @Override + public MetaKey getPrimaryKey() { + if (getSuperType() instanceof MetaEntity) + return getSuperType().getPrimaryKey(); + return primaryKey; + } + + @Override + public void init(MetaLookup lookup) { + super.init(lookup); + if (declaredKeys == null) { + ArrayList pkElements = new ArrayList<>(); + for (MetaAttribute attr : getAttributes()) { + if (attr.isId()) { + pkElements.add(attr); + } + } + if (!pkElements.isEmpty()) { + MetaType type; + if (pkElements.size() == 1) { + type = pkElements.get(0).getType(); + } + else { + throw new IllegalStateException("not supported"); + } + + primaryKey = new MetaKeyImpl(this, PK_NAME, pkElements, true, true, type); + } + + // TODO parse key annotations + declaredKeys = new HashSet<>(); + } + + } + + @Override + public Set getKeys() { + return declaredKeys; + } + + @Override + public Object fromString(String values) { + throw new UnsupportedOperationException("no yet implemented"); + } + +} diff --git a/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/MetaArrayTypeImpl.java b/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/MetaArrayTypeImpl.java index a7f54895..504fc310 100644 --- a/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/MetaArrayTypeImpl.java +++ b/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/MetaArrayTypeImpl.java @@ -1,26 +1,26 @@ -package io.katharsis.jpa.internal.meta.impl; - -import java.lang.reflect.Type; - -import io.katharsis.jpa.internal.meta.MetaArrayType; -import io.katharsis.jpa.internal.meta.MetaType; - -public class MetaArrayTypeImpl extends MetaTypeImpl implements MetaArrayType { - - private MetaType elementType; - - public MetaArrayTypeImpl(MetaElementImpl parent, Class implClass, Type implType, MetaType elementType) { - super(parent, implClass, implType); - this.elementType = elementType; - } - - @Override - public MetaType getElementType() { - return elementType; - } - - @Override - public Object fromString(String values) { - throw new UnsupportedOperationException("no yet implemented"); - } -} +package io.katharsis.jpa.internal.meta.impl; + +import java.lang.reflect.Type; + +import io.katharsis.jpa.internal.meta.MetaArrayType; +import io.katharsis.jpa.internal.meta.MetaType; + +public class MetaArrayTypeImpl extends MetaTypeImpl implements MetaArrayType { + + private MetaType elementType; + + public MetaArrayTypeImpl(MetaElementImpl parent, Class implClass, Type implType, MetaType elementType) { + super(parent, implClass, implType); + this.elementType = elementType; + } + + @Override + public MetaType getElementType() { + return elementType; + } + + @Override + public Object fromString(String values) { + throw new UnsupportedOperationException("no yet implemented"); + } +} diff --git a/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/MetaAttributeImpl.java b/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/MetaAttributeImpl.java index 7f90ec7e..0458aaa5 100644 --- a/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/MetaAttributeImpl.java +++ b/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/MetaAttributeImpl.java @@ -1,123 +1,127 @@ -package io.katharsis.jpa.internal.meta.impl; - -import java.beans.PropertyDescriptor; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Type; -import java.util.Collection; - -import org.apache.commons.beanutils.BeanUtilsBean; -import org.apache.commons.beanutils.PropertyUtilsBean; - -import io.katharsis.jpa.internal.meta.MetaAttribute; -import io.katharsis.jpa.internal.meta.MetaDataObject; -import io.katharsis.jpa.internal.meta.MetaElement; -import io.katharsis.jpa.internal.meta.MetaType; - -public class MetaAttributeImpl extends MetaElementImpl implements MetaAttribute { - - private String name; - private Type type; - private boolean association; - - public MetaAttributeImpl(MetaElement parent, String name, Type type) { - super(parent); - this.name = name; - this.type = type; - } - - public MetaAttributeImpl(MetaElement parent, PropertyDescriptor desc) { - super(parent); - this.name = desc.getName(); - this.type = desc.getReadMethod().getGenericReturnType(); - } - - @Override - public MetaDataObject getParent() { - return (MetaDataObject) super.getParent(); - } - - @Override - public final boolean isAssociation() { - return association; - } - - public void setAssociation(boolean association) { - this.association = association; - } - - @Override - public MetaType getType() { - return lookup.getMeta(type).asType(); - } - - @Override - public Object getValue(Object dataObject) { - PropertyUtilsBean utils = BeanUtilsBean.getInstance().getPropertyUtils(); - try { - return utils.getNestedProperty(dataObject, getName()); - } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { - throw new IllegalStateException( - "cannot access field " + getName() + " for " + dataObject.getClass().getName(), e); - } - } - - @Override - public void setValue(Object dataObject, Object value) { - PropertyUtilsBean utils = BeanUtilsBean.getInstance().getPropertyUtils(); - try { - utils.setNestedProperty(dataObject, getName(), value); - } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { - throw new IllegalStateException( - "cannot access field " + getName() + " for " + dataObject.getClass().getName(), e); - } - } - - @Override - public String getId() { - return getParent().getId() + "." + getName(); - } - - @Override - public MetaAttribute getOppositeAttribute() { - return null; - } - - @Override - public boolean isDerived() { - return false; - } - - @Override - public boolean isLazy() { - return false; - } - - @Override - public boolean isVersion() { - return false; - } - - @Override - public String getName() { - return name; - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - @Override - public void addValue(Object dataObject, Object value) { - Collection col = (Collection) getValue(dataObject); - col.add(value); - } - - @SuppressWarnings({ "rawtypes" }) - @Override - public void removeValue(Object dataObject, Object value) { - Collection col = (Collection) getValue(dataObject); - col.remove(value); - } - - @Override - public boolean isId() { - return false; - } -} +package io.katharsis.jpa.internal.meta.impl; + +import java.beans.PropertyDescriptor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Type; +import java.util.Collection; + +import org.apache.commons.beanutils.BeanUtilsBean; +import org.apache.commons.beanutils.PropertyUtilsBean; + +import io.katharsis.jpa.internal.meta.MetaAttribute; +import io.katharsis.jpa.internal.meta.MetaDataObject; +import io.katharsis.jpa.internal.meta.MetaElement; +import io.katharsis.jpa.internal.meta.MetaType; + +public class MetaAttributeImpl extends MetaElementImpl implements MetaAttribute { + + private String name; + private Type type; + private boolean association; + + public MetaAttributeImpl(MetaElement parent, String name, Type type) { + super(parent); + this.name = name; + this.type = type; + } + + public MetaAttributeImpl(MetaElement parent, PropertyDescriptor desc) { + super(parent); + this.name = desc.getName(); + this.type = desc.getReadMethod().getGenericReturnType(); + } + + @Override + public MetaDataObject getParent() { + return (MetaDataObject) super.getParent(); + } + + @Override + public final boolean isAssociation() { + return association; + } + + public void setAssociation(boolean association) { + this.association = association; + } + + @Override + public MetaType getType() { + if(lookup == null){ + throw new IllegalStateException(); + } + MetaElement meta = lookup.getMeta(type); + return meta.asType(); + } + + @Override + public Object getValue(Object dataObject) { + PropertyUtilsBean utils = BeanUtilsBean.getInstance().getPropertyUtils(); + try { + return utils.getNestedProperty(dataObject, getName()); + } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { + throw new IllegalStateException( + "cannot access field " + getName() + " for " + dataObject.getClass().getName(), e); + } + } + + @Override + public void setValue(Object dataObject, Object value) { + PropertyUtilsBean utils = BeanUtilsBean.getInstance().getPropertyUtils(); + try { + utils.setNestedProperty(dataObject, getName(), value); + } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { + throw new IllegalStateException( + "cannot access field " + getName() + " for " + dataObject.getClass().getName(), e); + } + } + + @Override + public String getId() { + return getParent().getId() + "." + getName(); + } + + @Override + public MetaAttribute getOppositeAttribute() { + return null; + } + + @Override + public boolean isDerived() { + return false; + } + + @Override + public boolean isLazy() { + return false; + } + + @Override + public boolean isVersion() { + return false; + } + + @Override + public String getName() { + return name; + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + @Override + public void addValue(Object dataObject, Object value) { + Collection col = (Collection) getValue(dataObject); + col.add(value); + } + + @SuppressWarnings({ "rawtypes" }) + @Override + public void removeValue(Object dataObject, Object value) { + Collection col = (Collection) getValue(dataObject); + col.remove(value); + } + + @Override + public boolean isId() { + return false; + } +} diff --git a/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/MetaCollectionTypeImpl.java b/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/MetaCollectionTypeImpl.java index dcb66d42..39c89984 100644 --- a/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/MetaCollectionTypeImpl.java +++ b/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/MetaCollectionTypeImpl.java @@ -1,41 +1,41 @@ -package io.katharsis.jpa.internal.meta.impl; - -import java.lang.reflect.Type; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import io.katharsis.jpa.internal.meta.MetaCollectionType; -import io.katharsis.jpa.internal.meta.MetaType; - -public abstract class MetaCollectionTypeImpl extends MetaTypeImpl implements MetaCollectionType { - - private MetaType elementType; - - public MetaCollectionTypeImpl(MetaElementImpl parent, Class implClass, Type implType, MetaType elementType) { - super(parent, implClass, implType); - this.elementType = elementType; - } - - @Override - public MetaType getElementType() { - return elementType; - } - - @Override - public Object fromString(String values) { - throw new UnsupportedOperationException("no yet implemented"); - } - - @Override - public Collection newInstance() { - if (getImplementationClass() == Set.class) - return new HashSet<>(); - if (getImplementationClass() == List.class) - return new ArrayList<>(); - throw new UnsupportedOperationException(getImplementationClass().getName()); - } - -} +package io.katharsis.jpa.internal.meta.impl; + +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import io.katharsis.jpa.internal.meta.MetaCollectionType; +import io.katharsis.jpa.internal.meta.MetaType; + +public abstract class MetaCollectionTypeImpl extends MetaTypeImpl implements MetaCollectionType { + + private MetaType elementType; + + public MetaCollectionTypeImpl(MetaElementImpl parent, Class implClass, Type implType, MetaType elementType) { + super(parent, implClass, implType); + this.elementType = elementType; + } + + @Override + public MetaType getElementType() { + return elementType; + } + + @Override + public Object fromString(String values) { + throw new UnsupportedOperationException("no yet implemented"); + } + + @Override + public Collection newInstance() { + if (getImplementationClass() == Set.class) + return new HashSet<>(); + if (getImplementationClass() == List.class) + return new ArrayList<>(); + throw new UnsupportedOperationException(getImplementationClass().getName()); + } + +} diff --git a/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/MetaDataObjectImpl.java b/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/MetaDataObjectImpl.java index b1345893..d93302c1 100644 --- a/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/MetaDataObjectImpl.java +++ b/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/MetaDataObjectImpl.java @@ -1,378 +1,378 @@ -package io.katharsis.jpa.internal.meta.impl; - -import java.beans.PropertyDescriptor; -import java.lang.reflect.Modifier; -import java.lang.reflect.Type; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.Collections; -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; - -import org.apache.commons.beanutils.BeanUtilsBean; -import org.apache.commons.beanutils.PropertyUtilsBean; - -import io.katharsis.jpa.internal.meta.MetaAttribute; -import io.katharsis.jpa.internal.meta.MetaAttributeFinder; -import io.katharsis.jpa.internal.meta.MetaAttributePath; -import io.katharsis.jpa.internal.meta.MetaAttributeProjection; -import io.katharsis.jpa.internal.meta.MetaDataObject; -import io.katharsis.jpa.internal.meta.MetaKey; -import io.katharsis.jpa.internal.meta.MetaMapAttribute; -import io.katharsis.jpa.internal.meta.MetaMapType; -import io.katharsis.jpa.internal.meta.MetaProjection; -import io.katharsis.jpa.internal.meta.MetaType; -import io.katharsis.jpa.internal.util.KatharsisAssert; - -public class MetaDataObjectImpl extends MetaTypeImpl implements MetaDataObject { - - private static final MetaAttributeFinder DEFAULT_ATTRIBUTE_FINDER = new MetaAttributeFinder() { - @Override - public MetaAttribute getAttribute(MetaDataObject meta, String name) { - return meta.getAttribute(name); - } - }; - - private static final MetaAttributeFinder SUBTYPE_ATTRIBUTE_FINDER = new MetaAttributeFinder() { - @Override - public MetaAttribute getAttribute(MetaDataObject meta, String name) { - return meta.findAttribute(name, true); - } - }; - - private ArrayList subTypes = new ArrayList<>(); - private MetaDataObject superType; - - private HashMap attrMap = new HashMap<>(); - private List attrs = new ArrayList<>(); - private List declaredAttrs = new ArrayList<>(); - - @SuppressWarnings("unchecked") - private List[] subTypesCache = new List[4]; - private HashMap subTypesMapCache; - - private MetaKey primaryKey; - private Set keys = new HashSet<>(); - - public MetaDataObjectImpl(Class implClass, Type implType, MetaDataObjectImpl superType) { - super(null, implClass, implType); - this.superType = superType; - if (superType != null) { - attrs.addAll(superType.getAttributes()); - attrMap.putAll(superType.attrMap); - - superType.addSubType(this); - } - - initAttributes(); - } - - protected void initAttributes() { - Class implClass = this.getImplementationClass(); - PropertyUtilsBean utils = BeanUtilsBean.getInstance().getPropertyUtils(); - PropertyDescriptor[] descriptors = utils.getPropertyDescriptors(implClass); - for (PropertyDescriptor desc : descriptors) { - if (desc.getReadMethod().getDeclaringClass() != implClass) - continue; // contained in super type - if (attrMap.containsKey(desc.getName())) - throw new IllegalStateException(desc.toString()); - - MetaAttributeImpl attr = newAttributeAttribute(this, desc); - addAttribute(attr); - } - } - - protected void addAttribute(MetaAttributeImpl attr) { - if (attrMap.containsKey(attr.getName())) - throw new IllegalStateException(attr.toString()); - - attrMap.put(attr.getName(), attr); - attrs.add(attr); - declaredAttrs.add(attr); - } - - @Override - public MetaAttribute getVersionAttribute() { - for (MetaAttribute attr : getAttributes()) { - if (attr.isVersion()) - return attr; - } - return null; - } - - private void addSubType(MetaDataObjectImpl subType) { - this.subTypes.add(subType); - this.clearCache(); - } - - @SuppressWarnings("unchecked") - private void clearCache() { - subTypesMapCache = null; - subTypesCache = new List[4]; - } - - @Override - public List getAttributes() { - return attrs; - } - - @Override - public List getDeclaredAttributes() { - return declaredAttrs; - } - - @Override - public MetaAttribute getAttribute(String name) { - MetaAttributeImpl attr = attrMap.get(name); - KatharsisAssert.assertNotNull(getName() + "." + name, attr); - return attr; - } - - @Override - public String toString(Object entity) { - boolean notFirst = false; - StringBuilder b = new StringBuilder(entity.getClass().getSimpleName()); - b.append('['); - - for (MetaAttribute attr : getAttributes()) { - if (attr.isAssociation()) { - continue; - } - - Object value = attr.getValue(entity); - - if (notFirst) { - b.append(','); - } else { - notFirst = true; - } - b.append(attr.getName()); - b.append("="); - b.append(formatValue(value)); - } - b.append(']'); - return b.toString(); - } - - private Object formatValue(Object value) { - if (value == null) { - return "null"; - } else if (value instanceof Calendar) { - Calendar cal = (Calendar) value; - SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss Z"); - return sdf.format(cal.getTime()); - } else if (value instanceof Date) { - Date cal = (Date) value; - SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss Z"); - return sdf.format(cal.getTime()); - } else { - return value.toString(); - } - } - - protected MetaAttributeImpl newAttributeAttribute(MetaDataObjectImpl metaDataObject, PropertyDescriptor desc) { - return new MetaAttributeImpl(metaDataObject, desc); - } - - @Override - public MetaAttributePath resolvePath(List attrPath, boolean includeSubTypes) { - MetaAttributeFinder finder = includeSubTypes ? SUBTYPE_ATTRIBUTE_FINDER : DEFAULT_ATTRIBUTE_FINDER; - return resolvePath(attrPath, finder); - } - - @Override - public MetaAttributePath resolvePath(List attrPath) { - return resolvePath(attrPath, true); - } - - @Override - public MetaAttributePath resolvePath(List attrPath, MetaAttributeFinder finder) { - if (attrPath == null || attrPath.isEmpty()) - throw new IllegalArgumentException("invalid attribute path '" + attrPath + "'"); - LinkedList list = new LinkedList<>(); - - MetaDataObject currentMdo = this; - int i = 0; - while (i < attrPath.size()) { - String pathElementName = attrPath.get(i); - MetaAttribute pathElement = finder.getAttribute(currentMdo, pathElementName); - if (i < attrPath.size() - 1 && pathElement.getType() instanceof MetaMapType) { - MetaMapType mapType = (MetaMapType) pathElement.getType(); - - // next "attribute" is the key within the map - String keyString = attrPath.get(i + 1); - - MetaMapAttribute keyAttr; - if (pathElement instanceof MetaAttributeProjection) { - keyAttr = new MetaMapAttributeProjectionImpl(mapType, pathElement, keyString, true); - } else { - keyAttr = new MetaMapAttributeImpl(mapType, pathElement, keyString, true); - } - list.add(keyAttr); - i++; - MetaType valueType = mapType.getValueType(); - currentMdo = nextPathElement(valueType, i, attrPath); - } else { - list.add(pathElement); - currentMdo = nextPathElement(pathElement.getType(), i, attrPath); - } - i++; - } - - return new MetaAttributePath(list); - } - - private MetaDataObject nextPathElement(MetaType pathElementType, int i, List pathElements) { - if (i == pathElements.size() - 1) { - return null; - } else { - if (!(pathElementType instanceof MetaDataObject)) { - throw new IllegalArgumentException("failed to resolve path " + pathElements); - } - return pathElementType.asDataObject(); - } - } - - @Override - public MetaAttribute findAttribute(String name, boolean includeSubTypes) { - if (hasAttribute(name)) { - return getAttribute(name); - } - - if (includeSubTypes) { - List transitiveSubTypes = getSubTypes(true, true); - for (MetaDataObject subType : transitiveSubTypes) { - if (subType.hasAttribute(name)) { - return subType.getAttribute(name); - } - } - } - - throw new IllegalStateException("attribute " + name + " not found in " + getName()); - } - - @Override - public boolean hasAttribute(String name) { - return attrMap.containsKey(name); - } - - @Override - public MetaDataObject getSuperType() { - return superType; - } - - @Override - public MetaDataObject getRootType() { - if (getSuperType() != null) // ensure proxy resolve - return superType.getRootType(); - else - return this; - } - - @Override - public List getSubTypes(boolean transitive, boolean self) { - int cacheIndex = (transitive ? 2 : 0) | (self ? 1 : 0); - - List cached = subTypesCache[cacheIndex]; - if (cached != null) { - return cached; - } else { - ArrayList types = computeSubTypes(transitive, self); - List unmodifiableList = Collections.unmodifiableList(types); - subTypesCache[cacheIndex] = unmodifiableList; - return unmodifiableList; - } - } - - private ArrayList computeSubTypes(boolean transitive, boolean self) { - ArrayList types = new ArrayList<>(); - - if (self && (!isAbstract() || !subTypes.isEmpty())) - types.add(this); - - for (MetaDataObject subType : subTypes) { - if (!subType.isAbstract() || !subType.getSubTypes().isEmpty()) - types.add(subType); - if (transitive) { - types.addAll(subType.getSubTypes(true, false)); - } - } - return types; - } - - @Override - public boolean isAbstract() { - return Modifier.isAbstract(getImplementationClass().getModifiers()); - } - - @Override - public List getSubTypes() { - return subTypes; - } - - private boolean isSuperTypeOf(MetaDataObject sub) { - return this == sub || (sub != null && isSuperTypeOf(sub.getSuperType())); - } - - @Override - public MetaDataObject findSubTypeOrSelf(Class implClass) { - if (implClass == null) - throw new NullPointerException("class is null"); - Class localImplClass = getImplementationClass(); - if (implClass == localImplClass) - return this; - MetaDataObject subType = lookup.getMeta(implClass).asDataObject(); - if (isSuperTypeOf(subType)) - return subType; - return null; - } - - /** - * Gets the subtype with the given name (simple or qualitified). - */ - public MetaDataObject findSubTypeOrSelf(String name) { - HashMap cache = subTypesMapCache; - if (cache == null) { - cache = new HashMap<>(); - List transitiveSubTypes = getSubTypes(true, true); - for (MetaDataObject subType : transitiveSubTypes) { - cache.put(subType.getName(), subType); - cache.put(subType.getId(), subType); - } - subTypesMapCache = cache; - } - return subTypesMapCache.get(name); - } - - @Override - public MetaProjection asProjection() { - if (this instanceof MetaProjection) - return (MetaProjection) this; - throw new IllegalStateException("not a projection"); - } - - @Override - public MetaKey getPrimaryKey() { - return primaryKey; - } - - @Override - public Set getKeys() { - return keys; - } - - public void setPrimaryKey(MetaKey key) { - this.primaryKey = key; - addKey(key); - } - - public void addKey(MetaKey key) { - keys.add(key); - } - +package io.katharsis.jpa.internal.meta.impl; + +import java.beans.PropertyDescriptor; +import java.lang.reflect.Modifier; +import java.lang.reflect.Type; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +import org.apache.commons.beanutils.BeanUtilsBean; +import org.apache.commons.beanutils.PropertyUtilsBean; + +import io.katharsis.jpa.internal.meta.MetaAttribute; +import io.katharsis.jpa.internal.meta.MetaAttributeFinder; +import io.katharsis.jpa.internal.meta.MetaAttributePath; +import io.katharsis.jpa.internal.meta.MetaAttributeProjection; +import io.katharsis.jpa.internal.meta.MetaDataObject; +import io.katharsis.jpa.internal.meta.MetaKey; +import io.katharsis.jpa.internal.meta.MetaMapAttribute; +import io.katharsis.jpa.internal.meta.MetaMapType; +import io.katharsis.jpa.internal.meta.MetaProjection; +import io.katharsis.jpa.internal.meta.MetaType; +import io.katharsis.jpa.internal.util.KatharsisAssert; + +public class MetaDataObjectImpl extends MetaTypeImpl implements MetaDataObject { + + private static final MetaAttributeFinder DEFAULT_ATTRIBUTE_FINDER = new MetaAttributeFinder() { + @Override + public MetaAttribute getAttribute(MetaDataObject meta, String name) { + return meta.getAttribute(name); + } + }; + + private static final MetaAttributeFinder SUBTYPE_ATTRIBUTE_FINDER = new MetaAttributeFinder() { + @Override + public MetaAttribute getAttribute(MetaDataObject meta, String name) { + return meta.findAttribute(name, true); + } + }; + + private ArrayList subTypes = new ArrayList<>(); + private MetaDataObject superType; + + private HashMap attrMap = new HashMap<>(); + private List attrs = new ArrayList<>(); + private List declaredAttrs = new ArrayList<>(); + + @SuppressWarnings("unchecked") + private List[] subTypesCache = new List[4]; + private HashMap subTypesMapCache; + + private MetaKey primaryKey; + private Set keys = new HashSet<>(); + + public MetaDataObjectImpl(Class implClass, Type implType, MetaDataObjectImpl superType) { + super(null, implClass, implType); + this.superType = superType; + if (superType != null) { + attrs.addAll(superType.getAttributes()); + attrMap.putAll(superType.attrMap); + + superType.addSubType(this); + } + + initAttributes(); + } + + protected void initAttributes() { + Class implClass = this.getImplementationClass(); + PropertyUtilsBean utils = BeanUtilsBean.getInstance().getPropertyUtils(); + PropertyDescriptor[] descriptors = utils.getPropertyDescriptors(implClass); + for (PropertyDescriptor desc : descriptors) { + if (desc.getReadMethod().getDeclaringClass() != implClass) + continue; // contained in super type + if (attrMap.containsKey(desc.getName())) + throw new IllegalStateException(desc.toString()); + + MetaAttributeImpl attr = newAttributeAttribute(this, desc); + addAttribute(attr); + } + } + + protected void addAttribute(MetaAttributeImpl attr) { + if (attrMap.containsKey(attr.getName())) + throw new IllegalStateException(attr.toString()); + + attrMap.put(attr.getName(), attr); + attrs.add(attr); + declaredAttrs.add(attr); + } + + @Override + public MetaAttribute getVersionAttribute() { + for (MetaAttribute attr : getAttributes()) { + if (attr.isVersion()) + return attr; + } + return null; + } + + private void addSubType(MetaDataObjectImpl subType) { + this.subTypes.add(subType); + this.clearCache(); + } + + @SuppressWarnings("unchecked") + private void clearCache() { + subTypesMapCache = null; + subTypesCache = new List[4]; + } + + @Override + public List getAttributes() { + return attrs; + } + + @Override + public List getDeclaredAttributes() { + return declaredAttrs; + } + + @Override + public MetaAttribute getAttribute(String name) { + MetaAttributeImpl attr = attrMap.get(name); + KatharsisAssert.assertNotNull(getName() + "." + name, attr); + return attr; + } + + @Override + public String toString(Object entity) { + boolean notFirst = false; + StringBuilder b = new StringBuilder(entity.getClass().getSimpleName()); + b.append('['); + + for (MetaAttribute attr : getAttributes()) { + if (attr.isAssociation()) { + continue; + } + + Object value = attr.getValue(entity); + + if (notFirst) { + b.append(','); + } else { + notFirst = true; + } + b.append(attr.getName()); + b.append("="); + b.append(formatValue(value)); + } + b.append(']'); + return b.toString(); + } + + private Object formatValue(Object value) { + if (value == null) { + return "null"; + } else if (value instanceof Calendar) { + Calendar cal = (Calendar) value; + SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss Z"); + return sdf.format(cal.getTime()); + } else if (value instanceof Date) { + Date cal = (Date) value; + SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss Z"); + return sdf.format(cal.getTime()); + } else { + return value.toString(); + } + } + + protected MetaAttributeImpl newAttributeAttribute(MetaDataObjectImpl metaDataObject, PropertyDescriptor desc) { + return new MetaAttributeImpl(metaDataObject, desc); + } + + @Override + public MetaAttributePath resolvePath(List attrPath, boolean includeSubTypes) { + MetaAttributeFinder finder = includeSubTypes ? SUBTYPE_ATTRIBUTE_FINDER : DEFAULT_ATTRIBUTE_FINDER; + return resolvePath(attrPath, finder); + } + + @Override + public MetaAttributePath resolvePath(List attrPath) { + return resolvePath(attrPath, true); + } + + @Override + public MetaAttributePath resolvePath(List attrPath, MetaAttributeFinder finder) { + if (attrPath == null || attrPath.isEmpty()) + throw new IllegalArgumentException("invalid attribute path '" + attrPath + "'"); + LinkedList list = new LinkedList<>(); + + MetaDataObject currentMdo = this; + int i = 0; + while (i < attrPath.size()) { + String pathElementName = attrPath.get(i); + MetaAttribute pathElement = finder.getAttribute(currentMdo, pathElementName); + if (i < attrPath.size() - 1 && pathElement.getType() instanceof MetaMapType) { + MetaMapType mapType = (MetaMapType) pathElement.getType(); + + // next "attribute" is the key within the map + String keyString = attrPath.get(i + 1); + + MetaMapAttribute keyAttr; + if (pathElement instanceof MetaAttributeProjection) { + keyAttr = new MetaMapAttributeProjectionImpl(mapType, pathElement, keyString, true); + } else { + keyAttr = new MetaMapAttributeImpl(mapType, pathElement, keyString, true); + } + list.add(keyAttr); + i++; + MetaType valueType = mapType.getValueType(); + currentMdo = nextPathElement(valueType, i, attrPath); + } else { + list.add(pathElement); + currentMdo = nextPathElement(pathElement.getType(), i, attrPath); + } + i++; + } + + return new MetaAttributePath(list); + } + + private MetaDataObject nextPathElement(MetaType pathElementType, int i, List pathElements) { + if (i == pathElements.size() - 1) { + return null; + } else { + if (!(pathElementType instanceof MetaDataObject)) { + throw new IllegalArgumentException("failed to resolve path " + pathElements); + } + return pathElementType.asDataObject(); + } + } + + @Override + public MetaAttribute findAttribute(String name, boolean includeSubTypes) { + if (hasAttribute(name)) { + return getAttribute(name); + } + + if (includeSubTypes) { + List transitiveSubTypes = getSubTypes(true, true); + for (MetaDataObject subType : transitiveSubTypes) { + if (subType.hasAttribute(name)) { + return subType.getAttribute(name); + } + } + } + + throw new IllegalStateException("attribute " + name + " not found in " + getName()); + } + + @Override + public boolean hasAttribute(String name) { + return attrMap.containsKey(name); + } + + @Override + public MetaDataObject getSuperType() { + return superType; + } + + @Override + public MetaDataObject getRootType() { + if (getSuperType() != null) // ensure proxy resolve + return superType.getRootType(); + else + return this; + } + + @Override + public List getSubTypes(boolean transitive, boolean self) { + int cacheIndex = (transitive ? 2 : 0) | (self ? 1 : 0); + + List cached = subTypesCache[cacheIndex]; + if (cached != null) { + return cached; + } else { + ArrayList types = computeSubTypes(transitive, self); + List unmodifiableList = Collections.unmodifiableList(types); + subTypesCache[cacheIndex] = unmodifiableList; + return unmodifiableList; + } + } + + private ArrayList computeSubTypes(boolean transitive, boolean self) { + ArrayList types = new ArrayList<>(); + + if (self && (!isAbstract() || !subTypes.isEmpty())) + types.add(this); + + for (MetaDataObject subType : subTypes) { + if (!subType.isAbstract() || !subType.getSubTypes().isEmpty()) + types.add(subType); + if (transitive) { + types.addAll(subType.getSubTypes(true, false)); + } + } + return types; + } + + @Override + public boolean isAbstract() { + return Modifier.isAbstract(getImplementationClass().getModifiers()); + } + + @Override + public List getSubTypes() { + return subTypes; + } + + private boolean isSuperTypeOf(MetaDataObject sub) { + return this == sub || (sub != null && isSuperTypeOf(sub.getSuperType())); + } + + @Override + public MetaDataObject findSubTypeOrSelf(Class implClass) { + if (implClass == null) + throw new NullPointerException("class is null"); + Class localImplClass = getImplementationClass(); + if (implClass == localImplClass) + return this; + MetaDataObject subType = lookup.getMeta(implClass).asDataObject(); + if (isSuperTypeOf(subType)) + return subType; + return null; + } + + /** + * Gets the subtype with the given name (simple or qualitified). + */ + public MetaDataObject findSubTypeOrSelf(String name) { + HashMap cache = subTypesMapCache; + if (cache == null) { + cache = new HashMap<>(); + List transitiveSubTypes = getSubTypes(true, true); + for (MetaDataObject subType : transitiveSubTypes) { + cache.put(subType.getName(), subType); + cache.put(subType.getId(), subType); + } + subTypesMapCache = cache; + } + return subTypesMapCache.get(name); + } + + @Override + public MetaProjection asProjection() { + if (this instanceof MetaProjection) + return (MetaProjection) this; + throw new IllegalStateException("not a projection"); + } + + @Override + public MetaKey getPrimaryKey() { + return primaryKey; + } + + @Override + public Set getKeys() { + return keys; + } + + public void setPrimaryKey(MetaKey key) { + this.primaryKey = key; + addKey(key); + } + + public void addKey(MetaKey key) { + keys.add(key); + } + } \ No newline at end of file diff --git a/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/MetaElementImpl.java b/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/MetaElementImpl.java index 1e613f03..7ba7f39d 100644 --- a/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/MetaElementImpl.java +++ b/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/MetaElementImpl.java @@ -1,74 +1,74 @@ -package io.katharsis.jpa.internal.meta.impl; - -import java.util.ArrayList; -import java.util.List; - -import io.katharsis.jpa.internal.meta.MetaDataObject; -import io.katharsis.jpa.internal.meta.MetaElement; -import io.katharsis.jpa.internal.meta.MetaEntity; -import io.katharsis.jpa.internal.meta.MetaLookup; -import io.katharsis.jpa.internal.meta.MetaType; - -public abstract class MetaElementImpl implements MetaElement { - - private MetaElement parent; - private List children = new ArrayList<>(); - - protected MetaLookup lookup; - - public MetaElementImpl(MetaElement parent) { - this.parent = parent; - if (parent != null) { - List parentsChildren = parent.getChildren(); - parentsChildren.add(this); - } - } - - @Override - public void init(MetaLookup lookup) { - this.lookup = lookup; - for (MetaElement child : children) { - ((MetaElementImpl) child).init(lookup); - } - } - - @Override - public MetaElement getParent() { - return parent; - } - - @Override - public List getChildren() { - return children; - } - - @Override - public MetaEntity asEntity() { - if (!(this instanceof MetaEntity)) - throw new IllegalStateException(getName() + " not a MetaEntity"); - return (MetaEntity) this; - } - - @Override - public MetaType asType() { - if (!(this instanceof MetaType)) - throw new IllegalStateException(getName() + " not a MetaEntity"); - return (MetaType) this; - } - - @Override - public MetaDataObject asDataObject() { - if (!(this instanceof MetaDataObject)) - throw new IllegalStateException(getName() + " not a MetaDataObject"); - return (MetaDataObject) this; - } - - public void setParent(MetaElementImpl parent) { - this.parent = parent; - } - - @Override - public String toString() { - return getClass().getSimpleName() + "[name=" + getName() + "]"; - } -} +package io.katharsis.jpa.internal.meta.impl; + +import java.util.ArrayList; +import java.util.List; + +import io.katharsis.jpa.internal.meta.MetaDataObject; +import io.katharsis.jpa.internal.meta.MetaElement; +import io.katharsis.jpa.internal.meta.MetaEntity; +import io.katharsis.jpa.internal.meta.MetaLookup; +import io.katharsis.jpa.internal.meta.MetaType; + +public abstract class MetaElementImpl implements MetaElement { + + private MetaElement parent; + private List children = new ArrayList<>(); + + protected MetaLookup lookup; + + public MetaElementImpl(MetaElement parent) { + this.parent = parent; + if (parent != null) { + List parentsChildren = parent.getChildren(); + parentsChildren.add(this); + } + } + + @Override + public void init(MetaLookup lookup) { + this.lookup = lookup; + for (MetaElement child : children) { + ((MetaElementImpl) child).init(lookup); + } + } + + @Override + public MetaElement getParent() { + return parent; + } + + @Override + public List getChildren() { + return children; + } + + @Override + public MetaEntity asEntity() { + if (!(this instanceof MetaEntity)) + throw new IllegalStateException(getName() + " not a MetaEntity"); + return (MetaEntity) this; + } + + @Override + public MetaType asType() { + if (!(this instanceof MetaType)) + throw new IllegalStateException(getName() + " not a MetaEntity"); + return (MetaType) this; + } + + @Override + public MetaDataObject asDataObject() { + if (!(this instanceof MetaDataObject)) + throw new IllegalStateException(getName() + " not a MetaDataObject"); + return (MetaDataObject) this; + } + + public void setParent(MetaElementImpl parent) { + this.parent = parent; + } + + @Override + public String toString() { + return getClass().getSimpleName() + "[name=" + getName() + "]"; + } +} diff --git a/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/MetaEmbeddableAttributeImpl.java b/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/MetaEmbeddableAttributeImpl.java index b070cd4d..bf52e5e3 100644 --- a/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/MetaEmbeddableAttributeImpl.java +++ b/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/MetaEmbeddableAttributeImpl.java @@ -1,20 +1,20 @@ -package io.katharsis.jpa.internal.meta.impl; - -import java.beans.PropertyDescriptor; - -import io.katharsis.jpa.query.AnyTypeObject; - -public class MetaEmbeddableAttributeImpl extends AbstractMetaEntityAttributeImpl { - - private static final Object VALUE_ANYTYPE_ATTR_NAME = "value"; - - public MetaEmbeddableAttributeImpl(MetaEmbeddableImpl parent, PropertyDescriptor desc) { - super(parent, desc); - } - - @Override - public boolean isDerived() { - return super.isDerived() || AnyTypeObject.class.isAssignableFrom(getParent().getImplementationClass()) - && getName().equals(VALUE_ANYTYPE_ATTR_NAME); - } +package io.katharsis.jpa.internal.meta.impl; + +import java.beans.PropertyDescriptor; + +import io.katharsis.jpa.query.AnyTypeObject; + +public class MetaEmbeddableAttributeImpl extends AbstractMetaEntityAttributeImpl { + + private static final Object VALUE_ANYTYPE_ATTR_NAME = "value"; + + public MetaEmbeddableAttributeImpl(MetaEmbeddableImpl parent, PropertyDescriptor desc) { + super(parent, desc); + } + + @Override + public boolean isDerived() { + return super.isDerived() || AnyTypeObject.class.isAssignableFrom(getParent().getImplementationClass()) + && getName().equals(VALUE_ANYTYPE_ATTR_NAME); + } } \ No newline at end of file diff --git a/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/MetaEmbeddableImpl.java b/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/MetaEmbeddableImpl.java index 064e591f..ea7048a2 100644 --- a/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/MetaEmbeddableImpl.java +++ b/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/MetaEmbeddableImpl.java @@ -1,42 +1,42 @@ -package io.katharsis.jpa.internal.meta.impl; - -import java.beans.PropertyDescriptor; -import java.lang.reflect.Type; -import java.util.Set; - -import io.katharsis.jpa.internal.meta.MetaEmbeddable; -import io.katharsis.jpa.internal.meta.MetaKey; -import io.katharsis.jpa.internal.meta.MetaProjection; - -public class MetaEmbeddableImpl extends MetaDataObjectImpl implements MetaEmbeddable { - - public MetaEmbeddableImpl(Class implClass, Type implType, MetaDataObjectImpl superType) { - super(implClass, implType, superType); - } - - @Override - protected MetaEmbeddableAttributeImpl newAttributeAttribute(MetaDataObjectImpl metaDataObject, - PropertyDescriptor desc) { - return new MetaEmbeddableAttributeImpl(this, desc); - } - - @Override - public MetaKey getPrimaryKey() { - throw new IllegalStateException("not available"); - } - - @Override - public Set getKeys() { - throw new IllegalStateException("not available"); - } - - @Override - public MetaProjection asProjection() { - throw new IllegalStateException("not a projection"); - } - - @Override - public Object fromString(String value) { - throw new UnsupportedOperationException("no yet implemented"); - } -} +package io.katharsis.jpa.internal.meta.impl; + +import java.beans.PropertyDescriptor; +import java.lang.reflect.Type; +import java.util.Set; + +import io.katharsis.jpa.internal.meta.MetaEmbeddable; +import io.katharsis.jpa.internal.meta.MetaKey; +import io.katharsis.jpa.internal.meta.MetaProjection; + +public class MetaEmbeddableImpl extends MetaDataObjectImpl implements MetaEmbeddable { + + public MetaEmbeddableImpl(Class implClass, Type implType, MetaDataObjectImpl superType) { + super(implClass, implType, superType); + } + + @Override + protected MetaEmbeddableAttributeImpl newAttributeAttribute(MetaDataObjectImpl metaDataObject, + PropertyDescriptor desc) { + return new MetaEmbeddableAttributeImpl(this, desc); + } + + @Override + public MetaKey getPrimaryKey() { + throw new IllegalStateException("not available"); + } + + @Override + public Set getKeys() { + throw new IllegalStateException("not available"); + } + + @Override + public MetaProjection asProjection() { + throw new IllegalStateException("not a projection"); + } + + @Override + public Object fromString(String value) { + throw new UnsupportedOperationException("no yet implemented"); + } +} diff --git a/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/MetaEntityAttributeImpl.java b/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/MetaEntityAttributeImpl.java index 95060749..4de41f6e 100644 --- a/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/MetaEntityAttributeImpl.java +++ b/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/MetaEntityAttributeImpl.java @@ -1,11 +1,11 @@ -package io.katharsis.jpa.internal.meta.impl; - -import java.beans.PropertyDescriptor; - -public class MetaEntityAttributeImpl extends AbstractMetaEntityAttributeImpl { - - public MetaEntityAttributeImpl(AbstractMetaEntityImpl parent, PropertyDescriptor desc) { - super(parent, desc); - } - +package io.katharsis.jpa.internal.meta.impl; + +import java.beans.PropertyDescriptor; + +public class MetaEntityAttributeImpl extends AbstractMetaEntityAttributeImpl { + + public MetaEntityAttributeImpl(AbstractMetaEntityImpl parent, PropertyDescriptor desc) { + super(parent, desc); + } + } \ No newline at end of file diff --git a/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/MetaEntityImpl.java b/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/MetaEntityImpl.java index a40a7f54..611d0ca9 100644 --- a/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/MetaEntityImpl.java +++ b/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/MetaEntityImpl.java @@ -1,13 +1,13 @@ -package io.katharsis.jpa.internal.meta.impl; - -import java.lang.reflect.Type; - -import io.katharsis.jpa.internal.meta.MetaEntity; - -public class MetaEntityImpl extends AbstractMetaEntityImpl implements MetaEntity { - - public MetaEntityImpl(Class implClass, Type implType, MetaDataObjectImpl superType) { - super(implClass, implType, superType); - } - +package io.katharsis.jpa.internal.meta.impl; + +import java.lang.reflect.Type; + +import io.katharsis.jpa.internal.meta.MetaEntity; + +public class MetaEntityImpl extends AbstractMetaEntityImpl implements MetaEntity { + + public MetaEntityImpl(Class implClass, Type implType, MetaDataObjectImpl superType) { + super(implClass, implType, superType); + } + } \ No newline at end of file diff --git a/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/MetaKeyImpl.java b/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/MetaKeyImpl.java index fbbe174e..3fe7c159 100644 --- a/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/MetaKeyImpl.java +++ b/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/MetaKeyImpl.java @@ -1,138 +1,138 @@ -package io.katharsis.jpa.internal.meta.impl; - -import java.util.List; - -import io.katharsis.jpa.internal.meta.MetaAttribute; -import io.katharsis.jpa.internal.meta.MetaEmbeddable; -import io.katharsis.jpa.internal.meta.MetaKey; -import io.katharsis.jpa.internal.meta.MetaType; - -public class MetaKeyImpl extends MetaElementImpl implements MetaKey { - - private static final String ID_ELEMENT_SEPARATOR = "-"; - - private String name; - private List elements; - private boolean primaryKey; - private boolean unique; - private MetaType type; - - public MetaKeyImpl(MetaDataObjectImpl parent, String name, List elements, boolean primaryKey, - boolean unique, MetaType type) { - super(parent); - this.name = name; - this.elements = elements; - this.primaryKey = primaryKey; - this.unique = unique; - this.type = type; - } - - @Override - public String getId() { - return getParent().getId() + "." + getName(); - } - - @Override - public List getElements() { - return elements; - } - - @Override - public boolean isUnique() { - return unique; - } - - @Override - public boolean isPrimaryKey() { - return primaryKey; - } - - @Override - public String getName() { - return name; - } - - @Override - public MetaType getType() { - return type; - } - - @Override - public MetaAttribute getUniqueElement() { - if (elements.size() != 1) - throw new IllegalStateException(getName() + " must contain a single primary key attribute"); - return elements.get(0); - } - - @Override - public Object fromKeyString(String idString) { - - // => support compound keys with unique ids - if (elements.size() == 1) { - MetaAttribute keyAttr = elements.get(0); - MetaType keyType = keyAttr.getType(); - - if (keyType instanceof MetaEmbeddable) { - return parseEmbeddableString((MetaEmbeddable) keyType, idString); - } else { - return keyType.fromString(idString); - } - } else { - throw new UnsupportedOperationException(); - } - } - - private Object parseEmbeddableString(MetaEmbeddable embType, String idString) { - String[] keyElements = idString.split(ID_ELEMENT_SEPARATOR); - - Object id; - try { - id = embType.getImplementationClass().newInstance(); - } catch (Exception e) { - throw new IllegalStateException(e); - } - - List embAttrs = embType.getAttributes(); - if (keyElements.length != embAttrs.size()) - throw new UnsupportedOperationException( - "failed to parse " + idString + ", expected " + elements.size() + " elements"); - for (int i = 0; i < keyElements.length; i++) { - MetaAttribute embAttr = embAttrs.get(i); - Object idElement = embAttr.getType().fromString(keyElements[i]); - embAttr.setValue(id, idElement); - } - return id; - } - - @Override - public String toKeyString(Object id) { - // => support compound keys with unique ids - if (elements.size() == 1) { - MetaAttribute keyAttr = elements.get(0); - MetaType type = keyAttr.getType(); - if (type instanceof MetaEmbeddable) { - MetaEmbeddable embType = (MetaEmbeddable) type; - return toEmbeddableKeyString(embType, id); - } else { - return id.toString(); - } - } else { - throw new UnsupportedOperationException(); - } - } - - private static String toEmbeddableKeyString(MetaEmbeddable embType, Object id) { - StringBuilder builder = new StringBuilder(); - List embAttrs = embType.getAttributes(); - for (int i = 0; i < embAttrs.size(); i++) { - MetaAttribute embAttr = embAttrs.get(i); - Object idElement = embAttr.getValue(id); - if (i > 0) { - builder.append(ID_ELEMENT_SEPARATOR); - } - builder.append(idElement); - } - return builder.toString(); - } - -} +package io.katharsis.jpa.internal.meta.impl; + +import java.util.List; + +import io.katharsis.jpa.internal.meta.MetaAttribute; +import io.katharsis.jpa.internal.meta.MetaEmbeddable; +import io.katharsis.jpa.internal.meta.MetaKey; +import io.katharsis.jpa.internal.meta.MetaType; + +public class MetaKeyImpl extends MetaElementImpl implements MetaKey { + + private static final String ID_ELEMENT_SEPARATOR = "-"; + + private String name; + private List elements; + private boolean primaryKey; + private boolean unique; + private MetaType type; + + public MetaKeyImpl(MetaDataObjectImpl parent, String name, List elements, boolean primaryKey, + boolean unique, MetaType type) { + super(parent); + this.name = name; + this.elements = elements; + this.primaryKey = primaryKey; + this.unique = unique; + this.type = type; + } + + @Override + public String getId() { + return getParent().getId() + "." + getName(); + } + + @Override + public List getElements() { + return elements; + } + + @Override + public boolean isUnique() { + return unique; + } + + @Override + public boolean isPrimaryKey() { + return primaryKey; + } + + @Override + public String getName() { + return name; + } + + @Override + public MetaType getType() { + return type; + } + + @Override + public MetaAttribute getUniqueElement() { + if (elements.size() != 1) + throw new IllegalStateException(getName() + " must contain a single primary key attribute"); + return elements.get(0); + } + + @Override + public Object fromKeyString(String idString) { + + // => support compound keys with unique ids + if (elements.size() == 1) { + MetaAttribute keyAttr = elements.get(0); + MetaType keyType = keyAttr.getType(); + + if (keyType instanceof MetaEmbeddable) { + return parseEmbeddableString((MetaEmbeddable) keyType, idString); + } else { + return keyType.fromString(idString); + } + } else { + throw new UnsupportedOperationException(); + } + } + + private Object parseEmbeddableString(MetaEmbeddable embType, String idString) { + String[] keyElements = idString.split(ID_ELEMENT_SEPARATOR); + + Object id; + try { + id = embType.getImplementationClass().newInstance(); + } catch (Exception e) { + throw new IllegalStateException(e); + } + + List embAttrs = embType.getAttributes(); + if (keyElements.length != embAttrs.size()) + throw new UnsupportedOperationException( + "failed to parse " + idString + ", expected " + elements.size() + " elements"); + for (int i = 0; i < keyElements.length; i++) { + MetaAttribute embAttr = embAttrs.get(i); + Object idElement = embAttr.getType().fromString(keyElements[i]); + embAttr.setValue(id, idElement); + } + return id; + } + + @Override + public String toKeyString(Object id) { + // => support compound keys with unique ids + if (elements.size() == 1) { + MetaAttribute keyAttr = elements.get(0); + MetaType type = keyAttr.getType(); + if (type instanceof MetaEmbeddable) { + MetaEmbeddable embType = (MetaEmbeddable) type; + return toEmbeddableKeyString(embType, id); + } else { + return id.toString(); + } + } else { + throw new UnsupportedOperationException(); + } + } + + private static String toEmbeddableKeyString(MetaEmbeddable embType, Object id) { + StringBuilder builder = new StringBuilder(); + List embAttrs = embType.getAttributes(); + for (int i = 0; i < embAttrs.size(); i++) { + MetaAttribute embAttr = embAttrs.get(i); + Object idElement = embAttr.getValue(id); + if (i > 0) { + builder.append(ID_ELEMENT_SEPARATOR); + } + builder.append(idElement); + } + return builder.toString(); + } + +} diff --git a/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/MetaListTypeImpl.java b/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/MetaListTypeImpl.java index f5d8be3e..d08509ae 100644 --- a/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/MetaListTypeImpl.java +++ b/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/MetaListTypeImpl.java @@ -1,13 +1,13 @@ -package io.katharsis.jpa.internal.meta.impl; - -import java.lang.reflect.Type; - -import io.katharsis.jpa.internal.meta.MetaListType; -import io.katharsis.jpa.internal.meta.MetaType; - -public class MetaListTypeImpl extends MetaCollectionTypeImpl implements MetaListType { - - public MetaListTypeImpl(MetaElementImpl parent, Class implClass, Type implType, MetaType elementType) { - super(parent, implClass, implType, elementType); - } -} +package io.katharsis.jpa.internal.meta.impl; + +import java.lang.reflect.Type; + +import io.katharsis.jpa.internal.meta.MetaListType; +import io.katharsis.jpa.internal.meta.MetaType; + +public class MetaListTypeImpl extends MetaCollectionTypeImpl implements MetaListType { + + public MetaListTypeImpl(MetaElementImpl parent, Class implClass, Type implType, MetaType elementType) { + super(parent, implClass, implType, elementType); + } +} diff --git a/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/MetaMapAttributeImpl.java b/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/MetaMapAttributeImpl.java index 7dc44db7..b63acf81 100644 --- a/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/MetaMapAttributeImpl.java +++ b/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/MetaMapAttributeImpl.java @@ -1,111 +1,111 @@ -package io.katharsis.jpa.internal.meta.impl; - -import io.katharsis.jpa.internal.meta.MetaAttribute; -import io.katharsis.jpa.internal.meta.MetaDataObject; -import io.katharsis.jpa.internal.meta.MetaMapAttribute; -import io.katharsis.jpa.internal.meta.MetaMapType; -import io.katharsis.jpa.internal.meta.MetaType; - -public class MetaMapAttributeImpl extends MetaElementImpl implements MetaMapAttribute { - - private boolean valueAccess; - private MetaMapType mapType; - private String keyString; - private MetaAttribute mapAttr; - - public MetaMapAttributeImpl(MetaMapType mapType, MetaAttribute mapAttr, String keyString, boolean valueAccess) { - // we dont 'want to attach to meta model since - super(null); - - this.keyString = keyString; - this.valueAccess = valueAccess; - this.mapType = mapType; - this.mapAttr = mapAttr; - } - - @Override - public MetaDataObject getParent() { - return (MetaDataObject) super.getParent(); - } - - @Override - public MetaType getType() { - return mapType; - } - - @Override - public Object getValue(Object dataObject) { - throw new UnsupportedOperationException(); - } - - @Override - public void setValue(Object dataObject, Object value) { - throw new UnsupportedOperationException(); - } - - @Override - public String getId() { - throw new UnsupportedOperationException(); - } - - @Override - public Object getKey() { - MetaType keyType = mapType.getKeyType(); - return keyType.fromString(keyString); - } - - @Override - public MetaAttribute getMapAttribute() { - return mapAttr; - } - - @Override - public String getName() { - return mapAttr.getName(); - } - - @Override - public boolean isKeyAccess() { - return !valueAccess; - } - - @Override - public boolean isAssociation() { - return mapAttr.isAssociation(); - } - - @Override - public boolean isDerived() { - return mapAttr.isDerived(); - } - - @Override - public void addValue(Object dataObject, Object value) { - throw new UnsupportedOperationException(); - } - - @Override - public void removeValue(Object dataObject, Object value) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isLazy() { - return mapAttr.isLazy(); - } - - @Override - public MetaAttribute getOppositeAttribute() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isVersion() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isId() { - throw new UnsupportedOperationException(); - } -} +package io.katharsis.jpa.internal.meta.impl; + +import io.katharsis.jpa.internal.meta.MetaAttribute; +import io.katharsis.jpa.internal.meta.MetaDataObject; +import io.katharsis.jpa.internal.meta.MetaMapAttribute; +import io.katharsis.jpa.internal.meta.MetaMapType; +import io.katharsis.jpa.internal.meta.MetaType; + +public class MetaMapAttributeImpl extends MetaElementImpl implements MetaMapAttribute { + + private boolean valueAccess; + private MetaMapType mapType; + private String keyString; + private MetaAttribute mapAttr; + + public MetaMapAttributeImpl(MetaMapType mapType, MetaAttribute mapAttr, String keyString, boolean valueAccess) { + // we dont 'want to attach to meta model since + super(null); + + this.keyString = keyString; + this.valueAccess = valueAccess; + this.mapType = mapType; + this.mapAttr = mapAttr; + } + + @Override + public MetaDataObject getParent() { + return (MetaDataObject) super.getParent(); + } + + @Override + public MetaType getType() { + return mapType; + } + + @Override + public Object getValue(Object dataObject) { + throw new UnsupportedOperationException(); + } + + @Override + public void setValue(Object dataObject, Object value) { + throw new UnsupportedOperationException(); + } + + @Override + public String getId() { + throw new UnsupportedOperationException(); + } + + @Override + public Object getKey() { + MetaType keyType = mapType.getKeyType(); + return keyType.fromString(keyString); + } + + @Override + public MetaAttribute getMapAttribute() { + return mapAttr; + } + + @Override + public String getName() { + return mapAttr.getName(); + } + + @Override + public boolean isKeyAccess() { + return !valueAccess; + } + + @Override + public boolean isAssociation() { + return mapAttr.isAssociation(); + } + + @Override + public boolean isDerived() { + return mapAttr.isDerived(); + } + + @Override + public void addValue(Object dataObject, Object value) { + throw new UnsupportedOperationException(); + } + + @Override + public void removeValue(Object dataObject, Object value) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isLazy() { + return mapAttr.isLazy(); + } + + @Override + public MetaAttribute getOppositeAttribute() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isVersion() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isId() { + throw new UnsupportedOperationException(); + } +} diff --git a/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/MetaMapAttributeProjectionImpl.java b/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/MetaMapAttributeProjectionImpl.java index e031d1a6..ac06375b 100644 --- a/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/MetaMapAttributeProjectionImpl.java +++ b/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/MetaMapAttributeProjectionImpl.java @@ -1,24 +1,24 @@ -package io.katharsis.jpa.internal.meta.impl; - -import io.katharsis.jpa.internal.meta.MetaAttribute; -import io.katharsis.jpa.internal.meta.MetaAttributePath; -import io.katharsis.jpa.internal.meta.MetaAttributeProjection; -import io.katharsis.jpa.internal.meta.MetaMapAttribute; -import io.katharsis.jpa.internal.meta.MetaMapType; - -public class MetaMapAttributeProjectionImpl extends MetaMapAttributeImpl implements MetaMapAttribute, MetaAttributeProjection { - - public MetaMapAttributeProjectionImpl(MetaMapType mapType, MetaAttribute mapAttr, String key, boolean valueAccess) { - super(mapType, mapAttr, key, valueAccess); - } - - @Override - public MetaAttributePath getPath() { - return ((MetaAttributeProjection) getMapAttribute()).getPath(); - } - - @Override - public boolean isDerived() { - return ((MetaAttributeProjection) getMapAttribute()).isDerived(); - } -} +package io.katharsis.jpa.internal.meta.impl; + +import io.katharsis.jpa.internal.meta.MetaAttribute; +import io.katharsis.jpa.internal.meta.MetaAttributePath; +import io.katharsis.jpa.internal.meta.MetaAttributeProjection; +import io.katharsis.jpa.internal.meta.MetaMapAttribute; +import io.katharsis.jpa.internal.meta.MetaMapType; + +public class MetaMapAttributeProjectionImpl extends MetaMapAttributeImpl implements MetaMapAttribute, MetaAttributeProjection { + + public MetaMapAttributeProjectionImpl(MetaMapType mapType, MetaAttribute mapAttr, String key, boolean valueAccess) { + super(mapType, mapAttr, key, valueAccess); + } + + @Override + public MetaAttributePath getPath() { + return ((MetaAttributeProjection) getMapAttribute()).getPath(); + } + + @Override + public boolean isDerived() { + return ((MetaAttributeProjection) getMapAttribute()).isDerived(); + } +} diff --git a/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/MetaMapTypeImpl.java b/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/MetaMapTypeImpl.java index 397e8ce0..71ab8bd9 100644 --- a/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/MetaMapTypeImpl.java +++ b/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/MetaMapTypeImpl.java @@ -1,34 +1,34 @@ -package io.katharsis.jpa.internal.meta.impl; - -import java.lang.reflect.Type; - -import io.katharsis.jpa.internal.meta.MetaMapType; -import io.katharsis.jpa.internal.meta.MetaType; - -public class MetaMapTypeImpl extends MetaTypeImpl implements MetaMapType { - - private MetaType keyType; - private MetaType valueType; - - public MetaMapTypeImpl(MetaElementImpl parent, Class implClass, Type implType, MetaType keyType, - MetaType valueType) { - super(parent, implClass, implType); - this.keyType = keyType; - this.valueType = valueType; - } - - @Override - public MetaType getKeyType() { - return keyType; - } - - @Override - public MetaType getValueType() { - return valueType; - } - - @Override - public Object fromString(String values) { - throw new UnsupportedOperationException("no yet implemented"); - } -} +package io.katharsis.jpa.internal.meta.impl; + +import java.lang.reflect.Type; + +import io.katharsis.jpa.internal.meta.MetaMapType; +import io.katharsis.jpa.internal.meta.MetaType; + +public class MetaMapTypeImpl extends MetaTypeImpl implements MetaMapType { + + private MetaType keyType; + private MetaType valueType; + + public MetaMapTypeImpl(MetaElementImpl parent, Class implClass, Type implType, MetaType keyType, + MetaType valueType) { + super(parent, implClass, implType); + this.keyType = keyType; + this.valueType = valueType; + } + + @Override + public MetaType getKeyType() { + return keyType; + } + + @Override + public MetaType getValueType() { + return valueType; + } + + @Override + public Object fromString(String values) { + throw new UnsupportedOperationException("no yet implemented"); + } +} diff --git a/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/MetaMappedSuperclassImpl.java b/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/MetaMappedSuperclassImpl.java index 0f4871b2..31788244 100644 --- a/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/MetaMappedSuperclassImpl.java +++ b/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/MetaMappedSuperclassImpl.java @@ -1,11 +1,11 @@ -package io.katharsis.jpa.internal.meta.impl; - -import java.lang.reflect.Type; - -public class MetaMappedSuperclassImpl extends AbstractMetaEntityImpl{ - - public MetaMappedSuperclassImpl(Class implClass, Type implType, MetaDataObjectImpl superType) { - super(implClass, implType, superType); - } - -} +package io.katharsis.jpa.internal.meta.impl; + +import java.lang.reflect.Type; + +public class MetaMappedSuperclassImpl extends AbstractMetaEntityImpl{ + + public MetaMappedSuperclassImpl(Class implClass, Type implType, MetaDataObjectImpl superType) { + super(implClass, implType, superType); + } + +} diff --git a/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/MetaPrimitiveType.java b/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/MetaPrimitiveType.java index 5e0cf490..af59b42f 100644 --- a/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/MetaPrimitiveType.java +++ b/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/MetaPrimitiveType.java @@ -1,67 +1,67 @@ -package io.katharsis.jpa.internal.meta.impl; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.lang.reflect.Type; -import java.util.UUID; - -import io.katharsis.utils.parser.TypeParser; - -public class MetaPrimitiveType extends MetaTypeImpl { - - public MetaPrimitiveType(Class implClass, Type implType) { - super(null, implClass, implType); - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - @Override - public Object fromString(String value) { // NOSONAR - - Class implClass = getImplementationClass(); - if (implClass == String.class) - return value; - - if (Number.class.isAssignableFrom(implClass) || implClass.isPrimitive()) { - if (implClass == long.class || implClass == Long.class) - return Long.valueOf(value); - if (implClass == int.class || implClass == Integer.class) - return Integer.valueOf(value); - if (implClass == float.class || implClass == Float.class) - return Float.valueOf(value); - if (implClass == double.class || implClass == Double.class) - return Double.valueOf(value); - if (implClass == byte.class || implClass == Byte.class) - return Byte.valueOf(value); - } - - if (implClass == boolean.class || implClass == Boolean.class) - return Boolean.valueOf(value); - - if (implClass == UUID.class) - return UUID.fromString(value); - - if (Enum.class.isAssignableFrom(implClass)) - return Enum.valueOf((Class) implClass, value); - - Object result = checkParse(implClass, value); - if (result != null) { - return result; - } - - TypeParser typeParser = new TypeParser(); - return typeParser.parse(value, (Class) implClass); - } - - private Object checkParse(Class implClass, String value) { - Method method; - try { - method = implClass.getMethod("parse", String.class); - return method.invoke(implClass, value); - } catch (NoSuchMethodException e) { // NOSONAR - // not available - return null; - } catch (IllegalAccessException | IllegalArgumentException | SecurityException | InvocationTargetException e) { - throw new IllegalStateException(e); - } - } -} +package io.katharsis.jpa.internal.meta.impl; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Type; +import java.util.UUID; + +import io.katharsis.utils.parser.TypeParser; + +public class MetaPrimitiveType extends MetaTypeImpl { + + public MetaPrimitiveType(Class implClass, Type implType) { + super(null, implClass, implType); + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + @Override + public Object fromString(String value) { // NOSONAR + + Class implClass = getImplementationClass(); + if (implClass == String.class) + return value; + + if (Number.class.isAssignableFrom(implClass) || implClass.isPrimitive()) { + if (implClass == long.class || implClass == Long.class) + return Long.valueOf(value); + if (implClass == int.class || implClass == Integer.class) + return Integer.valueOf(value); + if (implClass == float.class || implClass == Float.class) + return Float.valueOf(value); + if (implClass == double.class || implClass == Double.class) + return Double.valueOf(value); + if (implClass == byte.class || implClass == Byte.class) + return Byte.valueOf(value); + } + + if (implClass == boolean.class || implClass == Boolean.class) + return Boolean.valueOf(value); + + if (implClass == UUID.class) + return UUID.fromString(value); + + if (Enum.class.isAssignableFrom(implClass)) + return Enum.valueOf((Class) implClass, value); + + Object result = checkParse(implClass, value); + if (result != null) { + return result; + } + + TypeParser typeParser = new TypeParser(); + return typeParser.parse(value, (Class) implClass); + } + + private Object checkParse(Class implClass, String value) { + Method method; + try { + method = implClass.getMethod("parse", String.class); + return method.invoke(implClass, value); + } catch (NoSuchMethodException e) { // NOSONAR + // not available + return null; + } catch (IllegalAccessException | IllegalArgumentException | SecurityException | InvocationTargetException e) { + throw new IllegalStateException(e); + } + } +} diff --git a/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/MetaResourceImpl.java b/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/MetaResourceImpl.java index f25e9554..892c161e 100644 --- a/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/MetaResourceImpl.java +++ b/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/MetaResourceImpl.java @@ -1,47 +1,59 @@ -package io.katharsis.jpa.internal.meta.impl; - -import java.lang.reflect.Type; -import java.util.Arrays; -import java.util.List; -import java.util.Set; - -import io.katharsis.jpa.internal.meta.MetaEntity; -import io.katharsis.jpa.internal.meta.MetaKey; -import io.katharsis.resource.field.ResourceField; -import io.katharsis.resource.field.ResourceFieldNameTransformer; -import io.katharsis.resource.information.AnnotationResourceInformationBuilder; -import io.katharsis.resource.information.ResourceInformation; - -public class MetaResourceImpl extends MetaDataObjectImpl implements MetaEntity { - - public MetaResourceImpl(Class implClass, Type implType, MetaDataObjectImpl superType) { - super(implClass, implType, superType); - - } - - @Override - @SuppressWarnings({ "unchecked", "rawtypes" }) - protected void initAttributes() { - Class implClass = this.getImplementationClass(); - AnnotationResourceInformationBuilder builder = new AnnotationResourceInformationBuilder(new ResourceFieldNameTransformer()); - ResourceInformation resourceInformation = builder.build(implClass); - - ResourceField idField = resourceInformation.getIdField(); - MetaAttributeImpl idAttr = new MetaAttributeImpl(this, idField.getUnderlyingName(), idField.getGenericType()); - addAttribute(idAttr); - MetaKey key = new MetaKeyImpl(this, idAttr.getName(), (List) Arrays.asList(idAttr), true, true, idAttr.getType()); - setPrimaryKey(key); - - Set attrFields = resourceInformation.getAttributeFields().getFields(); - for (ResourceField field : attrFields) { - MetaAttributeImpl attr = new MetaAttributeImpl(this, field.getUnderlyingName(), field.getGenericType()); - addAttribute(attr); - } - - for (ResourceField field : resourceInformation.getRelationshipFields()) { - MetaAttributeImpl attr = new MetaAttributeImpl(this, field.getUnderlyingName(), field.getGenericType()); - attr.setAssociation(true); - addAttribute(attr); - } - } +package io.katharsis.jpa.internal.meta.impl; + +import java.lang.reflect.Type; +import java.util.Arrays; +import java.util.List; +import java.util.Set; + +import io.katharsis.jpa.internal.meta.MetaKey; +import io.katharsis.jpa.internal.meta.MetaLookup; +import io.katharsis.resource.field.ResourceField; +import io.katharsis.resource.field.ResourceFieldNameTransformer; +import io.katharsis.resource.information.AnnotationResourceInformationBuilder; +import io.katharsis.resource.information.ResourceInformation; + +public class MetaResourceImpl extends MetaDataObjectImpl { + + private MetaAttributeImpl idAttr; + + public MetaResourceImpl(Class implClass, Type implType, MetaDataObjectImpl superType) { + super(implClass, implType, superType); + + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + @Override + public void init(MetaLookup lookup) { + super.init(lookup); + + MetaKey key = new MetaKeyImpl(this, idAttr.getName(), (List) Arrays.asList(idAttr), true, true, idAttr.getType()); + setPrimaryKey(key); + } + + @Override + @SuppressWarnings({ "unchecked" }) + protected void initAttributes() { + Class implClass = this.getImplementationClass(); + AnnotationResourceInformationBuilder builder = new AnnotationResourceInformationBuilder( + new ResourceFieldNameTransformer()); + ResourceInformation resourceInformation = builder.build(implClass); + + ResourceField idField = resourceInformation.getIdField(); + idAttr = new MetaAttributeImpl(this, idField.getUnderlyingName(), idField.getGenericType()); + addAttribute(idAttr); + + super.init(lookup); + + Set attrFields = resourceInformation.getAttributeFields().getFields(); + for (ResourceField field : attrFields) { + MetaAttributeImpl attr = new MetaAttributeImpl(this, field.getUnderlyingName(), field.getGenericType()); + addAttribute(attr); + } + + for (ResourceField field : resourceInformation.getRelationshipFields()) { + MetaAttributeImpl attr = new MetaAttributeImpl(this, field.getUnderlyingName(), field.getGenericType()); + attr.setAssociation(true); + addAttribute(attr); + } + } } \ No newline at end of file diff --git a/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/MetaSetTypeImpl.java b/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/MetaSetTypeImpl.java index aed41c90..d874e0ed 100644 --- a/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/MetaSetTypeImpl.java +++ b/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/MetaSetTypeImpl.java @@ -1,13 +1,13 @@ -package io.katharsis.jpa.internal.meta.impl; - -import java.lang.reflect.Type; - -import io.katharsis.jpa.internal.meta.MetaSetType; -import io.katharsis.jpa.internal.meta.MetaType; - -public class MetaSetTypeImpl extends MetaCollectionTypeImpl implements MetaSetType { - - public MetaSetTypeImpl(MetaElementImpl parent, Class implClass, Type implType, MetaType elementType) { - super(parent, implClass, implType, elementType); - } -} +package io.katharsis.jpa.internal.meta.impl; + +import java.lang.reflect.Type; + +import io.katharsis.jpa.internal.meta.MetaSetType; +import io.katharsis.jpa.internal.meta.MetaType; + +public class MetaSetTypeImpl extends MetaCollectionTypeImpl implements MetaSetType { + + public MetaSetTypeImpl(MetaElementImpl parent, Class implClass, Type implType, MetaType elementType) { + super(parent, implClass, implType, elementType); + } +} diff --git a/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/MetaTypeImpl.java b/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/MetaTypeImpl.java index b95ddaaa..94298011 100644 --- a/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/MetaTypeImpl.java +++ b/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/meta/impl/MetaTypeImpl.java @@ -1,86 +1,86 @@ -package io.katharsis.jpa.internal.meta.impl; - -import java.lang.reflect.Type; -import java.util.HashSet; -import java.util.Set; - -import io.katharsis.jpa.internal.meta.MetaCollectionType; -import io.katharsis.jpa.internal.meta.MetaMapType; -import io.katharsis.jpa.internal.meta.MetaType; - -public class MetaTypeImpl extends MetaElementImpl implements MetaType { - - private Class implClass; - private Type implType; - - public MetaTypeImpl(MetaElementImpl parent, Class implClass, Type implType) { - super(parent); - this.implClass = implClass; - this.implType = implType; - } - - @Override - public Class getImplementationClass() { - return implClass; - } - - @Override - public Type getImplementationType() { - return implType; - } - - @Override - public String getName() { - return implClass.getSimpleName(); - } - - @Override - public String getId() { - return implClass.getName(); - } - - @Override - public Object fromString(String value) { - throw new UnsupportedOperationException(); - } - - @Override - public Set fromString(Set values) { - Set result = new HashSet<>(); - for (String value : values) { - result.add(fromString(value)); - } - return result; - } - - @Override - public boolean isCollection() { - return this instanceof MetaCollectionType; - } - - @Override - public MetaCollectionType asCollection() { - return (MetaCollectionType) this; - } - - @Override - public boolean isMap() { - return this instanceof MetaMapType; - } - - @Override - public MetaMapType asMap() { - return (MetaMapType) this; - } - - @Override - public MetaType getElementType() { - if (isCollection()) { - return asCollection().getElementType(); - } else if (isMap()) { - return asMap().getValueType(); - } else { - return this; - } - } -} +package io.katharsis.jpa.internal.meta.impl; + +import java.lang.reflect.Type; +import java.util.HashSet; +import java.util.Set; + +import io.katharsis.jpa.internal.meta.MetaCollectionType; +import io.katharsis.jpa.internal.meta.MetaMapType; +import io.katharsis.jpa.internal.meta.MetaType; + +public class MetaTypeImpl extends MetaElementImpl implements MetaType { + + private Class implClass; + private Type implType; + + public MetaTypeImpl(MetaElementImpl parent, Class implClass, Type implType) { + super(parent); + this.implClass = implClass; + this.implType = implType; + } + + @Override + public Class getImplementationClass() { + return implClass; + } + + @Override + public Type getImplementationType() { + return implType; + } + + @Override + public String getName() { + return implClass.getSimpleName(); + } + + @Override + public String getId() { + return implClass.getName(); + } + + @Override + public Object fromString(String value) { + throw new UnsupportedOperationException(); + } + + @Override + public Set fromString(Set values) { + Set result = new HashSet<>(); + for (String value : values) { + result.add(fromString(value)); + } + return result; + } + + @Override + public boolean isCollection() { + return this instanceof MetaCollectionType; + } + + @Override + public MetaCollectionType asCollection() { + return (MetaCollectionType) this; + } + + @Override + public boolean isMap() { + return this instanceof MetaMapType; + } + + @Override + public MetaMapType asMap() { + return (MetaMapType) this; + } + + @Override + public MetaType getElementType() { + if (isCollection()) { + return asCollection().getElementType(); + } else if (isMap()) { + return asMap().getValueType(); + } else { + return this; + } + } +} diff --git a/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/query/AbstractJpaQueryImpl.java b/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/query/AbstractJpaQueryImpl.java index c04cdf4c..d8b96aa4 100644 --- a/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/query/AbstractJpaQueryImpl.java +++ b/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/query/AbstractJpaQueryImpl.java @@ -1,214 +1,214 @@ -package io.katharsis.jpa.internal.query; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import javax.persistence.EntityManager; -import javax.persistence.criteria.JoinType; - -import io.katharsis.jpa.internal.meta.MetaAttribute; -import io.katharsis.jpa.internal.meta.MetaAttributePath; -import io.katharsis.jpa.internal.meta.MetaDataObject; -import io.katharsis.jpa.internal.meta.MetaLookup; -import io.katharsis.jpa.internal.query.backend.JpaQueryBackend; -import io.katharsis.jpa.query.JpaQuery; -import io.katharsis.queryspec.Direction; -import io.katharsis.queryspec.FilterOperator; -import io.katharsis.queryspec.FilterSpec; -import io.katharsis.queryspec.IncludeFieldSpec; -import io.katharsis.queryspec.SortSpec; - -public abstract class AbstractJpaQueryImpl> implements JpaQuery { - - protected final EntityManager em; - - protected final MetaDataObject meta; - - protected final Class clazz; - - protected JoinType defaultJoinType = JoinType.INNER; - - protected final Map joinTypes = new HashMap<>(); - - protected ArrayList filterSpecs = new ArrayList<>(); - - protected ArrayList sortSpecs = new ArrayList<>(); - - protected ArrayList includedFields = new ArrayList<>(); - - protected boolean autoDistinct = true; - - protected boolean autoGroupBy = false; - - protected boolean distinct = false; - - protected boolean ensureTotalOrder = true; - - protected Class parentEntityClass; - - protected List parentIds; - - protected MetaAttribute parentAttr; - - private VirtualAttributeRegistry virtualAttrs; - - protected AbstractJpaQueryImpl(MetaLookup metaLookup, EntityManager em, Class clazz, - VirtualAttributeRegistry virtualAttrs) { - this.em = em; - this.clazz = clazz; - this.meta = metaLookup.getMeta(clazz).asDataObject(); - this.virtualAttrs = virtualAttrs; - } - - @SuppressWarnings("unchecked") - public AbstractJpaQueryImpl(MetaLookup metaLookup, EntityManager em, Class entityClass, - VirtualAttributeRegistry virtualAttrs, String attrName, List entityIds) { - this.em = em; - this.virtualAttrs = virtualAttrs; - - MetaDataObject parentMeta = metaLookup.getMeta(entityClass).asDataObject(); - MetaAttribute attrMeta = parentMeta.getAttribute(attrName); - if(attrMeta.getType().isCollection()) { - this.meta = attrMeta.getType().asCollection().getElementType().asEntity(); - } - else { - this.meta = attrMeta.getType().asEntity(); - } - this.clazz = (Class) meta.getImplementationClass(); - - this.parentEntityClass = entityClass; - this.parentAttr = attrMeta; - this.parentIds = entityIds; - } - - @Override - public void addSelection(List path) { - includedFields.add(new IncludeFieldSpec(path)); - } - - @Override - public JpaQuery setEnsureTotalOrder(boolean ensureTotalOrder) { - this.ensureTotalOrder = ensureTotalOrder; - return this; - } - - @Override - public JpaQuery addFilter(FilterSpec filters) { - this.filterSpecs.add(filters); - return this; - } - - @Override - public JpaQuery addSortBy(List attributePath, Direction dir) { - this.sortSpecs.add(new SortSpec(attributePath, dir)); - return this; - } - - @Override - public JpaQuery addSortBy(SortSpec order) { - this.sortSpecs.add(order); - return this; - } - - @Override - public JpaQuery setDefaultJoinType(JoinType joinType) { - this.defaultJoinType = joinType; - return this; - } - - @Override - public JpaQuery setJoinType(List path, JoinType joinType) { - joinTypes.put(meta.resolvePath(path), joinType); - return this; - } - - @Override - public JpaQuery setAutoGroupBy(boolean autoGroupBy) { - this.autoGroupBy = autoGroupBy; - return this; - } - - @Override - public JpaQuery setDistinct(boolean distinct) { - this.autoDistinct = false; - this.distinct = distinct; - return this; - } - - @Override - public JpaQuery addFilter(String attrPath, FilterOperator filterOperator, Object value) { - return addFilter(Arrays.asList(attrPath.split("\\.")), filterOperator, value); - } - - @Override - public JpaQuery addFilter(List attrPath, FilterOperator filterOperator, Object value) { - addFilter(new FilterSpec(attrPath, filterOperator, value)); - return this; - } - - public List getSortSpecs() { - return sortSpecs; - } - - public boolean getEnsureTotalOrder() { - return ensureTotalOrder; - } - - public List getIncludedFields() { - return includedFields; - } - - public JoinType getJoinType(MetaAttributePath path) { - JoinType joinType = joinTypes.get(path); - if (joinType == null) - joinType = defaultJoinType; - return joinType; - } - - public VirtualAttributeRegistry getVirtualAttrs() { - return virtualAttrs; - } - - public MetaDataObject getMeta() { - return meta; - } - - @Override - public Class getEntityClass() { - return clazz; - } - - @Override - public AbstractQueryExecutorImpl buildExecutor() { - B backend = newBackend(); - - @SuppressWarnings({ "rawtypes", "unchecked" }) - QueryBuilder executorFactory = new QueryBuilder(this, backend); - Map selectionBindings = executorFactory.applySelectionSpec(); - executorFactory.applyFilterSpec(); - executorFactory.applySortSpec(); - int numAutoSelections = executorFactory.applyDistinct(); - - return newExecutor(backend, numAutoSelections, selectionBindings); - } - - protected abstract AbstractQueryExecutorImpl newExecutor(B ctx, int numAutoSelections, Map selectionBindings); - - protected abstract B newBackend(); - - @SuppressWarnings({ "unchecked", "hiding" }) - public List getParentIds() { - return (List) parentIds; - } - - public List getFilterSpecs() { - return filterSpecs; - } - - public MetaAttribute getParentAttr() { - return parentAttr; - } -} +package io.katharsis.jpa.internal.query; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.persistence.EntityManager; +import javax.persistence.criteria.JoinType; + +import io.katharsis.jpa.internal.meta.MetaAttribute; +import io.katharsis.jpa.internal.meta.MetaAttributePath; +import io.katharsis.jpa.internal.meta.MetaDataObject; +import io.katharsis.jpa.internal.meta.MetaLookup; +import io.katharsis.jpa.internal.query.backend.JpaQueryBackend; +import io.katharsis.jpa.query.JpaQuery; +import io.katharsis.queryspec.Direction; +import io.katharsis.queryspec.FilterOperator; +import io.katharsis.queryspec.FilterSpec; +import io.katharsis.queryspec.IncludeFieldSpec; +import io.katharsis.queryspec.SortSpec; + +public abstract class AbstractJpaQueryImpl> implements JpaQuery { + + protected final EntityManager em; + + protected final MetaDataObject meta; + + protected final Class clazz; + + protected JoinType defaultJoinType = JoinType.INNER; + + protected final Map joinTypes = new HashMap<>(); + + protected ArrayList filterSpecs = new ArrayList<>(); + + protected ArrayList sortSpecs = new ArrayList<>(); + + protected ArrayList includedFields = new ArrayList<>(); + + protected boolean autoDistinct = true; + + protected boolean autoGroupBy = false; + + protected boolean distinct = false; + + protected boolean ensureTotalOrder = true; + + protected Class parentEntityClass; + + protected List parentIds; + + protected MetaAttribute parentAttr; + + private ComputedAttributeRegistryImpl computedAttrs; + + protected AbstractJpaQueryImpl(MetaLookup metaLookup, EntityManager em, Class clazz, + ComputedAttributeRegistryImpl computedAttrs) { + this.em = em; + this.clazz = clazz; + this.meta = metaLookup.getMeta(clazz).asDataObject(); + this.computedAttrs = computedAttrs; + } + + @SuppressWarnings("unchecked") + public AbstractJpaQueryImpl(MetaLookup metaLookup, EntityManager em, Class entityClass, + ComputedAttributeRegistryImpl virtualAttrs, String attrName, List entityIds) { + this.em = em; + this.computedAttrs = virtualAttrs; + + MetaDataObject parentMeta = metaLookup.getMeta(entityClass).asDataObject(); + MetaAttribute attrMeta = parentMeta.getAttribute(attrName); + if(attrMeta.getType().isCollection()) { + this.meta = attrMeta.getType().asCollection().getElementType().asEntity(); + } + else { + this.meta = attrMeta.getType().asEntity(); + } + this.clazz = (Class) meta.getImplementationClass(); + + this.parentEntityClass = entityClass; + this.parentAttr = attrMeta; + this.parentIds = entityIds; + } + + @Override + public void addSelection(List path) { + includedFields.add(new IncludeFieldSpec(path)); + } + + @Override + public JpaQuery setEnsureTotalOrder(boolean ensureTotalOrder) { + this.ensureTotalOrder = ensureTotalOrder; + return this; + } + + @Override + public JpaQuery addFilter(FilterSpec filters) { + this.filterSpecs.add(filters); + return this; + } + + @Override + public JpaQuery addSortBy(List attributePath, Direction dir) { + this.sortSpecs.add(new SortSpec(attributePath, dir)); + return this; + } + + @Override + public JpaQuery addSortBy(SortSpec order) { + this.sortSpecs.add(order); + return this; + } + + @Override + public JpaQuery setDefaultJoinType(JoinType joinType) { + this.defaultJoinType = joinType; + return this; + } + + @Override + public JpaQuery setJoinType(List path, JoinType joinType) { + joinTypes.put(meta.resolvePath(path), joinType); + return this; + } + + @Override + public JpaQuery setAutoGroupBy(boolean autoGroupBy) { + this.autoGroupBy = autoGroupBy; + return this; + } + + @Override + public JpaQuery setDistinct(boolean distinct) { + this.autoDistinct = false; + this.distinct = distinct; + return this; + } + + @Override + public JpaQuery addFilter(String attrPath, FilterOperator filterOperator, Object value) { + return addFilter(Arrays.asList(attrPath.split("\\.")), filterOperator, value); + } + + @Override + public JpaQuery addFilter(List attrPath, FilterOperator filterOperator, Object value) { + addFilter(new FilterSpec(attrPath, filterOperator, value)); + return this; + } + + public List getSortSpecs() { + return sortSpecs; + } + + public boolean getEnsureTotalOrder() { + return ensureTotalOrder; + } + + public List getIncludedFields() { + return includedFields; + } + + public JoinType getJoinType(MetaAttributePath path) { + JoinType joinType = joinTypes.get(path); + if (joinType == null) + joinType = defaultJoinType; + return joinType; + } + + public ComputedAttributeRegistryImpl getComputedAttrs() { + return computedAttrs; + } + + public MetaDataObject getMeta() { + return meta; + } + + @Override + public Class getEntityClass() { + return clazz; + } + + @Override + public AbstractQueryExecutorImpl buildExecutor() { + B backend = newBackend(); + + @SuppressWarnings({ "rawtypes", "unchecked" }) + QueryBuilder executorFactory = new QueryBuilder(this, backend); + Map selectionBindings = executorFactory.applySelectionSpec(); + executorFactory.applyFilterSpec(); + executorFactory.applySortSpec(); + int numAutoSelections = executorFactory.applyDistinct(); + + return newExecutor(backend, numAutoSelections, selectionBindings); + } + + protected abstract AbstractQueryExecutorImpl newExecutor(B ctx, int numAutoSelections, Map selectionBindings); + + protected abstract B newBackend(); + + @SuppressWarnings({ "unchecked", "hiding" }) + public List getParentIds() { + return (List) parentIds; + } + + public List getFilterSpecs() { + return filterSpecs; + } + + public MetaAttribute getParentAttr() { + return parentAttr; + } +} diff --git a/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/query/VirtualAttributeRegistry.java b/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/query/ComputedAttributeRegistryImpl.java similarity index 58% rename from katharsis-jpa/src/main/java/io/katharsis/jpa/internal/query/VirtualAttributeRegistry.java rename to katharsis-jpa/src/main/java/io/katharsis/jpa/internal/query/ComputedAttributeRegistryImpl.java index 6b9ac6e7..8b1aad1d 100644 --- a/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/query/VirtualAttributeRegistry.java +++ b/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/query/ComputedAttributeRegistryImpl.java @@ -1,50 +1,69 @@ -package io.katharsis.jpa.internal.query; - -import java.lang.reflect.Type; -import java.util.HashMap; -import java.util.Map; - -import io.katharsis.jpa.internal.meta.MetaDataObject; -import io.katharsis.jpa.internal.meta.MetaLookup; - -public class VirtualAttributeRegistry { - - private Map map = new HashMap<>(); - private MetaLookup metaLookup; - - private static class Registration { - private MetaVirtualAttribute attribute; - private Object expressionFactory; - } - - public VirtualAttributeRegistry(MetaLookup metaLookup) { - this.metaLookup = metaLookup; - } - - public Object get(MetaVirtualAttribute attr) { - Class clazz = attr.getParent().getImplementationClass(); - Registration registration = map.get(key(clazz, attr.getName())); - return registration != null ? registration.expressionFactory : null; - } - - public MetaVirtualAttribute get(MetaDataObject meta, String name) { - Class clazz = meta.getImplementationClass(); - Registration registration = map.get(key(clazz, name)); - return registration != null ? registration.attribute : null; - } - - public void register(Class targetClass, String name, Object expressionFactory, Type type) { - MetaDataObject targetMeta = metaLookup.getMeta(targetClass).asDataObject(); - MetaVirtualAttribute attr = new MetaVirtualAttribute(targetMeta, name, type); - attr.init(metaLookup); - - Registration registration = new Registration(); - registration.attribute = attr; - registration.expressionFactory = expressionFactory; - map.put(key(targetClass, name), registration); - } - - private String key(Class targetClass, String name) { - return targetClass.getName() + "." + name; - } -} +package io.katharsis.jpa.internal.query; + +import java.lang.reflect.Type; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import io.katharsis.jpa.internal.meta.MetaDataObject; +import io.katharsis.jpa.internal.meta.MetaLookup; +import io.katharsis.jpa.query.ComputedAttributeRegistry; + +public class ComputedAttributeRegistryImpl implements ComputedAttributeRegistry { + + private Map map = new HashMap<>(); + + private MetaLookup metaLookup; + + private static class Registration { + + private MetaComputedAttribute attribute; + + private Object expressionFactory; + } + + public ComputedAttributeRegistryImpl(MetaLookup metaLookup) { + this.metaLookup = metaLookup; + } + + public Object get(MetaComputedAttribute attr) { + Class clazz = attr.getParent().getImplementationClass(); + Registration registration = map.get(key(clazz, attr.getName())); + return registration != null ? registration.expressionFactory : null; + } + + public MetaComputedAttribute get(MetaDataObject meta, String name) { + Class clazz = meta.getImplementationClass(); + Registration registration = map.get(key(clazz, name)); + return registration != null ? registration.attribute : null; + } + + public void register(Class targetClass, String name, Object expressionFactory, Type type) { + MetaDataObject targetMeta = metaLookup.getMeta(targetClass).asDataObject(); + MetaComputedAttribute attr = new MetaComputedAttribute(targetMeta, name, type); + attr.init(metaLookup); + + Registration registration = new Registration(); + registration.attribute = attr; + registration.expressionFactory = expressionFactory; + map.put(key(targetClass, name), registration); + } + + private String key(Class targetClass, String name) { + return targetClass.getName() + "." + name; + } + + @Override + public Set getForType(Class entityType) { + Set set = new HashSet<>(); + for (Registration reg : map.values()) { + MetaDataObject parent = reg.attribute.getParent(); + Class parentImpl = parent.getImplementationClass(); + if (parentImpl.isAssignableFrom(entityType)) { + set.add(reg.attribute.getName()); + } + } + return set; + } +} diff --git a/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/query/MetaVirtualAttribute.java b/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/query/MetaComputedAttribute.java similarity index 78% rename from katharsis-jpa/src/main/java/io/katharsis/jpa/internal/query/MetaVirtualAttribute.java rename to katharsis-jpa/src/main/java/io/katharsis/jpa/internal/query/MetaComputedAttribute.java index 16157dd6..3da6de42 100644 --- a/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/query/MetaVirtualAttribute.java +++ b/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/query/MetaComputedAttribute.java @@ -1,33 +1,33 @@ -package io.katharsis.jpa.internal.query; - -import java.lang.reflect.Type; - -import io.katharsis.jpa.internal.meta.MetaDataObject; -import io.katharsis.jpa.internal.meta.impl.MetaAttributeImpl; - -public class MetaVirtualAttribute extends MetaAttributeImpl { - - private MetaDataObject virtualParent; - - public MetaVirtualAttribute(MetaDataObject parent, String name, Type type) { - super(null, name, type); - - // not attach to actual parent! - this.virtualParent = parent; - } - - @Override - public MetaDataObject getParent() { - return virtualParent; - } - - @Override - public Object getValue(Object dataObject) { - throw new UnsupportedOperationException(); - } - - @Override - public void setValue(Object dataObject, Object value) { - throw new UnsupportedOperationException(); - } -} +package io.katharsis.jpa.internal.query; + +import java.lang.reflect.Type; + +import io.katharsis.jpa.internal.meta.MetaDataObject; +import io.katharsis.jpa.internal.meta.impl.MetaAttributeImpl; + +public class MetaComputedAttribute extends MetaAttributeImpl { + + private MetaDataObject virtualParent; + + public MetaComputedAttribute(MetaDataObject parent, String name, Type type) { + super(null, name, type); + + // not attach to actual parent! + this.virtualParent = parent; + } + + @Override + public MetaDataObject getParent() { + return virtualParent; + } + + @Override + public Object getValue(Object dataObject) { + throw new UnsupportedOperationException(); + } + + @Override + public void setValue(Object dataObject, Object value) { + throw new UnsupportedOperationException(); + } +} diff --git a/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/query/QueryBuilder.java b/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/query/QueryBuilder.java index b049b8b4..0bf192a8 100644 --- a/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/query/QueryBuilder.java +++ b/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/query/QueryBuilder.java @@ -1,131 +1,131 @@ -package io.katharsis.jpa.internal.query; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import io.katharsis.jpa.internal.meta.MetaAttribute; -import io.katharsis.jpa.internal.meta.MetaAttributeFinder; -import io.katharsis.jpa.internal.meta.MetaAttributePath; -import io.katharsis.jpa.internal.meta.MetaDataObject; -import io.katharsis.jpa.internal.meta.MetaEntity; -import io.katharsis.jpa.internal.meta.MetaKey; -import io.katharsis.jpa.internal.query.backend.JpaQueryBackend; -import io.katharsis.queryspec.FilterSpec; -import io.katharsis.queryspec.IncludeFieldSpec; - -public class QueryBuilder { - - private static final String SORT_COLUMN_ALIAS_PREFIX = "__sort"; - - private JpaQueryBackend backend; - - private AbstractJpaQueryImpl query; - - private MetaAttributeFinder attributeFinder; - - public QueryBuilder(AbstractJpaQueryImpl query, JpaQueryBackend backend) { - this.query = query; - this.backend = backend; - - final VirtualAttributeRegistry virtualAttrs = query.getVirtualAttrs(); - this.attributeFinder = new MetaAttributeFinder() { - - @Override - public MetaAttribute getAttribute(MetaDataObject meta, String name) { - MetaVirtualAttribute attr = virtualAttrs.get(meta, name); - if (attr != null) { - return attr; - } - return meta.findAttribute(name, true); - } - }; - } - - /** - * Returns true if distinct is set manually or the "auto distinct" mode is - * enabled and the user performs a join or fetch on a many assocation. In - * this case, attributes from referenced entities included in the sort - * clause are also added to the select clause. - */ - protected int applyDistinct() { - int numAutoSelections = 0; - boolean distinct; - if (query.autoDistinct) { - // distinct for many join/fetches or manual - // in case of ViewQuery we may not need this here, but we need to do - // the selection of order-by columns below - distinct = query.autoDistinct && !query.autoGroupBy && backend.hasManyRootsFetchesOrJoins(); - if (distinct) { - // we also need to select sorted attributes (for references) - numAutoSelections = addOrderExpressionsToSelection(); - } - } - else { - distinct = query.distinct; - } - if (distinct) { - backend.distinct(); - } - return numAutoSelections; - } - - public Map applySelectionSpec() { - MetaDataObject meta = query.getMeta(); - - Map selectionBindings = new HashMap<>(); - - int index = 1; - List includedFields = query.getIncludedFields(); - for (IncludeFieldSpec includedField : includedFields) { - MetaAttributePath path = meta.resolvePath(includedField.getAttributePath(), attributeFinder); - E attr = backend.getAttribute(path); - backend.addSelection(attr, path.toString()); - selectionBindings.put(path.toString(), index++); - } - return selectionBindings; - } - - private int addOrderExpressionsToSelection() { - int numAutoSelections = 0; - int prefixIndex = 0; - List newOrderList = new ArrayList<>(); - for (O order : backend.getOrderList()) { - E expression = backend.getExpression(order); - if (backend.containsRelation(expression)) { - backend.addSelection(expression, SORT_COLUMN_ALIAS_PREFIX + prefixIndex++); - numAutoSelections++; - } - newOrderList.add(order); - } - backend.setOrder(newOrderList); - return numAutoSelections; - } - - protected void applySortSpec() { - QuerySortBuilder orderBuilder = new QuerySortBuilder<>(query, backend); - orderBuilder.applySortSpec(); - } - - protected void applyFilterSpec() { - QueryFilterBuilder predicateBuilder = new QueryFilterBuilder<>(query.getVirtualAttrs(), backend, attributeFinder); - - MetaDataObject meta = query.getMeta(); - List filters = query.getFilterSpecs(); - List

predicates = predicateBuilder.filterSpecListToPredicateArray(meta, backend.getRoot(), filters); - if (predicates != null && !predicates.isEmpty()) { - backend.addPredicate(backend.and(predicates)); - } - - MetaAttribute parentAttr = query.getParentAttr(); - if (parentAttr != null) { - MetaEntity parentEntity = parentAttr.getParent().asEntity(); - MetaKey primaryKey = parentEntity.getPrimaryKey(); - MetaAttribute primaryKeyAttr = primaryKey.getUniqueElement(); - - backend.addParentPredicate(primaryKeyAttr); - } - } - -} +package io.katharsis.jpa.internal.query; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import io.katharsis.jpa.internal.meta.MetaAttribute; +import io.katharsis.jpa.internal.meta.MetaAttributeFinder; +import io.katharsis.jpa.internal.meta.MetaAttributePath; +import io.katharsis.jpa.internal.meta.MetaDataObject; +import io.katharsis.jpa.internal.meta.MetaEntity; +import io.katharsis.jpa.internal.meta.MetaKey; +import io.katharsis.jpa.internal.query.backend.JpaQueryBackend; +import io.katharsis.queryspec.FilterSpec; +import io.katharsis.queryspec.IncludeFieldSpec; + +public class QueryBuilder { + + private static final String SORT_COLUMN_ALIAS_PREFIX = "__sort"; + + private JpaQueryBackend backend; + + private AbstractJpaQueryImpl query; + + private MetaAttributeFinder attributeFinder; + + public QueryBuilder(AbstractJpaQueryImpl query, JpaQueryBackend backend) { + this.query = query; + this.backend = backend; + + final ComputedAttributeRegistryImpl virtualAttrs = query.getComputedAttrs(); + this.attributeFinder = new MetaAttributeFinder() { + + @Override + public MetaAttribute getAttribute(MetaDataObject meta, String name) { + MetaComputedAttribute attr = virtualAttrs.get(meta, name); + if (attr != null) { + return attr; + } + return meta.findAttribute(name, true); + } + }; + } + + /** + * Returns true if distinct is set manually or the "auto distinct" mode is + * enabled and the user performs a join or fetch on a many assocation. In + * this case, attributes from referenced entities included in the sort + * clause are also added to the select clause. + */ + protected int applyDistinct() { + int numAutoSelections = 0; + boolean distinct; + if (query.autoDistinct) { + // distinct for many join/fetches or manual + // in case of ViewQuery we may not need this here, but we need to do + // the selection of order-by columns below + distinct = query.autoDistinct && !query.autoGroupBy && backend.hasManyRootsFetchesOrJoins(); + if (distinct) { + // we also need to select sorted attributes (for references) + numAutoSelections = addOrderExpressionsToSelection(); + } + } + else { + distinct = query.distinct; + } + if (distinct) { + backend.distinct(); + } + return numAutoSelections; + } + + public Map applySelectionSpec() { + MetaDataObject meta = query.getMeta(); + + Map selectionBindings = new HashMap<>(); + + int index = 1; + List includedFields = query.getIncludedFields(); + for (IncludeFieldSpec includedField : includedFields) { + MetaAttributePath path = meta.resolvePath(includedField.getAttributePath(), attributeFinder); + E attr = backend.getAttribute(path); + backend.addSelection(attr, path.toString()); + selectionBindings.put(path.toString(), index++); + } + return selectionBindings; + } + + private int addOrderExpressionsToSelection() { + int numAutoSelections = 0; + int prefixIndex = 0; + List newOrderList = new ArrayList<>(); + for (O order : backend.getOrderList()) { + E expression = backend.getExpression(order); + if (backend.containsRelation(expression)) { + backend.addSelection(expression, SORT_COLUMN_ALIAS_PREFIX + prefixIndex++); + numAutoSelections++; + } + newOrderList.add(order); + } + backend.setOrder(newOrderList); + return numAutoSelections; + } + + protected void applySortSpec() { + QuerySortBuilder orderBuilder = new QuerySortBuilder<>(query, backend); + orderBuilder.applySortSpec(); + } + + protected void applyFilterSpec() { + QueryFilterBuilder predicateBuilder = new QueryFilterBuilder<>(query.getComputedAttrs(), backend, attributeFinder); + + MetaDataObject meta = query.getMeta(); + List filters = query.getFilterSpecs(); + List

predicates = predicateBuilder.filterSpecListToPredicateArray(meta, backend.getRoot(), filters); + if (predicates != null && !predicates.isEmpty()) { + backend.addPredicate(backend.and(predicates)); + } + + MetaAttribute parentAttr = query.getParentAttr(); + if (parentAttr != null) { + MetaEntity parentEntity = parentAttr.getParent().asEntity(); + MetaKey primaryKey = parentEntity.getPrimaryKey(); + MetaAttribute primaryKeyAttr = primaryKey.getUniqueElement(); + + backend.addParentPredicate(primaryKeyAttr); + } + } + +} diff --git a/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/query/QueryFilterBuilder.java b/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/query/QueryFilterBuilder.java index 045862ec..b9584c59 100644 --- a/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/query/QueryFilterBuilder.java +++ b/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/query/QueryFilterBuilder.java @@ -1,153 +1,153 @@ -package io.katharsis.jpa.internal.query; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Set; - -import javax.persistence.criteria.JoinType; - -import io.katharsis.jpa.internal.meta.MetaAttribute; -import io.katharsis.jpa.internal.meta.MetaAttributeFinder; -import io.katharsis.jpa.internal.meta.MetaAttributePath; -import io.katharsis.jpa.internal.meta.MetaDataObject; -import io.katharsis.jpa.internal.meta.MetaMapType; -import io.katharsis.jpa.internal.meta.MetaProjection; -import io.katharsis.jpa.internal.meta.MetaType; -import io.katharsis.jpa.internal.query.backend.JpaQueryBackend; -import io.katharsis.jpa.query.AnyTypeObject; -import io.katharsis.queryspec.FilterOperator; -import io.katharsis.queryspec.FilterSpec; - -public final class QueryFilterBuilder { - - private static final int ORACLE_PARAM_LIMIT = 900; - - private MetaAttributeFinder attributeFinder; - - private JpaQueryBackend backend; - - protected QueryFilterBuilder(final VirtualAttributeRegistry virtualAttrs, JpaQueryBackend backend, MetaAttributeFinder attributeFinder) { - this.backend = backend; - this.attributeFinder = attributeFinder; - } - - public List

filterSpecListToPredicateArray(MetaDataObject rootMeta, F root, List rowFilters) { - return filterSpecListToPredicateArray(rootMeta, root, rowFilters, false, null); - } - - public List

filterSpecListToPredicateArray(MetaDataObject rootMeta, F root, List rowFilters, - boolean forceEntityBased, JoinType defaultPredicateJoinType) { - ArrayList

predicateList = new ArrayList<>(); - for (FilterSpec rowFilter : rowFilters) { - predicateList.add( - filterSpecListToPredicate(rootMeta, root, rowFilter, forceEntityBased, defaultPredicateJoinType)); - } - return predicateList; - } - - protected P filterSpecListToPredicate(MetaDataObject rootMeta, F root, FilterSpec fs) { - return filterSpecListToPredicate(rootMeta, root, fs, false, null); - } - - protected P filterSpecListToPredicate(MetaDataObject rootMeta, F root, FilterSpec fs, boolean forceEntityBased, - JoinType defaultPredicateJoinType) { - if ((fs.getOperator() == FilterOperator.EQ || fs.getOperator() == FilterOperator.NEQ) - && fs.getValue() instanceof Collection && ((Collection) fs.getValue()).size() > ORACLE_PARAM_LIMIT) { - - return filterLargeValueSets(fs, rootMeta, root, forceEntityBased, defaultPredicateJoinType); - } else { - if (fs.hasExpressions()) { - return filterExpressions(fs, rootMeta, root, forceEntityBased, defaultPredicateJoinType); - } - - else { - return filterSimpleOperation(fs, rootMeta, forceEntityBased); - } - } - } - - /** - * Split filters with two many value possibilities. For example, Oracle - * cannot handle more than 1000. - * - * @param fs - * @param rootMeta - * @param root - * @param forceEntityBased - * @param defaultPredicateJoinType - * @return - */ - private P filterLargeValueSets(FilterSpec fs, MetaDataObject rootMeta, F root, boolean forceEntityBased, - JoinType defaultPredicateJoinType) { - ArrayList specs = new ArrayList<>(); - List list = new ArrayList<>((Collection) fs.getValue()); - for (int i = 0; i < list.size(); i += ORACLE_PARAM_LIMIT) { - int nextOffset = i + Math.min(list.size() - i, ORACLE_PARAM_LIMIT); - List batchList = list.subList(i, nextOffset); - specs.add(new FilterSpec(fs.getAttributePath(), fs.getOperator(), batchList)); - } - - FilterSpec orSpec = FilterSpec.or(specs); - return filterSpecListToPredicate(rootMeta, root, orSpec, forceEntityBased, defaultPredicateJoinType); - } - - private P filterSimpleOperation(FilterSpec fs, MetaDataObject rootMeta, boolean forceEntityBased) { - Object value = fs.getValue(); - if (value instanceof Set) { - // HashSet not properly supported in ORM/JDBC, convert to - // list - Set set = (Set) value; - value = new ArrayList(set); - } - MetaDataObject rootBaseType = getBaseType(rootMeta, forceEntityBased); - MetaAttributePath path = rootBaseType.resolvePath(fs.getAttributePath(), attributeFinder); - path = enhanceAttributePath(path, value); - return backend.buildPredicate(fs.getOperator(), path, value); - } - - private P filterExpressions(FilterSpec fs, MetaDataObject rootMeta, F root, boolean forceEntityBased, - JoinType defaultPredicateJoinType) { - // and, or, not. - if (fs.getOperator() == FilterOperator.NOT) { - return backend.not(backend.and(filterSpecListToPredicateArray(rootMeta, root, fs.getExpression(), - forceEntityBased, defaultPredicateJoinType))); - } else if (fs.getOperator() == FilterOperator.AND) { - return backend.and(filterSpecListToPredicateArray(rootMeta, root, fs.getExpression(), forceEntityBased, - defaultPredicateJoinType)); - } else if (fs.getOperator() == FilterOperator.OR) { - return backend.or(filterSpecListToPredicateArray(rootMeta, root, fs.getExpression(), forceEntityBased, - defaultPredicateJoinType)); - } else { - throw new IllegalArgumentException(fs.toString()); - } - } - - public MetaAttributePath enhanceAttributePath(MetaAttributePath attrPath, Object value) { - MetaAttribute attr = attrPath.getLast(); - - MetaType valueType = attr.getType(); - if (valueType instanceof MetaMapType) { - valueType = ((MetaMapType) valueType).getValueType(); - } - - boolean anyType = AnyTypeObject.class.isAssignableFrom(valueType.getImplementationClass()); - if (anyType) { - // we have and AnyType and do need to select the proper attribute of - // the embeddable - MetaAttribute anyAttr = AnyUtils.findAttribute((MetaDataObject) valueType, value); - return attrPath.concat(anyAttr); - } else { - return attrPath; - } - } - - public static MetaDataObject getBaseType(MetaDataObject meta, boolean forceEntityBased) { - if (forceEntityBased && meta instanceof MetaProjection) { - return ((MetaProjection) meta).getBaseType(); - } else { - return meta; - } - } - -} +package io.katharsis.jpa.internal.query; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Set; + +import javax.persistence.criteria.JoinType; + +import io.katharsis.jpa.internal.meta.MetaAttribute; +import io.katharsis.jpa.internal.meta.MetaAttributeFinder; +import io.katharsis.jpa.internal.meta.MetaAttributePath; +import io.katharsis.jpa.internal.meta.MetaDataObject; +import io.katharsis.jpa.internal.meta.MetaMapType; +import io.katharsis.jpa.internal.meta.MetaProjection; +import io.katharsis.jpa.internal.meta.MetaType; +import io.katharsis.jpa.internal.query.backend.JpaQueryBackend; +import io.katharsis.jpa.query.AnyTypeObject; +import io.katharsis.queryspec.FilterOperator; +import io.katharsis.queryspec.FilterSpec; + +public final class QueryFilterBuilder { + + private static final int ORACLE_PARAM_LIMIT = 900; + + private MetaAttributeFinder attributeFinder; + + private JpaQueryBackend backend; + + protected QueryFilterBuilder(final ComputedAttributeRegistryImpl virtualAttrs, JpaQueryBackend backend, MetaAttributeFinder attributeFinder) { + this.backend = backend; + this.attributeFinder = attributeFinder; + } + + public List

filterSpecListToPredicateArray(MetaDataObject rootMeta, F root, List rowFilters) { + return filterSpecListToPredicateArray(rootMeta, root, rowFilters, false, null); + } + + public List

filterSpecListToPredicateArray(MetaDataObject rootMeta, F root, List rowFilters, + boolean forceEntityBased, JoinType defaultPredicateJoinType) { + ArrayList

predicateList = new ArrayList<>(); + for (FilterSpec rowFilter : rowFilters) { + predicateList.add( + filterSpecListToPredicate(rootMeta, root, rowFilter, forceEntityBased, defaultPredicateJoinType)); + } + return predicateList; + } + + protected P filterSpecListToPredicate(MetaDataObject rootMeta, F root, FilterSpec fs) { + return filterSpecListToPredicate(rootMeta, root, fs, false, null); + } + + protected P filterSpecListToPredicate(MetaDataObject rootMeta, F root, FilterSpec fs, boolean forceEntityBased, + JoinType defaultPredicateJoinType) { + if ((fs.getOperator() == FilterOperator.EQ || fs.getOperator() == FilterOperator.NEQ) + && fs.getValue() instanceof Collection && ((Collection) fs.getValue()).size() > ORACLE_PARAM_LIMIT) { + + return filterLargeValueSets(fs, rootMeta, root, forceEntityBased, defaultPredicateJoinType); + } else { + if (fs.hasExpressions()) { + return filterExpressions(fs, rootMeta, root, forceEntityBased, defaultPredicateJoinType); + } + + else { + return filterSimpleOperation(fs, rootMeta, forceEntityBased); + } + } + } + + /** + * Split filters with two many value possibilities. For example, Oracle + * cannot handle more than 1000. + * + * @param fs + * @param rootMeta + * @param root + * @param forceEntityBased + * @param defaultPredicateJoinType + * @return + */ + private P filterLargeValueSets(FilterSpec fs, MetaDataObject rootMeta, F root, boolean forceEntityBased, + JoinType defaultPredicateJoinType) { + ArrayList specs = new ArrayList<>(); + List list = new ArrayList<>((Collection) fs.getValue()); + for (int i = 0; i < list.size(); i += ORACLE_PARAM_LIMIT) { + int nextOffset = i + Math.min(list.size() - i, ORACLE_PARAM_LIMIT); + List batchList = list.subList(i, nextOffset); + specs.add(new FilterSpec(fs.getAttributePath(), fs.getOperator(), batchList)); + } + + FilterSpec orSpec = FilterSpec.or(specs); + return filterSpecListToPredicate(rootMeta, root, orSpec, forceEntityBased, defaultPredicateJoinType); + } + + private P filterSimpleOperation(FilterSpec fs, MetaDataObject rootMeta, boolean forceEntityBased) { + Object value = fs.getValue(); + if (value instanceof Set) { + // HashSet not properly supported in ORM/JDBC, convert to + // list + Set set = (Set) value; + value = new ArrayList(set); + } + MetaDataObject rootBaseType = getBaseType(rootMeta, forceEntityBased); + MetaAttributePath path = rootBaseType.resolvePath(fs.getAttributePath(), attributeFinder); + path = enhanceAttributePath(path, value); + return backend.buildPredicate(fs.getOperator(), path, value); + } + + private P filterExpressions(FilterSpec fs, MetaDataObject rootMeta, F root, boolean forceEntityBased, + JoinType defaultPredicateJoinType) { + // and, or, not. + if (fs.getOperator() == FilterOperator.NOT) { + return backend.not(backend.and(filterSpecListToPredicateArray(rootMeta, root, fs.getExpression(), + forceEntityBased, defaultPredicateJoinType))); + } else if (fs.getOperator() == FilterOperator.AND) { + return backend.and(filterSpecListToPredicateArray(rootMeta, root, fs.getExpression(), forceEntityBased, + defaultPredicateJoinType)); + } else if (fs.getOperator() == FilterOperator.OR) { + return backend.or(filterSpecListToPredicateArray(rootMeta, root, fs.getExpression(), forceEntityBased, + defaultPredicateJoinType)); + } else { + throw new IllegalArgumentException(fs.toString()); + } + } + + public MetaAttributePath enhanceAttributePath(MetaAttributePath attrPath, Object value) { + MetaAttribute attr = attrPath.getLast(); + + MetaType valueType = attr.getType(); + if (valueType instanceof MetaMapType) { + valueType = ((MetaMapType) valueType).getValueType(); + } + + boolean anyType = AnyTypeObject.class.isAssignableFrom(valueType.getImplementationClass()); + if (anyType) { + // we have and AnyType and do need to select the proper attribute of + // the embeddable + MetaAttribute anyAttr = AnyUtils.findAttribute((MetaDataObject) valueType, value); + return attrPath.concat(anyAttr); + } else { + return attrPath; + } + } + + public static MetaDataObject getBaseType(MetaDataObject meta, boolean forceEntityBased) { + if (forceEntityBased && meta instanceof MetaProjection) { + return ((MetaProjection) meta).getBaseType(); + } else { + return meta; + } + } + +} diff --git a/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/query/backend/criteria/JpaCriteriaQueryBackend.java b/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/query/backend/criteria/JpaCriteriaQueryBackend.java index aa868aba..fa27af20 100644 --- a/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/query/backend/criteria/JpaCriteriaQueryBackend.java +++ b/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/query/backend/criteria/JpaCriteriaQueryBackend.java @@ -1,338 +1,338 @@ -package io.katharsis.jpa.internal.query.backend.criteria; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -import javax.persistence.EntityManager; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.CriteriaQuery; -import javax.persistence.criteria.Expression; -import javax.persistence.criteria.From; -import javax.persistence.criteria.JoinType; -import javax.persistence.criteria.MapJoin; -import javax.persistence.criteria.Order; -import javax.persistence.criteria.Path; -import javax.persistence.criteria.Predicate; -import javax.persistence.criteria.Root; -import javax.persistence.criteria.Selection; - -import io.katharsis.jpa.internal.meta.MetaAttribute; -import io.katharsis.jpa.internal.meta.MetaAttributePath; -import io.katharsis.jpa.internal.query.JoinRegistry; -import io.katharsis.jpa.internal.query.MetaVirtualAttribute; -import io.katharsis.jpa.internal.query.QueryUtil; -import io.katharsis.jpa.internal.query.backend.JpaQueryBackend; -import io.katharsis.jpa.query.criteria.JpaCriteriaExpressionFactory; -import io.katharsis.queryspec.Direction; -import io.katharsis.queryspec.FilterOperator; - -public class JpaCriteriaQueryBackend implements JpaQueryBackend, Order, Predicate, Expression> { - - private CriteriaQuery criteriaQuery; - - private JoinRegistry, Expression> joinHelper; - - protected CriteriaBuilder cb; - - private From root; - - private Root parentFrom; - - private JpaCriteriaQueryImpl queryImpl; - - @SuppressWarnings("unchecked") - public JpaCriteriaQueryBackend(JpaCriteriaQueryImpl query, EntityManager em, Class clazz, Class parentEntityClass, - MetaAttribute parentAttr) { - this.queryImpl = query; - - cb = em.getCriteriaBuilder(); - criteriaQuery = (CriteriaQuery) cb.createQuery(); - - if (parentEntityClass != null) { - parentFrom = criteriaQuery.from(parentEntityClass); - root = parentFrom.join(parentAttr.getName()); - joinHelper = new JoinRegistry<>(this, query); - joinHelper.putJoin(new MetaAttributePath(), root); - criteriaQuery.select(root); - } - else { - root = criteriaQuery.from(clazz); - joinHelper = new JoinRegistry<>(this, query); - joinHelper.putJoin(new MetaAttributePath(), root); - criteriaQuery.select(root); - } - } - - @Override - public Expression getAttribute(MetaAttributePath attrPath) { - return joinHelper.getEntityAttribute(attrPath); - } - - @Override - public void addPredicate(Predicate predicate) { - Predicate restriction = criteriaQuery.getRestriction(); - if (restriction != null) { - criteriaQuery.where(restriction, predicate); - } - else { - criteriaQuery.where(predicate); - } - } - - @Override - public From getRoot() { - return root; - } - - @Override - public void setOrder(List list) { - criteriaQuery.orderBy(list); - } - - @Override - public List getOrderList() { - return new ArrayList<>(criteriaQuery.getOrderList()); - } - - @Override - public Order newSort(Expression expr, Direction dir) { - if (dir == Direction.ASC) { - return cb.asc(expr); - } - else { - return cb.desc(expr); - } - } - - public CriteriaBuilder getCriteriaBuilder() { - return cb; - } - - @Override - public void distinct() { - criteriaQuery.distinct(true); - } - - public CriteriaQuery getCriteriaQuery() { - return criteriaQuery; - } - - @Override - public void addParentPredicate(MetaAttribute primaryKeyAttr) { - List parentIds = queryImpl.getParentIds(); - Path parentIdPath = parentFrom.get(primaryKeyAttr.getName()); - addPredicate(parentIdPath.in(new ArrayList(parentIds))); - } - - @Override - public boolean hasManyRootsFetchesOrJoins() { - return QueryUtil.hasManyRootsFetchesOrJoins(criteriaQuery); - } - - @Override - public void addSelection(Expression expression, String name) { - Selection selection = criteriaQuery.getSelection(); - - List> newSelection = new ArrayList<>(); - if (selection != null) { - if (selection.isCompoundSelection()) { - newSelection.addAll(selection.getCompoundSelectionItems()); - } - else { - newSelection.add(selection); - } - } - newSelection.add(expression); - criteriaQuery.multiselect(newSelection); - } - - @Override - public Expression getExpression(Order order) { - return order.getExpression(); - } - - @Override - public boolean containsRelation(Expression expression) { - return QueryUtil.containsRelation(expression); - } - - public Predicate ilike(Expression expr, String val) { - return cb.like(cb.lower(expr), val.toLowerCase()); - } - - private Predicate negateIfNeeded(Predicate p, FilterOperator fc) { - if (fc.equals(FilterOperator.NEQ)) - return cb.not(p); - return p; - } - - @Override - public Predicate buildPredicate(FilterOperator operator, MetaAttributePath attrPath, Object value) { - Expression attr = getAttribute(attrPath); - return buildPredicate(operator, attr, value); - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - public Predicate buildPredicate(FilterOperator operator, Expression expressionObj, Object value) { - Expression expression = expressionObj; - - expression = handleConversions(expression, operator); - - if (expression instanceof Predicate && expression.getJavaType() == Boolean.class && operator == FilterOperator.EQ) { - return handleBoolean(expression, value); - } - - return handle(expression, operator, value); - - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - private Predicate handleBoolean(Expression expression, Object value) { - if (value.equals(Boolean.TRUE)) { - return (Predicate) expression; - } - else { - return cb.not(expression); - } - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - private Predicate handle(Expression expression, FilterOperator operator, Object value) { - if (operator == FilterOperator.EQ || operator == FilterOperator.NEQ) { - return handleEquals(expression, operator, value); - } - else if (operator ==FilterOperator.LIKE) { - return ilike(expression, value.toString()); - } - else if (operator == FilterOperator.GT) { - return cb.greaterThan(expression, (Comparable) value); - } - else if (operator == FilterOperator.LT) { - return cb.lessThan(expression, (Comparable) value); - } - else if (operator == FilterOperator.GE) { - return cb.greaterThanOrEqualTo(expression, (Comparable) value); - } - else if (operator == FilterOperator.LE) { - return cb.lessThanOrEqualTo(expression, (Comparable) value); - } - else { - throw new IllegalStateException("unexpected operator " + operator); - } - - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - private Predicate handleEquals(Expression expression, FilterOperator operator, Object value) { - if (value instanceof List) { - Predicate p = expression.in(((List) value).toArray()); - return negateIfNeeded(p, operator); - } - else if (Collection.class.isAssignableFrom(expression.getJavaType())) { - Predicate p = cb.literal(value).in(expression); - return negateIfNeeded(p, operator); - } - else if (expression instanceof MapJoin) { - Predicate p = cb.literal(value).in(((MapJoin) expression).value()); - return negateIfNeeded(p, operator); - } - else if (value == null) { - return negateIfNeeded(cb.isNull(expression), operator); - } - return negateIfNeeded(cb.equal(expression, value), operator); - } - - private Expression handleConversions(Expression expression, FilterOperator operator) { - // convert to String for LIKE operators - if (expression.getJavaType() != String.class && (operator == FilterOperator.LIKE)) { - return expression.as(String.class); - } - else { - return expression; - } - } - - @Override - public Predicate and(List predicates) { - return cb.and(predicates.toArray(new Predicate[predicates.size()])); - } - - @Override - public Predicate not(Predicate predicate) { - return cb.not(predicate); - } - - @Override - public Predicate or(List predicates) { - return cb.or(predicates.toArray(new Predicate[predicates.size()])); - } - - @Override - public Expression joinMapValue(Expression currentCriteriaPath, MetaAttribute pathElement, Object key) { - MapJoin mapJoin = ((From) currentCriteriaPath).joinMap(pathElement.getName(), - JoinType.LEFT); - Predicate mapJoinCondition = cb.equal(mapJoin.key(), key); - Predicate nullCondition = cb.isNull(mapJoin.key()); - addPredicate(cb.or(mapJoinCondition, nullCondition)); - return mapJoin; - } - - @Override - public Expression joinMapValues(Expression currentCriteriaPath, MetaAttribute mapPathElement) { - MapJoin mapJoin = ((From) currentCriteriaPath).joinMap(mapPathElement.getName(), - JoinType.LEFT); - return mapJoin.value(); - } - - @Override - public Expression joinMapKey(Expression currentCriteriaPath, MetaAttribute mapPathElement) { - MapJoin mapJoin = ((From) currentCriteriaPath).joinMap(mapPathElement.getName(), - JoinType.LEFT); - return mapJoin.key(); - } - - @Override - public Class getJavaElementType(Expression currentCriteriaPath) { - return currentCriteriaPath.getJavaType(); - } - - @Override - @SuppressWarnings({ "rawtypes", "unchecked" }) - public Expression getAttribute(Expression currentCriteriaPath, MetaAttribute pathElement) { - if (pathElement instanceof MetaVirtualAttribute) { - MetaVirtualAttribute projAttr = (MetaVirtualAttribute) pathElement; - JpaCriteriaExpressionFactory expressionFactory = (JpaCriteriaExpressionFactory) queryImpl.getVirtualAttrs() - .get(projAttr); - - From from = (From) currentCriteriaPath; - return expressionFactory.getExpression(from, getCriteriaQuery()); - } - else { - return ((Path) currentCriteriaPath).get(pathElement.getName()); - } - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - @Override - public Expression joinSubType(Expression currentCriteriaPath, Class entityType) { - return cb.treat((Path) currentCriteriaPath, (Class) entityType); - } - - @SuppressWarnings("unchecked") - @Override - public From doJoin(MetaAttribute targetAttr, JoinType joinType, From parent) { - if (targetAttr instanceof MetaVirtualAttribute) { - MetaVirtualAttribute projAttr = (MetaVirtualAttribute) targetAttr; - @SuppressWarnings("rawtypes") - JpaCriteriaExpressionFactory expressionFactory = (JpaCriteriaExpressionFactory) queryImpl.getVirtualAttrs() - .get(projAttr); - - return (From) expressionFactory.getExpression(parent, getCriteriaQuery()); - } - else { - return parent.join(targetAttr.getName(), joinType); - } - } - +package io.katharsis.jpa.internal.query.backend.criteria; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import javax.persistence.EntityManager; +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Expression; +import javax.persistence.criteria.From; +import javax.persistence.criteria.JoinType; +import javax.persistence.criteria.MapJoin; +import javax.persistence.criteria.Order; +import javax.persistence.criteria.Path; +import javax.persistence.criteria.Predicate; +import javax.persistence.criteria.Root; +import javax.persistence.criteria.Selection; + +import io.katharsis.jpa.internal.meta.MetaAttribute; +import io.katharsis.jpa.internal.meta.MetaAttributePath; +import io.katharsis.jpa.internal.query.JoinRegistry; +import io.katharsis.jpa.internal.query.MetaComputedAttribute; +import io.katharsis.jpa.internal.query.QueryUtil; +import io.katharsis.jpa.internal.query.backend.JpaQueryBackend; +import io.katharsis.jpa.query.criteria.JpaCriteriaExpressionFactory; +import io.katharsis.queryspec.Direction; +import io.katharsis.queryspec.FilterOperator; + +public class JpaCriteriaQueryBackend implements JpaQueryBackend, Order, Predicate, Expression> { + + private CriteriaQuery criteriaQuery; + + private JoinRegistry, Expression> joinHelper; + + protected CriteriaBuilder cb; + + private From root; + + private Root parentFrom; + + private JpaCriteriaQueryImpl queryImpl; + + @SuppressWarnings("unchecked") + public JpaCriteriaQueryBackend(JpaCriteriaQueryImpl query, EntityManager em, Class clazz, Class parentEntityClass, + MetaAttribute parentAttr) { + this.queryImpl = query; + + cb = em.getCriteriaBuilder(); + criteriaQuery = (CriteriaQuery) cb.createQuery(); + + if (parentEntityClass != null) { + parentFrom = criteriaQuery.from(parentEntityClass); + root = parentFrom.join(parentAttr.getName()); + joinHelper = new JoinRegistry<>(this, query); + joinHelper.putJoin(new MetaAttributePath(), root); + criteriaQuery.select(root); + } + else { + root = criteriaQuery.from(clazz); + joinHelper = new JoinRegistry<>(this, query); + joinHelper.putJoin(new MetaAttributePath(), root); + criteriaQuery.select(root); + } + } + + @Override + public Expression getAttribute(MetaAttributePath attrPath) { + return joinHelper.getEntityAttribute(attrPath); + } + + @Override + public void addPredicate(Predicate predicate) { + Predicate restriction = criteriaQuery.getRestriction(); + if (restriction != null) { + criteriaQuery.where(restriction, predicate); + } + else { + criteriaQuery.where(predicate); + } + } + + @Override + public From getRoot() { + return root; + } + + @Override + public void setOrder(List list) { + criteriaQuery.orderBy(list); + } + + @Override + public List getOrderList() { + return new ArrayList<>(criteriaQuery.getOrderList()); + } + + @Override + public Order newSort(Expression expr, Direction dir) { + if (dir == Direction.ASC) { + return cb.asc(expr); + } + else { + return cb.desc(expr); + } + } + + public CriteriaBuilder getCriteriaBuilder() { + return cb; + } + + @Override + public void distinct() { + criteriaQuery.distinct(true); + } + + public CriteriaQuery getCriteriaQuery() { + return criteriaQuery; + } + + @Override + public void addParentPredicate(MetaAttribute primaryKeyAttr) { + List parentIds = queryImpl.getParentIds(); + Path parentIdPath = parentFrom.get(primaryKeyAttr.getName()); + addPredicate(parentIdPath.in(new ArrayList(parentIds))); + } + + @Override + public boolean hasManyRootsFetchesOrJoins() { + return QueryUtil.hasManyRootsFetchesOrJoins(criteriaQuery); + } + + @Override + public void addSelection(Expression expression, String name) { + Selection selection = criteriaQuery.getSelection(); + + List> newSelection = new ArrayList<>(); + if (selection != null) { + if (selection.isCompoundSelection()) { + newSelection.addAll(selection.getCompoundSelectionItems()); + } + else { + newSelection.add(selection); + } + } + newSelection.add(expression); + criteriaQuery.multiselect(newSelection); + } + + @Override + public Expression getExpression(Order order) { + return order.getExpression(); + } + + @Override + public boolean containsRelation(Expression expression) { + return QueryUtil.containsRelation(expression); + } + + public Predicate ilike(Expression expr, String val) { + return cb.like(cb.lower(expr), val.toLowerCase()); + } + + private Predicate negateIfNeeded(Predicate p, FilterOperator fc) { + if (fc.equals(FilterOperator.NEQ)) + return cb.not(p); + return p; + } + + @Override + public Predicate buildPredicate(FilterOperator operator, MetaAttributePath attrPath, Object value) { + Expression attr = getAttribute(attrPath); + return buildPredicate(operator, attr, value); + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + public Predicate buildPredicate(FilterOperator operator, Expression expressionObj, Object value) { + Expression expression = expressionObj; + + expression = handleConversions(expression, operator); + + if (expression instanceof Predicate && expression.getJavaType() == Boolean.class && operator == FilterOperator.EQ) { + return handleBoolean(expression, value); + } + + return handle(expression, operator, value); + + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + private Predicate handleBoolean(Expression expression, Object value) { + if (value.equals(Boolean.TRUE)) { + return (Predicate) expression; + } + else { + return cb.not(expression); + } + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + private Predicate handle(Expression expression, FilterOperator operator, Object value) { + if (operator == FilterOperator.EQ || operator == FilterOperator.NEQ) { + return handleEquals(expression, operator, value); + } + else if (operator ==FilterOperator.LIKE) { + return ilike(expression, value.toString()); + } + else if (operator == FilterOperator.GT) { + return cb.greaterThan(expression, (Comparable) value); + } + else if (operator == FilterOperator.LT) { + return cb.lessThan(expression, (Comparable) value); + } + else if (operator == FilterOperator.GE) { + return cb.greaterThanOrEqualTo(expression, (Comparable) value); + } + else if (operator == FilterOperator.LE) { + return cb.lessThanOrEqualTo(expression, (Comparable) value); + } + else { + throw new IllegalStateException("unexpected operator " + operator); + } + + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + private Predicate handleEquals(Expression expression, FilterOperator operator, Object value) { + if (value instanceof List) { + Predicate p = expression.in(((List) value).toArray()); + return negateIfNeeded(p, operator); + } + else if (Collection.class.isAssignableFrom(expression.getJavaType())) { + Predicate p = cb.literal(value).in(expression); + return negateIfNeeded(p, operator); + } + else if (expression instanceof MapJoin) { + Predicate p = cb.literal(value).in(((MapJoin) expression).value()); + return negateIfNeeded(p, operator); + } + else if (value == null) { + return negateIfNeeded(cb.isNull(expression), operator); + } + return negateIfNeeded(cb.equal(expression, value), operator); + } + + private Expression handleConversions(Expression expression, FilterOperator operator) { + // convert to String for LIKE operators + if (expression.getJavaType() != String.class && (operator == FilterOperator.LIKE)) { + return expression.as(String.class); + } + else { + return expression; + } + } + + @Override + public Predicate and(List predicates) { + return cb.and(predicates.toArray(new Predicate[predicates.size()])); + } + + @Override + public Predicate not(Predicate predicate) { + return cb.not(predicate); + } + + @Override + public Predicate or(List predicates) { + return cb.or(predicates.toArray(new Predicate[predicates.size()])); + } + + @Override + public Expression joinMapValue(Expression currentCriteriaPath, MetaAttribute pathElement, Object key) { + MapJoin mapJoin = ((From) currentCriteriaPath).joinMap(pathElement.getName(), + JoinType.LEFT); + Predicate mapJoinCondition = cb.equal(mapJoin.key(), key); + Predicate nullCondition = cb.isNull(mapJoin.key()); + addPredicate(cb.or(mapJoinCondition, nullCondition)); + return mapJoin; + } + + @Override + public Expression joinMapValues(Expression currentCriteriaPath, MetaAttribute mapPathElement) { + MapJoin mapJoin = ((From) currentCriteriaPath).joinMap(mapPathElement.getName(), + JoinType.LEFT); + return mapJoin.value(); + } + + @Override + public Expression joinMapKey(Expression currentCriteriaPath, MetaAttribute mapPathElement) { + MapJoin mapJoin = ((From) currentCriteriaPath).joinMap(mapPathElement.getName(), + JoinType.LEFT); + return mapJoin.key(); + } + + @Override + public Class getJavaElementType(Expression currentCriteriaPath) { + return currentCriteriaPath.getJavaType(); + } + + @Override + @SuppressWarnings({ "rawtypes", "unchecked" }) + public Expression getAttribute(Expression currentCriteriaPath, MetaAttribute pathElement) { + if (pathElement instanceof MetaComputedAttribute) { + MetaComputedAttribute projAttr = (MetaComputedAttribute) pathElement; + JpaCriteriaExpressionFactory expressionFactory = (JpaCriteriaExpressionFactory) queryImpl.getComputedAttrs() + .get(projAttr); + + From from = (From) currentCriteriaPath; + return expressionFactory.getExpression(from, getCriteriaQuery()); + } + else { + return ((Path) currentCriteriaPath).get(pathElement.getName()); + } + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + @Override + public Expression joinSubType(Expression currentCriteriaPath, Class entityType) { + return cb.treat((Path) currentCriteriaPath, (Class) entityType); + } + + @SuppressWarnings("unchecked") + @Override + public From doJoin(MetaAttribute targetAttr, JoinType joinType, From parent) { + if (targetAttr instanceof MetaComputedAttribute) { + MetaComputedAttribute projAttr = (MetaComputedAttribute) targetAttr; + @SuppressWarnings("rawtypes") + JpaCriteriaExpressionFactory expressionFactory = (JpaCriteriaExpressionFactory) queryImpl.getComputedAttrs() + .get(projAttr); + + return (From) expressionFactory.getExpression(parent, getCriteriaQuery()); + } + else { + return parent.join(targetAttr.getName(), joinType); + } + } + } \ No newline at end of file diff --git a/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/query/backend/criteria/JpaCriteriaQueryExecutorImpl.java b/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/query/backend/criteria/JpaCriteriaQueryExecutorImpl.java index 81e1acca..cb70aeca 100644 --- a/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/query/backend/criteria/JpaCriteriaQueryExecutorImpl.java +++ b/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/query/backend/criteria/JpaCriteriaQueryExecutorImpl.java @@ -1,105 +1,107 @@ -package io.katharsis.jpa.internal.query.backend.criteria; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import javax.persistence.EntityManager; -import javax.persistence.Tuple; -import javax.persistence.TypedQuery; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.CriteriaQuery; -import javax.persistence.criteria.Expression; -import javax.persistence.criteria.Order; -import javax.persistence.criteria.Root; -import javax.persistence.criteria.Selection; - -import io.katharsis.jpa.internal.meta.MetaDataObject; -import io.katharsis.jpa.internal.query.AbstractQueryExecutorImpl; -import io.katharsis.jpa.internal.query.QueryUtil; -import io.katharsis.jpa.query.criteria.JpaCriteriaQueryExecutor; - -public class JpaCriteriaQueryExecutorImpl extends AbstractQueryExecutorImpl implements JpaCriteriaQueryExecutor { - - private CriteriaQuery query; - - public JpaCriteriaQueryExecutorImpl(EntityManager em, MetaDataObject meta, CriteriaQuery criteriaQuery, - int numAutoSelections, Map selectionBindings) { - super(em, meta, numAutoSelections, selectionBindings); - - this.query = criteriaQuery; - } - - public CriteriaQuery getQuery() { - return query; - } - - @Override - protected List executeQuery() { - TypedQuery typedQuery = em.createQuery(query); - return executeQuery(typedQuery); - } - - @Override - protected boolean isCompoundSelection() { - return query.getSelection().isCompoundSelection(); - } - - @Override - protected boolean isDistinct() { - return query.isDistinct(); - } - - @Override - protected boolean hasManyRootsFetchesOrJoins() { - return QueryUtil.hasManyRootsFetchesOrJoins(query); - } - - @Override - @SuppressWarnings({ "rawtypes" }) - public long getTotalRowCount() { - Selection selection = query.getSelection(); - List orderList = query.getOrderList(); - try { - CriteriaBuilder builder = em.getCriteriaBuilder(); - Expression countExpr; - - Set> roots = query.getRoots(); - if (roots.size() != 1) { - throw new IllegalStateException("cannot compute totalRowCount in case of multiple query roots"); - } - if (!query.getGroupList().isEmpty()) { - throw new IllegalStateException("cannot compute totalRowCount for grouped queries"); - } - - // transform query to a count query - Root root = roots.iterator().next(); - countExpr = builder.count(root); - query.multiselect(countExpr); - query.orderBy(new ArrayList()); - TypedQuery countQuery = em.createQuery(query); - - return (Long) countQuery.getSingleResult(); - } - finally { - // transform count query back to regular query - query.multiselect(selection); - query.orderBy(orderList); - } - } - - @Override - public List getResultTuples() { - TypedQuery typedQuery = em.createQuery(query); - List results = executeQuery(); - List tuples = new ArrayList<>(); - for (Object result : results) { - if (!(result instanceof Object[])) { - throw new IllegalStateException("not a tuple result: " + result); - } - tuples.add(new CriteriaTupleImpl((Object[]) result, selectionBindings)); - } - return tuples; - } -} +package io.katharsis.jpa.internal.query.backend.criteria; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.persistence.EntityManager; +import javax.persistence.Tuple; +import javax.persistence.TypedQuery; +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Expression; +import javax.persistence.criteria.Order; +import javax.persistence.criteria.Root; +import javax.persistence.criteria.Selection; + +import io.katharsis.jpa.internal.meta.MetaDataObject; +import io.katharsis.jpa.internal.query.AbstractQueryExecutorImpl; +import io.katharsis.jpa.internal.query.QueryUtil; +import io.katharsis.jpa.internal.query.backend.querydsl.SingleObjectTupleImpl; +import io.katharsis.jpa.query.criteria.JpaCriteriaQueryExecutor; + +public class JpaCriteriaQueryExecutorImpl extends AbstractQueryExecutorImpl implements JpaCriteriaQueryExecutor { + + private CriteriaQuery query; + + public JpaCriteriaQueryExecutorImpl(EntityManager em, MetaDataObject meta, CriteriaQuery criteriaQuery, + int numAutoSelections, Map selectionBindings) { + super(em, meta, numAutoSelections, selectionBindings); + + this.query = criteriaQuery; + } + + public CriteriaQuery getQuery() { + return query; + } + + @Override + protected List executeQuery() { + TypedQuery typedQuery = em.createQuery(query); + return executeQuery(typedQuery); + } + + @Override + protected boolean isCompoundSelection() { + return query.getSelection().isCompoundSelection(); + } + + @Override + protected boolean isDistinct() { + return query.isDistinct(); + } + + @Override + protected boolean hasManyRootsFetchesOrJoins() { + return QueryUtil.hasManyRootsFetchesOrJoins(query); + } + + @Override + @SuppressWarnings({ "rawtypes" }) + public long getTotalRowCount() { + Selection selection = query.getSelection(); + List orderList = query.getOrderList(); + try { + CriteriaBuilder builder = em.getCriteriaBuilder(); + Expression countExpr; + + Set> roots = query.getRoots(); + if (roots.size() != 1) { + throw new IllegalStateException("cannot compute totalRowCount in case of multiple query roots"); + } + if (!query.getGroupList().isEmpty()) { + throw new IllegalStateException("cannot compute totalRowCount for grouped queries"); + } + + // transform query to a count query + Root root = roots.iterator().next(); + countExpr = builder.count(root); + query.multiselect(countExpr); + query.orderBy(new ArrayList()); + TypedQuery countQuery = em.createQuery(query); + + return (Long) countQuery.getSingleResult(); + } + finally { + // transform count query back to regular query + query.multiselect(selection); + query.orderBy(orderList); + } + } + + @Override + public List getResultTuples() { + List results = executeQuery(); + List tuples = new ArrayList<>(); + for (Object result : results) { + if (result instanceof Object[]) { + tuples.add(new CriteriaTupleImpl((Object[]) result, selectionBindings)); + } + else { + tuples.add(new SingleObjectTupleImpl(result)); + } + } + return tuples; + } +} diff --git a/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/query/backend/criteria/JpaCriteriaQueryImpl.java b/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/query/backend/criteria/JpaCriteriaQueryImpl.java index ed204d74..0b81ae6c 100644 --- a/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/query/backend/criteria/JpaCriteriaQueryImpl.java +++ b/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/query/backend/criteria/JpaCriteriaQueryImpl.java @@ -1,45 +1,45 @@ -package io.katharsis.jpa.internal.query.backend.criteria; - -import java.util.List; -import java.util.Map; - -import javax.persistence.EntityManager; -import javax.persistence.criteria.CriteriaQuery; - -import io.katharsis.jpa.internal.meta.MetaLookup; -import io.katharsis.jpa.internal.query.AbstractJpaQueryImpl; -import io.katharsis.jpa.internal.query.VirtualAttributeRegistry; -import io.katharsis.jpa.query.criteria.JpaCriteriaQuery; - -public class JpaCriteriaQueryImpl extends AbstractJpaQueryImpl> - implements JpaCriteriaQuery { - - public JpaCriteriaQueryImpl(MetaLookup metaLookup, EntityManager em, Class clazz, - VirtualAttributeRegistry virtualAttrs) { - super(metaLookup, em, clazz, virtualAttrs); - } - - public JpaCriteriaQueryImpl(MetaLookup metaLookup, EntityManager em, Class clazz, - VirtualAttributeRegistry virtualAttrs, String attrName, List entityIds) { - super(metaLookup, em, clazz, virtualAttrs, attrName, entityIds); - } - - public CriteriaQuery buildQuery() { - return buildExecutor().getQuery(); - } - - @Override - public JpaCriteriaQueryExecutorImpl buildExecutor() { - return (JpaCriteriaQueryExecutorImpl) super.buildExecutor(); - } - - @Override - protected JpaCriteriaQueryBackend newBackend() { - return new JpaCriteriaQueryBackend<>(this, em, clazz, parentEntityClass, parentAttr); - } - - @Override - protected JpaCriteriaQueryExecutorImpl newExecutor(JpaCriteriaQueryBackend ctx, int numAutoSelections, Map selectionBindings) { - return new JpaCriteriaQueryExecutorImpl<>(em, meta, ctx.getCriteriaQuery(), numAutoSelections, selectionBindings); - } -} +package io.katharsis.jpa.internal.query.backend.criteria; + +import java.util.List; +import java.util.Map; + +import javax.persistence.EntityManager; +import javax.persistence.criteria.CriteriaQuery; + +import io.katharsis.jpa.internal.meta.MetaLookup; +import io.katharsis.jpa.internal.query.AbstractJpaQueryImpl; +import io.katharsis.jpa.internal.query.ComputedAttributeRegistryImpl; +import io.katharsis.jpa.query.criteria.JpaCriteriaQuery; + +public class JpaCriteriaQueryImpl extends AbstractJpaQueryImpl> + implements JpaCriteriaQuery { + + public JpaCriteriaQueryImpl(MetaLookup metaLookup, EntityManager em, Class clazz, + ComputedAttributeRegistryImpl virtualAttrs) { + super(metaLookup, em, clazz, virtualAttrs); + } + + public JpaCriteriaQueryImpl(MetaLookup metaLookup, EntityManager em, Class clazz, + ComputedAttributeRegistryImpl virtualAttrs, String attrName, List entityIds) { + super(metaLookup, em, clazz, virtualAttrs, attrName, entityIds); + } + + public CriteriaQuery buildQuery() { + return buildExecutor().getQuery(); + } + + @Override + public JpaCriteriaQueryExecutorImpl buildExecutor() { + return (JpaCriteriaQueryExecutorImpl) super.buildExecutor(); + } + + @Override + protected JpaCriteriaQueryBackend newBackend() { + return new JpaCriteriaQueryBackend<>(this, em, clazz, parentEntityClass, parentAttr); + } + + @Override + protected JpaCriteriaQueryExecutorImpl newExecutor(JpaCriteriaQueryBackend ctx, int numAutoSelections, Map selectionBindings) { + return new JpaCriteriaQueryExecutorImpl<>(em, meta, ctx.getCriteriaQuery(), numAutoSelections, selectionBindings); + } +} diff --git a/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/query/backend/querydsl/QuerydslExecutorImpl.java b/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/query/backend/querydsl/QuerydslExecutorImpl.java index e1cb06ed..f496fe1f 100644 --- a/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/query/backend/querydsl/QuerydslExecutorImpl.java +++ b/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/query/backend/querydsl/QuerydslExecutorImpl.java @@ -1,76 +1,78 @@ -package io.katharsis.jpa.internal.query.backend.querydsl; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -import javax.persistence.EntityManager; -import javax.persistence.Query; - -import com.querydsl.core.Tuple; -import com.querydsl.core.types.QTuple; -import com.querydsl.jpa.impl.JPAQuery; - -import io.katharsis.jpa.internal.meta.MetaDataObject; -import io.katharsis.jpa.internal.query.AbstractQueryExecutorImpl; -import io.katharsis.jpa.query.querydsl.QuerydslExecutor; -import io.katharsis.jpa.query.querydsl.QuerydslTuple; - -public class QuerydslExecutorImpl extends AbstractQueryExecutorImpl implements QuerydslExecutor { - - private JPAQuery query; - - public QuerydslExecutorImpl(EntityManager em, MetaDataObject meta, JPAQuery query, int numAutoSelections, - Map selectionBindings) { - super(em, meta, numAutoSelections, selectionBindings); - - this.query = query; - } - - public JPAQuery getQuery() { - return query; - } - - @Override - protected List executeQuery() { - Query jpaQuery = query.createQuery(); - return executeQuery(jpaQuery); - } - - @Override - protected boolean isCompoundSelection() { - return query.getMetadata().getProjection() instanceof QTuple; - } - - @Override - protected boolean isDistinct() { - return query.getMetadata().isDistinct(); - } - - @Override - protected boolean hasManyRootsFetchesOrJoins() { - return QuerydslUtils.hasManyRootsFetchesOrJoins(query); - } - - /** - * Returns the row count for the query. - */ - @Override - public long getTotalRowCount() { - return query.fetchCount(); - } - - @Override - public List getResultTuples() { - List results = executeQuery(); - - List tuples = new ArrayList<>(); - for (Object result : results) { - if (!(result instanceof Tuple)) { - throw new IllegalStateException("not a tuple result: " + result); - } - tuples.add(new QuerydslTupleImpl((Tuple) result, selectionBindings)); - } - return tuples; - } -} +package io.katharsis.jpa.internal.query.backend.querydsl; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import javax.persistence.EntityManager; +import javax.persistence.Query; + +import com.querydsl.core.Tuple; +import com.querydsl.core.types.QTuple; +import com.querydsl.jpa.impl.JPAQuery; + +import io.katharsis.jpa.internal.meta.MetaDataObject; +import io.katharsis.jpa.internal.query.AbstractQueryExecutorImpl; +import io.katharsis.jpa.query.querydsl.QuerydslExecutor; +import io.katharsis.jpa.query.querydsl.QuerydslTuple; + +public class QuerydslExecutorImpl extends AbstractQueryExecutorImpl implements QuerydslExecutor { + + private JPAQuery query; + + public QuerydslExecutorImpl(EntityManager em, MetaDataObject meta, JPAQuery query, int numAutoSelections, + Map selectionBindings) { + super(em, meta, numAutoSelections, selectionBindings); + + this.query = query; + } + + public JPAQuery getQuery() { + return query; + } + + @Override + protected List executeQuery() { + Query jpaQuery = query.createQuery(); + return executeQuery(jpaQuery); + } + + @Override + protected boolean isCompoundSelection() { + return query.getMetadata().getProjection() instanceof QTuple; + } + + @Override + protected boolean isDistinct() { + return query.getMetadata().isDistinct(); + } + + @Override + protected boolean hasManyRootsFetchesOrJoins() { + return QuerydslUtils.hasManyRootsFetchesOrJoins(query); + } + + /** + * Returns the row count for the query. + */ + @Override + public long getTotalRowCount() { + return query.fetchCount(); + } + + @Override + public List getResultTuples() { + List results = executeQuery(); + + List tuples = new ArrayList<>(); + for (Object result : results) { + if (result instanceof Tuple) { + tuples.add(new QuerydslTupleImpl((Tuple) result, selectionBindings)); + } + else { + tuples.add(new SingleObjectTupleImpl(result)); + } + } + return tuples; + } +} diff --git a/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/query/backend/querydsl/QuerydslQueryBackend.java b/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/query/backend/querydsl/QuerydslQueryBackend.java index 77e1a70a..3ec7c2f1 100644 --- a/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/query/backend/querydsl/QuerydslQueryBackend.java +++ b/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/query/backend/querydsl/QuerydslQueryBackend.java @@ -1,386 +1,400 @@ -package io.katharsis.jpa.internal.query.backend.querydsl; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -import javax.annotation.Nullable; -import javax.persistence.criteria.JoinType; - -import com.google.common.collect.ImmutableList; -import com.querydsl.core.types.CollectionExpression; -import com.querydsl.core.types.EntityPath; -import com.querydsl.core.types.Expression; -import com.querydsl.core.types.ExpressionUtils; -import com.querydsl.core.types.OperationImpl; -import com.querydsl.core.types.Ops; -import com.querydsl.core.types.Order; -import com.querydsl.core.types.OrderSpecifier; -import com.querydsl.core.types.Path; -import com.querydsl.core.types.Predicate; -import com.querydsl.core.types.QTuple; -import com.querydsl.core.types.dsl.BeanPath; -import com.querydsl.core.types.dsl.CollectionExpressionBase; -import com.querydsl.core.types.dsl.ComparableExpression; -import com.querydsl.core.types.dsl.LiteralExpression; -import com.querydsl.core.types.dsl.MapExpressionBase; -import com.querydsl.core.types.dsl.MapPath; -import com.querydsl.core.types.dsl.NumberExpression; -import com.querydsl.core.types.dsl.SimpleExpression; -import com.querydsl.core.types.dsl.StringExpression; -import com.querydsl.jpa.impl.JPAQuery; -import com.querydsl.jpa.impl.JPAQueryFactory; - -import io.katharsis.jpa.internal.meta.MetaAttribute; -import io.katharsis.jpa.internal.meta.MetaAttributePath; -import io.katharsis.jpa.internal.query.JoinRegistry; -import io.katharsis.jpa.internal.query.MetaVirtualAttribute; -import io.katharsis.jpa.internal.query.QueryUtil; -import io.katharsis.jpa.internal.query.VirtualAttributeRegistry; -import io.katharsis.jpa.internal.query.backend.JpaQueryBackend; -import io.katharsis.jpa.query.querydsl.QuerydslExpressionFactory; -import io.katharsis.queryspec.Direction; -import io.katharsis.queryspec.FilterOperator; - -@SuppressWarnings({ "rawtypes", "unchecked" }) -public class QuerydslQueryBackend implements JpaQueryBackend, OrderSpecifier, Predicate, Expression> { - - private JoinRegistry, Expression> joinHelper; - - private Path root; - - private EntityPath parentFrom; - - private QuerydslQueryImpl queryImpl; - - private JPAQuery querydslQuery; - - private List> orderList = new ArrayList<>(); - - public QuerydslQueryBackend(QuerydslQueryImpl queryImpl, Class clazz, Class parentEntityClass, - MetaAttribute parentAttr) { - this.queryImpl = queryImpl; - - JPAQueryFactory queryFactory = queryImpl.getQueryFactory(); - - if (parentEntityClass != null) { - parentFrom = QuerydslUtils.getEntityPath(parentEntityClass); - root = QuerydslUtils.getEntityPath(clazz); - - Path joinPath = (Path) QuerydslUtils.get(parentFrom, parentAttr.getName()); - joinHelper = new JoinRegistry<>(this, queryImpl); - - joinHelper.putJoin(new MetaAttributePath(), root); - querydslQuery = queryFactory.select(root); - querydslQuery = querydslQuery.from(parentFrom); - if (joinPath instanceof CollectionExpression) { - querydslQuery = querydslQuery.join((CollectionExpression) joinPath, root); - } - else { - querydslQuery = querydslQuery.join((EntityPath) joinPath, root); - } - } - else { - root = QuerydslUtils.getEntityPath(clazz); - joinHelper = new JoinRegistry<>(this, queryImpl); - joinHelper.putJoin(new MetaAttributePath(), root); - querydslQuery = queryFactory.select(root); - querydslQuery = querydslQuery.from((EntityPath) root); - } - } - - public JPAQuery getQuery() { - JPAQuery finalQuery = querydslQuery; - for (OrderSpecifier order : orderList) { - finalQuery = finalQuery.orderBy(order); - } - return finalQuery; - } - - @Override - public Expression getAttribute(MetaAttributePath attrPath) { - return joinHelper.getEntityAttribute(attrPath); - } - - @Override - public void addPredicate(Predicate predicate) { - querydslQuery = querydslQuery.where(predicate); - } - - @Override - public Path getRoot() { - return root; - } - - @Override - public void setOrder(List> list) { - this.orderList = list; - } - - @Override - public List> getOrderList() { - return orderList; - } - - @Override - public OrderSpecifier newSort(Expression expr, Direction dir) { - if (dir == Direction.ASC) { - return new OrderSpecifier(Order.ASC, expr); - } - else { - return new OrderSpecifier(Order.DESC, expr); - } - } - - @Override - public void distinct() { - querydslQuery = querydslQuery.distinct(); - } - - @Override - public void addParentPredicate(MetaAttribute primaryKeyAttr) { - List parentIds = queryImpl.getParentIds(); - SimpleExpression parentIdPath = (SimpleExpression) QuerydslUtils.get(parentFrom, primaryKeyAttr.getName()); - addPredicate(parentIdPath.in((List) parentIds)); - } - - @Override - public boolean hasManyRootsFetchesOrJoins() { - return QuerydslUtils.hasManyRootsFetchesOrJoins(querydslQuery); - } - - @Override - public void addSelection(Expression expression, String name) { - Expression selection = querydslQuery.getMetadata().getProjection(); - - List> newSelection = new ArrayList<>(); - if (selection != null) { - if (selection instanceof QTuple) { - newSelection.addAll(((QTuple) selection).getArgs()); - } - else { - newSelection.add(selection); - } - } - newSelection.add(expression); - querydslQuery = (JPAQuery) querydslQuery.select(newSelection.toArray(new Expression[newSelection.size()])); - } - - @Override - public Expression getExpression(OrderSpecifier order) { - return order.getTarget(); - } - - @Override - public boolean containsRelation(Expression expression) { - return QueryUtil.containsRelation(expression); - } - - @Override - public Predicate buildPredicate(FilterOperator operator, MetaAttributePath attrPath, Object value) { - Expression attr = getAttribute(attrPath); - return buildPredicate(operator, attr, value); - } - - public Predicate buildPredicate(FilterOperator operator, Expression expressionObj, Object value) { - Expression expression = expressionObj; - - expression = handleConversions(expression, operator); - - return handle(expression, operator, value); - - } - - private Predicate handle(Expression expression, FilterOperator operator, Object value) { // NOSONAR - if (operator == FilterOperator.EQ || operator == FilterOperator.NEQ) { - return handleEquals(expression, operator, value); - } - else if (operator == FilterOperator.LIKE) { - return ((StringExpression) expression).lower().like(value.toString().toLowerCase()); - } - else if (operator == FilterOperator.GT) { - if (expression instanceof NumberExpression) { - return ((NumberExpression) expression).gt((Number) value); - } - else { - return ((ComparableExpression) expression).gt((Comparable) value); - } - } - else if (operator == FilterOperator.LT) { - if (expression instanceof NumberExpression) { - return ((NumberExpression) expression).lt((Number) value); - } - else { - return ((ComparableExpression) expression).lt((Comparable) value); - } - } - else if (operator == FilterOperator.GE) { - if (expression instanceof NumberExpression) { - return ((NumberExpression) expression).goe((Number) value); - } - else { - return ((ComparableExpression) expression).goe((Comparable) value); - } - } - else if (operator == FilterOperator.LE) { - if (expression instanceof NumberExpression) { - return ((NumberExpression) expression).loe((Number) value); - } - else { - return ((ComparableExpression) expression).loe((Comparable) value); - } - } - else { - throw new IllegalStateException("unexpected operator " + operator); - } - - } - - private Predicate handleEquals(Expression expression, FilterOperator operator, Object value) { - if (value instanceof List) { - Predicate p = ((SimpleExpression) expression).in((List) value); - return negateIfNeeded(p, operator); - } - else if (Collection.class.isAssignableFrom(expression.getType())) { - SimpleExpression simpleExpr = (SimpleExpression) expression; - Predicate p = simpleExpr.in(value); - return negateIfNeeded(p, operator); - } - else if (expression instanceof MapExpressionBase) { - MapExpressionBase mapExpression = (MapExpressionBase) expression; - Predicate p = mapExpression.containsValue(value); - return negateIfNeeded(p, operator); - } - else if (value == null) { - return negateIfNeeded(((SimpleExpression) expression).isNull(), operator); - } - return negateIfNeeded(((SimpleExpression) expression).eq(value), operator); - } - - private Expression handleConversions(Expression expression, FilterOperator operator) { - // convert to String for LIKE operators - if (expression.getType() != String.class - && (operator == FilterOperator.LIKE)) { - return ((LiteralExpression) expression).stringValue(); - } - else { - return expression; - } - } - - @Override - public Predicate and(List predicates) { - if (predicates.size() == 1) { - return predicates.get(0); - } - else { - return new BooleanPredicateOperation(Ops.AND, (ImmutableList) ImmutableList.copyOf(predicates)); - } - } - - @Override - public Predicate not(Predicate predicate) { - return predicate.not(); - } - - @Override - public Predicate or(List predicates) { - if (predicates.size() == 1) { - return predicates.get(0); - } - else { - return new BooleanPredicateOperation(Ops.OR, (ImmutableList) ImmutableList.copyOf(predicates)); - } - } - - public final class BooleanPredicateOperation extends OperationImpl implements Predicate { - - private static final long serialVersionUID = -5371430939203772072L; - - @Nullable - private transient volatile Predicate not; - - protected BooleanPredicateOperation(Ops ops, ImmutableList> list) { - super(Boolean.class, ops, list); - if (list.isEmpty()) { - throw new IllegalArgumentException("list cannot be empty"); - } - } - - @Override - public Predicate not() { - if (not == null) { - not = ExpressionUtils.predicate(Ops.NOT, this); - } - return not; - } - } - - private Predicate negateIfNeeded(Predicate p, FilterOperator fc) { - if (fc.equals(FilterOperator.NEQ)) { - return p.not(); - } - return p; - } - - @Override - public Expression joinMapValue(Expression currentCriteriaPath, MetaAttribute pathElement, Object key) { - MapPath mapPath = (MapPath) QuerydslUtils.get(currentCriteriaPath, pathElement.getName()); - return mapPath.get(key); - } - - @Override - public Expression joinMapValues(Expression currentCriteriaPath, MetaAttribute pathElement) { - throw new UnsupportedOperationException(); - } - - @Override - public Expression joinMapKey(Expression currentCriteriaPath, MetaAttribute pathElement) { - throw new UnsupportedOperationException(); - } - - @Override - public Class getJavaElementType(Expression expression) { - if (expression instanceof CollectionExpressionBase) { - return ((CollectionExpressionBase) expression).getElementType(); - } - return expression.getType(); - } - - @Override - public Expression getAttribute(final Expression expression, MetaAttribute pathElement) { - if (pathElement instanceof MetaVirtualAttribute) { - VirtualAttributeRegistry virtualAttrs = queryImpl.getVirtualAttrs(); - QuerydslExpressionFactory expressionFactory = (QuerydslExpressionFactory) virtualAttrs - .get((MetaVirtualAttribute) pathElement); - return expressionFactory.getExpression(expression, getQuery()); - } - else { - return QuerydslUtils.get(expression, pathElement.getName()); - } - } - - @Override - public Expression joinSubType(Expression expression, Class entityClass) { - BeanPath beanPath = (BeanPath) expression; - Class queryClass = QuerydslUtils.getQueryClass(entityClass); - return beanPath.as(queryClass); - } - - @Override - public Expression doJoin(MetaAttribute targetAttr, JoinType joinType, Expression parent) { - if (targetAttr instanceof MetaVirtualAttribute) { - - MetaVirtualAttribute virtualAttr = (MetaVirtualAttribute) targetAttr; - QuerydslExpressionFactory expressionFactory = (QuerydslExpressionFactory) queryImpl.getVirtualAttrs() - .get(virtualAttr); - - return expressionFactory.getExpression(parent, getQuery()); - } - else { - Expression expression = QuerydslUtils.get(parent, targetAttr.getName()); - querydslQuery.getMetadata().addJoin(QuerydslUtils.convertJoinType(joinType), expression); - return expression; - } - } +package io.katharsis.jpa.internal.query.backend.querydsl; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import javax.annotation.Nullable; +import javax.persistence.criteria.JoinType; + +import com.google.common.collect.ImmutableList; +import com.querydsl.core.support.FetchableSubQueryBase; +import com.querydsl.core.types.CollectionExpression; +import com.querydsl.core.types.EntityPath; +import com.querydsl.core.types.Expression; +import com.querydsl.core.types.ExpressionUtils; +import com.querydsl.core.types.OperationImpl; +import com.querydsl.core.types.Ops; +import com.querydsl.core.types.Order; +import com.querydsl.core.types.OrderSpecifier; +import com.querydsl.core.types.Path; +import com.querydsl.core.types.Predicate; +import com.querydsl.core.types.QTuple; +import com.querydsl.core.types.dsl.BeanPath; +import com.querydsl.core.types.dsl.CollectionExpressionBase; +import com.querydsl.core.types.dsl.ComparableExpression; +import com.querydsl.core.types.dsl.LiteralExpression; +import com.querydsl.core.types.dsl.MapExpressionBase; +import com.querydsl.core.types.dsl.MapPath; +import com.querydsl.core.types.dsl.NumberExpression; +import com.querydsl.core.types.dsl.SimpleExpression; +import com.querydsl.core.types.dsl.StringExpression; +import com.querydsl.jpa.impl.JPAQuery; +import com.querydsl.jpa.impl.JPAQueryFactory; + +import io.katharsis.jpa.internal.meta.MetaAttribute; +import io.katharsis.jpa.internal.meta.MetaAttributePath; +import io.katharsis.jpa.internal.query.ComputedAttributeRegistryImpl; +import io.katharsis.jpa.internal.query.JoinRegistry; +import io.katharsis.jpa.internal.query.MetaComputedAttribute; +import io.katharsis.jpa.internal.query.QueryUtil; +import io.katharsis.jpa.internal.query.backend.JpaQueryBackend; +import io.katharsis.jpa.query.querydsl.QuerydslExpressionFactory; +import io.katharsis.queryspec.Direction; +import io.katharsis.queryspec.FilterOperator; + +@SuppressWarnings({ "rawtypes", "unchecked" }) +public class QuerydslQueryBackend implements JpaQueryBackend, OrderSpecifier, Predicate, Expression> { + + private JoinRegistry, Expression> joinHelper; + + private Path root; + + private EntityPath parentFrom; + + private QuerydslQueryImpl queryImpl; + + private JPAQuery querydslQuery; + + private List> orderList = new ArrayList<>(); + + public QuerydslQueryBackend(QuerydslQueryImpl queryImpl, Class clazz, Class parentEntityClass, + MetaAttribute parentAttr) { + this.queryImpl = queryImpl; + + JPAQueryFactory queryFactory = queryImpl.getQueryFactory(); + + if (parentEntityClass != null) { + parentFrom = QuerydslUtils.getEntityPath(parentEntityClass); + root = QuerydslUtils.getEntityPath(clazz); + + Path joinPath = (Path) QuerydslUtils.get(parentFrom, parentAttr.getName()); + joinHelper = new JoinRegistry<>(this, queryImpl); + + joinHelper.putJoin(new MetaAttributePath(), root); + querydslQuery = queryFactory.select(root); + querydslQuery = querydslQuery.from(parentFrom); + if (joinPath instanceof CollectionExpression) { + querydslQuery = querydslQuery.join((CollectionExpression) joinPath, root); + } + else { + querydslQuery = querydslQuery.join((EntityPath) joinPath, root); + } + } + else { + root = QuerydslUtils.getEntityPath(clazz); + joinHelper = new JoinRegistry<>(this, queryImpl); + joinHelper.putJoin(new MetaAttributePath(), root); + querydslQuery = queryFactory.select(root); + querydslQuery = querydslQuery.from((EntityPath) root); + } + } + + public JPAQuery getQuery() { + JPAQuery finalQuery = querydslQuery; + for (OrderSpecifier order : orderList) { + finalQuery = finalQuery.orderBy(order); + } + return finalQuery; + } + + @Override + public Expression getAttribute(MetaAttributePath attrPath) { + return joinHelper.getEntityAttribute(attrPath); + } + + @Override + public void addPredicate(Predicate predicate) { + querydslQuery = querydslQuery.where(predicate); + } + + @Override + public Path getRoot() { + return root; + } + + @Override + public void setOrder(List> list) { + this.orderList = list; + } + + @Override + public List> getOrderList() { + return orderList; + } + + @Override + public OrderSpecifier newSort(Expression expr, Direction dir) { + if (dir == Direction.ASC) { + return new OrderSpecifier(Order.ASC, expr); + } + else { + return new OrderSpecifier(Order.DESC, expr); + } + } + + @Override + public void distinct() { + querydslQuery = querydslQuery.distinct(); + } + + @Override + public void addParentPredicate(MetaAttribute primaryKeyAttr) { + List parentIds = queryImpl.getParentIds(); + SimpleExpression parentIdPath = (SimpleExpression) QuerydslUtils.get(parentFrom, primaryKeyAttr.getName()); + addPredicate(parentIdPath.in((List) parentIds)); + } + + @Override + public boolean hasManyRootsFetchesOrJoins() { + return QuerydslUtils.hasManyRootsFetchesOrJoins(querydslQuery); + } + + @Override + public void addSelection(Expression expression, String name) { + Expression selection = querydslQuery.getMetadata().getProjection(); + + List> newSelection = new ArrayList<>(); + if (selection != null) { + if (selection instanceof QTuple) { + newSelection.addAll(((QTuple) selection).getArgs()); + } + else { + newSelection.add(selection); + } + } + newSelection.add(expression); + querydslQuery = (JPAQuery) querydslQuery.select(newSelection.toArray(new Expression[newSelection.size()])); + } + + @Override + public Expression getExpression(OrderSpecifier order) { + return order.getTarget(); + } + + @Override + public boolean containsRelation(Expression expression) { + return QueryUtil.containsRelation(expression); + } + + @Override + public Predicate buildPredicate(FilterOperator operator, MetaAttributePath attrPath, Object value) { + Expression attr = getAttribute(attrPath); + return buildPredicate(operator, attr, value); + } + + public Predicate buildPredicate(FilterOperator operator, Expression expressionObj, Object value) { + Expression expression = expressionObj; + + expression = handleConversions(expression, operator); + + return handle(expression, operator, value); + + } + + private Predicate handle(Expression expression, FilterOperator operator, Object value) { // NOSONAR + // checking multiple comparision implementations is a mess, created + // https://github.com/querydsl/querydsl/issues/2028 + if (operator == FilterOperator.EQ || operator == FilterOperator.NEQ) { + return handleEquals(expression, operator, value); + } + else if (operator == FilterOperator.LIKE) { + return ((StringExpression) expression).lower().like(value.toString().toLowerCase()); + } + else if (operator == FilterOperator.GT) { + if (expression instanceof FetchableSubQueryBase) { + return ((FetchableSubQueryBase) expression).gt((Number) value); + } + else if (expression instanceof NumberExpression) { + return ((NumberExpression) expression).gt((Number) value); + } + else { + return ((ComparableExpression) expression).gt((Comparable) value); + } + } + else if (operator == FilterOperator.LT) { + if (expression instanceof FetchableSubQueryBase) { + return ((FetchableSubQueryBase) expression).lt((Number) value); + } + else if (expression instanceof NumberExpression) { + return ((NumberExpression) expression).lt((Number) value); + } + else { + return ((ComparableExpression) expression).lt((Comparable) value); + } + } + else if (operator == FilterOperator.GE) { + if (expression instanceof FetchableSubQueryBase) { + return ((FetchableSubQueryBase) expression).goe((Number) value); + } + else if (expression instanceof NumberExpression) { + return ((NumberExpression) expression).goe((Number) value); + } + else { + return ((ComparableExpression) expression).goe((Comparable) value); + } + } + else if (operator == FilterOperator.LE) { + if (expression instanceof FetchableSubQueryBase) { + return ((FetchableSubQueryBase) expression).loe((Number) value); + } + else if (expression instanceof NumberExpression) { + return ((NumberExpression) expression).loe((Number) value); + } + else { + return ((ComparableExpression) expression).loe((Comparable) value); + } + } + else { + throw new IllegalStateException("unexpected operator " + operator); + } + + } + + private Predicate handleEquals(Expression expression, FilterOperator operator, Object value) { + if (value instanceof List) { + Predicate p = ((SimpleExpression) expression).in((List) value); + return negateIfNeeded(p, operator); + } + else if (Collection.class.isAssignableFrom(expression.getType())) { + SimpleExpression simpleExpr = (SimpleExpression) expression; + Predicate p = simpleExpr.in(value); + return negateIfNeeded(p, operator); + } + else if (expression instanceof MapExpressionBase) { + MapExpressionBase mapExpression = (MapExpressionBase) expression; + Predicate p = mapExpression.containsValue(value); + return negateIfNeeded(p, operator); + } + else if (value == null) { + return negateIfNeeded(((SimpleExpression) expression).isNull(), operator); + } + return negateIfNeeded(((SimpleExpression) expression).eq(value), operator); + } + + private Expression handleConversions(Expression expression, FilterOperator operator) { + // convert to String for LIKE operators + if (expression.getType() != String.class && (operator == FilterOperator.LIKE)) { + return ((LiteralExpression) expression).stringValue(); + } + else { + return expression; + } + } + + @Override + public Predicate and(List predicates) { + if (predicates.size() == 1) { + return predicates.get(0); + } + else { + return new BooleanPredicateOperation(Ops.AND, (ImmutableList) ImmutableList.copyOf(predicates)); + } + } + + @Override + public Predicate not(Predicate predicate) { + return predicate.not(); + } + + @Override + public Predicate or(List predicates) { + if (predicates.size() == 1) { + return predicates.get(0); + } + else { + return new BooleanPredicateOperation(Ops.OR, (ImmutableList) ImmutableList.copyOf(predicates)); + } + } + + public final class BooleanPredicateOperation extends OperationImpl implements Predicate { + + private static final long serialVersionUID = -5371430939203772072L; + + @Nullable + private transient volatile Predicate not; + + protected BooleanPredicateOperation(Ops ops, ImmutableList> list) { + super(Boolean.class, ops, list); + if (list.isEmpty()) { + throw new IllegalArgumentException("list cannot be empty"); + } + } + + @Override + public Predicate not() { + if (not == null) { + not = ExpressionUtils.predicate(Ops.NOT, this); + } + return not; + } + } + + private Predicate negateIfNeeded(Predicate p, FilterOperator fc) { + if (fc.equals(FilterOperator.NEQ)) { + return p.not(); + } + return p; + } + + @Override + public Expression joinMapValue(Expression currentCriteriaPath, MetaAttribute pathElement, Object key) { + MapPath mapPath = (MapPath) QuerydslUtils.get(currentCriteriaPath, pathElement.getName()); + return mapPath.get(key); + } + + @Override + public Expression joinMapValues(Expression currentCriteriaPath, MetaAttribute pathElement) { + throw new UnsupportedOperationException(); + } + + @Override + public Expression joinMapKey(Expression currentCriteriaPath, MetaAttribute pathElement) { + throw new UnsupportedOperationException(); + } + + @Override + public Class getJavaElementType(Expression expression) { + if (expression instanceof CollectionExpressionBase) { + return ((CollectionExpressionBase) expression).getElementType(); + } + return expression.getType(); + } + + @Override + public Expression getAttribute(final Expression expression, MetaAttribute pathElement) { + if (pathElement instanceof MetaComputedAttribute) { + ComputedAttributeRegistryImpl virtualAttrs = queryImpl.getComputedAttrs(); + QuerydslExpressionFactory expressionFactory = (QuerydslExpressionFactory) virtualAttrs + .get((MetaComputedAttribute) pathElement); + return expressionFactory.getExpression(expression, getQuery()); + } + else { + return QuerydslUtils.get(expression, pathElement.getName()); + } + } + + @Override + public Expression joinSubType(Expression expression, Class entityClass) { + BeanPath beanPath = (BeanPath) expression; + Class queryClass = QuerydslUtils.getQueryClass(entityClass); + return beanPath.as(queryClass); + } + + @Override + public Expression doJoin(MetaAttribute targetAttr, JoinType joinType, Expression parent) { + if (targetAttr instanceof MetaComputedAttribute) { + + MetaComputedAttribute computedAttr = (MetaComputedAttribute) targetAttr; + QuerydslExpressionFactory expressionFactory = (QuerydslExpressionFactory) queryImpl.getComputedAttrs() + .get(computedAttr); + + return expressionFactory.getExpression(parent, getQuery()); + } + else { + Expression expression = QuerydslUtils.get(parent, targetAttr.getName()); + querydslQuery.getMetadata().addJoin(QuerydslUtils.convertJoinType(joinType), expression); + return expression; + } + } } \ No newline at end of file diff --git a/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/query/backend/querydsl/QuerydslQueryImpl.java b/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/query/backend/querydsl/QuerydslQueryImpl.java index 719ec37b..8a356c33 100644 --- a/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/query/backend/querydsl/QuerydslQueryImpl.java +++ b/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/query/backend/querydsl/QuerydslQueryImpl.java @@ -1,49 +1,49 @@ -package io.katharsis.jpa.internal.query.backend.querydsl; - -import java.util.List; -import java.util.Map; - -import javax.persistence.EntityManager; - -import com.querydsl.jpa.impl.JPAQueryFactory; - -import io.katharsis.jpa.internal.meta.MetaLookup; -import io.katharsis.jpa.internal.query.AbstractJpaQueryImpl; -import io.katharsis.jpa.internal.query.VirtualAttributeRegistry; -import io.katharsis.jpa.query.querydsl.QuerydslQuery; - -public class QuerydslQueryImpl extends AbstractJpaQueryImpl> implements QuerydslQuery { - - private JPAQueryFactory queryFactory; - - public QuerydslQueryImpl(MetaLookup metaLookup, EntityManager em, Class clazz, - VirtualAttributeRegistry virtualAttrs) { - super(metaLookup, em, clazz, virtualAttrs); - queryFactory = new JPAQueryFactory(em); - } - - public QuerydslQueryImpl(MetaLookup metaLookup, EntityManager em, Class clazz, - VirtualAttributeRegistry virtualAttrs, String attrName, List entityIds) { - super(metaLookup, em, clazz, virtualAttrs, attrName, entityIds); - queryFactory = new JPAQueryFactory(em); - } - - @Override - public QuerydslExecutorImpl buildExecutor() { - return (QuerydslExecutorImpl) super.buildExecutor(); - } - - protected JPAQueryFactory getQueryFactory() { - return queryFactory; - } - - @Override - protected QuerydslQueryBackend newBackend() { - return new QuerydslQueryBackend<>(this, clazz, parentEntityClass, parentAttr); - } - - @Override - protected QuerydslExecutorImpl newExecutor(QuerydslQueryBackend ctx, int numAutoSelections, Map selectionBindings) { - return new QuerydslExecutorImpl<>(em, meta, ctx.getQuery(), numAutoSelections, selectionBindings); - } -} +package io.katharsis.jpa.internal.query.backend.querydsl; + +import java.util.List; +import java.util.Map; + +import javax.persistence.EntityManager; + +import com.querydsl.jpa.impl.JPAQueryFactory; + +import io.katharsis.jpa.internal.meta.MetaLookup; +import io.katharsis.jpa.internal.query.AbstractJpaQueryImpl; +import io.katharsis.jpa.internal.query.ComputedAttributeRegistryImpl; +import io.katharsis.jpa.query.querydsl.QuerydslQuery; + +public class QuerydslQueryImpl extends AbstractJpaQueryImpl> implements QuerydslQuery { + + private JPAQueryFactory queryFactory; + + public QuerydslQueryImpl(MetaLookup metaLookup, EntityManager em, Class clazz, + ComputedAttributeRegistryImpl computedAttrs) { + super(metaLookup, em, clazz, computedAttrs); + queryFactory = new JPAQueryFactory(em); + } + + public QuerydslQueryImpl(MetaLookup metaLookup, EntityManager em, Class clazz, + ComputedAttributeRegistryImpl virtualAttrs, String attrName, List entityIds) { + super(metaLookup, em, clazz, virtualAttrs, attrName, entityIds); + queryFactory = new JPAQueryFactory(em); + } + + @Override + public QuerydslExecutorImpl buildExecutor() { + return (QuerydslExecutorImpl) super.buildExecutor(); + } + + protected JPAQueryFactory getQueryFactory() { + return queryFactory; + } + + @Override + protected QuerydslQueryBackend newBackend() { + return new QuerydslQueryBackend<>(this, clazz, parentEntityClass, parentAttr); + } + + @Override + protected QuerydslExecutorImpl newExecutor(QuerydslQueryBackend ctx, int numAutoSelections, Map selectionBindings) { + return new QuerydslExecutorImpl<>(em, meta, ctx.getQuery(), numAutoSelections, selectionBindings); + } +} diff --git a/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/query/backend/querydsl/SingleObjectTupleImpl.java b/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/query/backend/querydsl/SingleObjectTupleImpl.java new file mode 100644 index 00000000..a73c7fd6 --- /dev/null +++ b/katharsis-jpa/src/main/java/io/katharsis/jpa/internal/query/backend/querydsl/SingleObjectTupleImpl.java @@ -0,0 +1,71 @@ +package io.katharsis.jpa.internal.query.backend.querydsl; + +import java.util.List; + +import javax.persistence.TupleElement; + +import com.querydsl.core.types.Expression; + +import io.katharsis.jpa.query.criteria.JpaCriteriaTuple; +import io.katharsis.jpa.query.querydsl.QuerydslTuple; + +public class SingleObjectTupleImpl implements QuerydslTuple, JpaCriteriaTuple { + + private Object entity; + + public SingleObjectTupleImpl(Object entity) { + this.entity = entity; + } + + @SuppressWarnings("unchecked") + @Override + public T get(int index, Class type) { + if (index == 0) { + return (T) entity; + } + else { + throw new IndexOutOfBoundsException("index=" + index); + } + } + + @Override + public T get(Expression expr) { + throw new UnsupportedOperationException(); + } + + @Override + public int size() { + return 1; + } + + @Override + public Object[] toArray() { + return new Object[] { entity }; + } + + @Override + public T get(String name, Class clazz) { + throw new UnsupportedOperationException(); + } + + @Override + public X get(TupleElement element) { + throw new UnsupportedOperationException(); + } + + @Override + public Object get(String name) { + throw new UnsupportedOperationException(); + } + + @Override + public Object get(int index) { + return get(index, Object.class); + } + + @Override + public List> getElements() { + throw new UnsupportedOperationException(); + } + +} diff --git a/katharsis-jpa/src/main/java/io/katharsis/jpa/mapping/IdentityMapper.java b/katharsis-jpa/src/main/java/io/katharsis/jpa/mapping/IdentityMapper.java new file mode 100644 index 00000000..95d14ad4 --- /dev/null +++ b/katharsis-jpa/src/main/java/io/katharsis/jpa/mapping/IdentityMapper.java @@ -0,0 +1,24 @@ +package io.katharsis.jpa.mapping; + +import io.katharsis.jpa.query.Tuple; + +public class IdentityMapper implements JpaMapper { + + private IdentityMapper() { + } + + public static final IdentityMapper newInstance() { + return new IdentityMapper<>(); + } + + @SuppressWarnings("unchecked") + @Override + public E map(Tuple tuple) { + return (E) tuple.get(0, Object.class); + } + + @Override + public E unmap(E dto) { + return dto; + } +} diff --git a/katharsis-jpa/src/main/java/io/katharsis/jpa/mapping/JpaMapper.java b/katharsis-jpa/src/main/java/io/katharsis/jpa/mapping/JpaMapper.java new file mode 100644 index 00000000..37a93ff2 --- /dev/null +++ b/katharsis-jpa/src/main/java/io/katharsis/jpa/mapping/JpaMapper.java @@ -0,0 +1,30 @@ +package io.katharsis.jpa.mapping; + +import io.katharsis.jpa.query.Tuple; + +/** + * Maps a tuple to a DTO and a DTO back to a entity. + */ +public interface JpaMapper { + + /** + * Usually the first tuple entry is the entity and any additonal tuple entries + * are computed attributes. However, applications may choose to override this + * to only fetch a subset of attributes for performance reasons. + * + * @param tuple + * @return mapped dto + */ + public D map(Tuple tuple); + + /** + * Maps the dto back to the entity. Make sure to return a managed entity instance to support + * proper inserts, updates and deletes. An implementation may choose to lookup the entity + * with the entity manager and create a new instance if it has not been found. + * + * @param dto + * @return entity + */ + public E unmap(D dto); + +} diff --git a/katharsis-jpa/src/main/java/io/katharsis/jpa/query/ComputedAttributeRegistry.java b/katharsis-jpa/src/main/java/io/katharsis/jpa/query/ComputedAttributeRegistry.java new file mode 100644 index 00000000..51efdd8e --- /dev/null +++ b/katharsis-jpa/src/main/java/io/katharsis/jpa/query/ComputedAttributeRegistry.java @@ -0,0 +1,16 @@ +package io.katharsis.jpa.query; + +import java.util.Set; + +/** + * Holds the computed attributes registered to a JpaQueryFactory. + */ +public interface ComputedAttributeRegistry { + + /** + * @param entityType + * @return list of computed attribute names for the given entity type + */ + Set getForType(Class entityType); + +} diff --git a/katharsis-jpa/src/main/java/io/katharsis/jpa/query/JpaQueryFactory.java b/katharsis-jpa/src/main/java/io/katharsis/jpa/query/JpaQueryFactory.java index a824c4a0..d101d708 100644 --- a/katharsis-jpa/src/main/java/io/katharsis/jpa/query/JpaQueryFactory.java +++ b/katharsis-jpa/src/main/java/io/katharsis/jpa/query/JpaQueryFactory.java @@ -1,18 +1,23 @@ -package io.katharsis.jpa.query; - -import java.util.List; - -public interface JpaQueryFactory { - - /** - * Builds a new query for the given entity class. - */ - JpaQuery query(Class entityClass); - - /** - * Builds a new query for the given attribute. Used to retrieve relations of - * an entity. - */ - JpaQuery query(Class entityClass, String attrName, List entityIds); - -} +package io.katharsis.jpa.query; + +import java.util.List; + +public interface JpaQueryFactory { + + /** + * Builds a new query for the given entity class. + */ + JpaQuery query(Class entityClass); + + /** + * Builds a new query for the given attribute. Used to retrieve relations of + * an entity. + */ + JpaQuery query(Class entityClass, String attrName, List entityIds); + + /** + * @return ComputedAttributeRegistry holding registered computed attributes + */ + ComputedAttributeRegistry getComputedAttributes(); + +} diff --git a/katharsis-jpa/src/main/java/io/katharsis/jpa/query/criteria/JpaCriteriaQueryFactory.java b/katharsis-jpa/src/main/java/io/katharsis/jpa/query/criteria/JpaCriteriaQueryFactory.java index ea46949f..d3faa9cd 100644 --- a/katharsis-jpa/src/main/java/io/katharsis/jpa/query/criteria/JpaCriteriaQueryFactory.java +++ b/katharsis-jpa/src/main/java/io/katharsis/jpa/query/criteria/JpaCriteriaQueryFactory.java @@ -1,37 +1,38 @@ -package io.katharsis.jpa.query.criteria; - -import java.lang.reflect.Type; -import java.util.List; - -import javax.persistence.EntityManager; - -import io.katharsis.jpa.internal.JpaQueryFactoryBase; -import io.katharsis.jpa.internal.meta.MetaLookup; -import io.katharsis.jpa.internal.query.backend.criteria.JpaCriteriaQueryImpl; -import io.katharsis.jpa.query.JpaQueryFactory; - -public class JpaCriteriaQueryFactory extends JpaQueryFactoryBase implements JpaQueryFactory { - - private JpaCriteriaQueryFactory(MetaLookup metaLookup, EntityManager em) { - super(metaLookup, em); - } - - public static JpaCriteriaQueryFactory newInstance(MetaLookup metaLookup, EntityManager em) { - return new JpaCriteriaQueryFactory(metaLookup, em); - } - - @Override - public JpaCriteriaQuery query(Class entityClass) { - return new JpaCriteriaQueryImpl<>(metaLookup, em, entityClass, virtualAttrs); - } - - @Override - public JpaCriteriaQuery query(Class entityClass, String attrName, List entityIds) { - return new JpaCriteriaQueryImpl<>(metaLookup, em, entityClass, virtualAttrs, attrName, entityIds); - } - - public void registerVirtualAttribute(Class targetClass, String attributeName, Type attributeType, - JpaCriteriaExpressionFactory expressionFactory) { - virtualAttrs.register(targetClass, attributeName, expressionFactory, attributeType); - } -} +package io.katharsis.jpa.query.criteria; + +import java.lang.reflect.Type; +import java.util.List; + +import javax.persistence.EntityManager; + +import io.katharsis.jpa.internal.JpaQueryFactoryBase; +import io.katharsis.jpa.internal.meta.MetaLookup; +import io.katharsis.jpa.internal.query.backend.criteria.JpaCriteriaQueryImpl; +import io.katharsis.jpa.query.JpaQueryFactory; + +public class JpaCriteriaQueryFactory extends JpaQueryFactoryBase implements JpaQueryFactory { + + private JpaCriteriaQueryFactory(MetaLookup metaLookup, EntityManager em) { + super(metaLookup, em); + } + + public static JpaCriteriaQueryFactory newInstance(MetaLookup metaLookup, EntityManager em) { + return new JpaCriteriaQueryFactory(metaLookup, em); + } + + @Override + public JpaCriteriaQuery query(Class entityClass) { + return new JpaCriteriaQueryImpl<>(metaLookup, em, entityClass, computedAttrs); + } + + @Override + public JpaCriteriaQuery query(Class entityClass, String attrName, List entityIds) { + return new JpaCriteriaQueryImpl<>(metaLookup, em, entityClass, computedAttrs, attrName, entityIds); + } + + public void registerComputedAttribute(Class targetClass, String attributeName, Type attributeType, + JpaCriteriaExpressionFactory expressionFactory) { + computedAttrs.register(targetClass, attributeName, expressionFactory, attributeType); + } + +} diff --git a/katharsis-jpa/src/main/java/io/katharsis/jpa/query/querydsl/QuerydslQueryFactory.java b/katharsis-jpa/src/main/java/io/katharsis/jpa/query/querydsl/QuerydslQueryFactory.java index f3421856..ec296ef6 100644 --- a/katharsis-jpa/src/main/java/io/katharsis/jpa/query/querydsl/QuerydslQueryFactory.java +++ b/katharsis-jpa/src/main/java/io/katharsis/jpa/query/querydsl/QuerydslQueryFactory.java @@ -1,38 +1,38 @@ -package io.katharsis.jpa.query.querydsl; - -import java.lang.reflect.Type; -import java.util.List; - -import javax.persistence.EntityManager; - -import io.katharsis.jpa.internal.JpaQueryFactoryBase; -import io.katharsis.jpa.internal.meta.MetaLookup; -import io.katharsis.jpa.internal.query.backend.querydsl.QuerydslQueryImpl; -import io.katharsis.jpa.query.JpaQueryFactory; - -public class QuerydslQueryFactory extends JpaQueryFactoryBase implements JpaQueryFactory { - - private QuerydslQueryFactory(MetaLookup metaLookup, EntityManager em) { - super(metaLookup, em); - } - - public static QuerydslQueryFactory newInstance(MetaLookup metaLookup, EntityManager em) { - return new QuerydslQueryFactory(metaLookup, em); - } - - @Override - public QuerydslQuery query(Class entityClass) { - return new QuerydslQueryImpl<>(metaLookup, em, entityClass, virtualAttrs); - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - @Override - public QuerydslQuery query(Class entityClass, String attrName, List entityIds) { - return new QuerydslQueryImpl(metaLookup, em, entityClass, virtualAttrs, attrName, entityIds); - } - - public void registerVirtualAttribute(Class targetClass, String attributeName, Type attributeType, - QuerydslExpressionFactory expressionFactory) { - virtualAttrs.register(targetClass, attributeName, expressionFactory, attributeType); - } -} +package io.katharsis.jpa.query.querydsl; + +import java.lang.reflect.Type; +import java.util.List; + +import javax.persistence.EntityManager; + +import io.katharsis.jpa.internal.JpaQueryFactoryBase; +import io.katharsis.jpa.internal.meta.MetaLookup; +import io.katharsis.jpa.internal.query.backend.querydsl.QuerydslQueryImpl; +import io.katharsis.jpa.query.JpaQueryFactory; + +public class QuerydslQueryFactory extends JpaQueryFactoryBase implements JpaQueryFactory { + + private QuerydslQueryFactory(MetaLookup metaLookup, EntityManager em) { + super(metaLookup, em); + } + + public static QuerydslQueryFactory newInstance(MetaLookup metaLookup, EntityManager em) { + return new QuerydslQueryFactory(metaLookup, em); + } + + @Override + public QuerydslQuery query(Class entityClass) { + return new QuerydslQueryImpl<>(metaLookup, em, entityClass, computedAttrs); + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Override + public QuerydslQuery query(Class entityClass, String attrName, List entityIds) { + return new QuerydslQueryImpl(metaLookup, em, entityClass, computedAttrs, attrName, entityIds); + } + + public void registerComputedAttribute(Class targetClass, String attributeName, Type attributeType, + QuerydslExpressionFactory expressionFactory) { + computedAttrs.register(targetClass, attributeName, expressionFactory, attributeType); + } +} diff --git a/katharsis-jpa/src/test/java/io/katharsis/jpa/AbstractJpaJerseyTest.java b/katharsis-jpa/src/test/java/io/katharsis/jpa/AbstractJpaJerseyTest.java index 92b1c0fc..14b6e561 100644 --- a/katharsis-jpa/src/test/java/io/katharsis/jpa/AbstractJpaJerseyTest.java +++ b/katharsis-jpa/src/test/java/io/katharsis/jpa/AbstractJpaJerseyTest.java @@ -1,107 +1,107 @@ -package io.katharsis.jpa; - -import java.util.concurrent.Callable; -import java.util.concurrent.TimeUnit; - -import javax.persistence.EntityManager; -import javax.persistence.EntityManagerFactory; -import javax.ws.rs.ApplicationPath; -import javax.ws.rs.core.Application; - -import org.glassfish.jersey.server.ResourceConfig; -import org.glassfish.jersey.test.JerseyTest; -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.springframework.context.annotation.AnnotationConfigApplicationContext; - -import com.fasterxml.jackson.databind.ObjectMapper; - -import io.katharsis.client.KatharsisClient; -import io.katharsis.jpa.model.TestEntity; -import io.katharsis.jpa.query.AbstractJpaTest; -import io.katharsis.jpa.query.querydsl.QuerydslQueryFactory; -import io.katharsis.jpa.util.EntityManagerProducer; -import io.katharsis.jpa.util.SpringTransactionRunner; -import io.katharsis.jpa.util.TestConfig; -import io.katharsis.locator.SampleJsonServiceLocator; -import io.katharsis.queryParams.DefaultQueryParamsParser; -import io.katharsis.queryParams.QueryParamsBuilder; -import io.katharsis.rs.KatharsisFeature; -import io.katharsis.rs.KatharsisProperties; - -public abstract class AbstractJpaJerseyTest extends JerseyTest { - - protected KatharsisClient client; - - protected QueryParamsBuilder queryParamsBuilder = new QueryParamsBuilder(new DefaultQueryParamsParser()); - - protected AnnotationConfigApplicationContext context; - - @Before - public void setup() { - client = new KatharsisClient(getBaseUri().toString(), "io.katharsis.client.mock"); - - JpaModule module = new JpaModule(TestEntity.class.getPackage().getName()); - setupModule(module); - client.addModule(module); - - client.getHttpClient().setReadTimeout(1000000, TimeUnit.MILLISECONDS); - } - - protected void setupModule(JpaModule module) { - } - - @Override - @After - public void tearDown() throws Exception { - super.tearDown(); - - SpringTransactionRunner transactionRunner = context.getBean(SpringTransactionRunner.class); - transactionRunner.doInTransaction(new Callable() { - - @Override - public Object call() throws Exception { - EntityManager em = context.getBean(EntityManagerProducer.class).getEntityManager(); - AbstractJpaTest.clear(em); - return null; - } - }); - - if (context != null) - context.destroy(); - } - - @Override - protected Application configure() { - return new TestApplication(); - } - - @ApplicationPath("/") - private class TestApplication extends ResourceConfig { - - public TestApplication() { - property(KatharsisProperties.RESOURCE_SEARCH_PACKAGE, "io.katharsis.client.mock"); - - Assert.assertNull(context); - - context = new AnnotationConfigApplicationContext(TestConfig.class); - context.start(); - EntityManagerFactory emFactory = context.getBean(EntityManagerFactory.class); - EntityManager em = context.getBean(EntityManagerProducer.class).getEntityManager(); - SpringTransactionRunner transactionRunner = context.getBean(SpringTransactionRunner.class); - - KatharsisFeature feature = new KatharsisFeature(new ObjectMapper(), - new QueryParamsBuilder(new DefaultQueryParamsParser()), new SampleJsonServiceLocator()); - - JpaModule module = new JpaModule(emFactory, em, transactionRunner); - module.setQueryFactory(QuerydslQueryFactory.newInstance(module.getMetaLookup(), em)); - setupModule(module); - feature.addModule(module); - - register(feature); - - } - } - -} +package io.katharsis.jpa; + +import java.util.concurrent.Callable; +import java.util.concurrent.TimeUnit; + +import javax.persistence.EntityManager; +import javax.persistence.EntityManagerFactory; +import javax.ws.rs.ApplicationPath; +import javax.ws.rs.core.Application; + +import org.glassfish.jersey.server.ResourceConfig; +import org.glassfish.jersey.test.JerseyTest; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import io.katharsis.client.KatharsisClient; +import io.katharsis.jpa.model.TestEntity; +import io.katharsis.jpa.query.AbstractJpaTest; +import io.katharsis.jpa.query.querydsl.QuerydslQueryFactory; +import io.katharsis.jpa.util.EntityManagerProducer; +import io.katharsis.jpa.util.SpringTransactionRunner; +import io.katharsis.jpa.util.TestConfig; +import io.katharsis.locator.SampleJsonServiceLocator; +import io.katharsis.queryParams.DefaultQueryParamsParser; +import io.katharsis.queryParams.QueryParamsBuilder; +import io.katharsis.rs.KatharsisFeature; +import io.katharsis.rs.KatharsisProperties; + +public abstract class AbstractJpaJerseyTest extends JerseyTest { + + protected KatharsisClient client; + + protected QueryParamsBuilder queryParamsBuilder = new QueryParamsBuilder(new DefaultQueryParamsParser()); + + protected AnnotationConfigApplicationContext context; + + @Before + public void setup() { + client = new KatharsisClient(getBaseUri().toString(), "io.katharsis.jpa.model.dto"); + + JpaModule module = JpaModule.newClientModule(TestEntity.class.getPackage().getName()); + setupModule(module, false); + client.addModule(module); + + client.getHttpClient().setReadTimeout(1000000, TimeUnit.MILLISECONDS); + } + + protected void setupModule(JpaModule module, boolean server) { + } + + @Override + @After + public void tearDown() throws Exception { + super.tearDown(); + + SpringTransactionRunner transactionRunner = context.getBean(SpringTransactionRunner.class); + transactionRunner.doInTransaction(new Callable() { + + @Override + public Object call() throws Exception { + EntityManager em = context.getBean(EntityManagerProducer.class).getEntityManager(); + AbstractJpaTest.clear(em); + return null; + } + }); + + if (context != null) + context.destroy(); + } + + @Override + protected Application configure() { + return new TestApplication(); + } + + @ApplicationPath("/") + private class TestApplication extends ResourceConfig { + + public TestApplication() { + property(KatharsisProperties.RESOURCE_SEARCH_PACKAGE, "io.katharsis.client.mock"); + + Assert.assertNull(context); + + context = new AnnotationConfigApplicationContext(TestConfig.class); + context.start(); + EntityManagerFactory emFactory = context.getBean(EntityManagerFactory.class); + EntityManager em = context.getBean(EntityManagerProducer.class).getEntityManager(); + SpringTransactionRunner transactionRunner = context.getBean(SpringTransactionRunner.class); + + KatharsisFeature feature = new KatharsisFeature(new ObjectMapper(), + new QueryParamsBuilder(new DefaultQueryParamsParser()), new SampleJsonServiceLocator()); + + JpaModule module = JpaModule.newServerModule(emFactory, em, transactionRunner); + module.setQueryFactory(QuerydslQueryFactory.newInstance(module.getMetaLookup(), em)); + setupModule(module, true); + feature.addModule(module); + + register(feature); + + } + } + +} diff --git a/katharsis-jpa/src/test/java/io/katharsis/jpa/JpaPartialEntityExposureTest.java b/katharsis-jpa/src/test/java/io/katharsis/jpa/JpaPartialEntityExposureTest.java index 98bb25bc..3f1472b6 100644 --- a/katharsis-jpa/src/test/java/io/katharsis/jpa/JpaPartialEntityExposureTest.java +++ b/katharsis-jpa/src/test/java/io/katharsis/jpa/JpaPartialEntityExposureTest.java @@ -1,82 +1,83 @@ -package io.katharsis.jpa; - -import java.util.List; -import java.util.Set; - -import javax.persistence.EntityManager; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -import io.katharsis.client.ResourceRepositoryStub; -import io.katharsis.jpa.internal.JpaResourceInformationBuilder; -import io.katharsis.jpa.model.RelatedEntity; -import io.katharsis.jpa.model.TestEntity; -import io.katharsis.queryParams.QueryParams; -import io.katharsis.resource.exception.init.ResourceNotFoundInitializationException; -import io.katharsis.resource.field.ResourceField; -import io.katharsis.resource.information.ResourceInformation; - -public class JpaPartialEntityExposureTest extends AbstractJpaJerseyTest { - - private JpaModule module; - - protected ResourceRepositoryStub testRepo; - - @Override - @Before - public void setup() { - super.setup(); - testRepo = client.getRepository(TestEntity.class); - } - - @Override - protected void setupModule(JpaModule module) { - this.module = module; - this.module.removeEntityClass(RelatedEntity.class); - } - - @Override - @After - public void tearDown() throws Exception { - super.tearDown(); - } - - @Test - public void testCrud() { - TestEntity test = new TestEntity(); - test.setId(2L); - test.setStringValue("test"); - testRepo.save(test); - - List tests = testRepo.findAll(new QueryParams()); - Assert.assertEquals(1, tests.size()); - test = tests.get(0); - Assert.assertEquals(2L, test.getId().longValue()); - Assert.assertNull(test.getOneRelatedValue()); - Assert.assertNull(test.getEagerRelatedValue()); - Assert.assertTrue(test.getManyRelatedValues().isEmpty()); - - testRepo.delete(test.getId()); - tests = testRepo.findAll(new QueryParams()); - Assert.assertEquals(0, tests.size()); - } - - @Test - public void testInformationBuilder() { - EntityManager em = null; - JpaResourceInformationBuilder builder = new JpaResourceInformationBuilder(module.getMetaLookup(), em, - module.getEntityClasses()); - ResourceInformation info = builder.build(TestEntity.class); - Set relationshipFields = info.getRelationshipFields(); - Assert.assertEquals(0, relationshipFields.size()); - } - - @Test(expected = ResourceNotFoundInitializationException.class) - public void testRelatedEntityNotAvailable() { - client.getRepository(RelatedEntity.class); - } - +package io.katharsis.jpa; + +import java.util.List; +import java.util.Set; + +import javax.persistence.EntityManager; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import io.katharsis.client.ResourceRepositoryStub; +import io.katharsis.jpa.internal.JpaResourceInformationBuilder; +import io.katharsis.jpa.model.RelatedEntity; +import io.katharsis.jpa.model.TestEntity; +import io.katharsis.queryParams.QueryParams; +import io.katharsis.resource.exception.init.ResourceNotFoundInitializationException; +import io.katharsis.resource.field.ResourceField; +import io.katharsis.resource.information.ResourceInformation; + +public class JpaPartialEntityExposureTest extends AbstractJpaJerseyTest { + + private JpaModule module; + + protected ResourceRepositoryStub testRepo; + + @Override + @Before + public void setup() { + super.setup(); + testRepo = client.getRepository(TestEntity.class); + } + + @Override + protected void setupModule(JpaModule module, boolean server) { + super.setupModule(module, server); + this.module = module; + this.module.removeEntityClass(RelatedEntity.class); + } + + @Override + @After + public void tearDown() throws Exception { + super.tearDown(); + } + + @Test + public void testCrud() { + TestEntity test = new TestEntity(); + test.setId(2L); + test.setStringValue("test"); + testRepo.save(test); + + List tests = testRepo.findAll(new QueryParams()); + Assert.assertEquals(1, tests.size()); + test = tests.get(0); + Assert.assertEquals(2L, test.getId().longValue()); + Assert.assertNull(test.getOneRelatedValue()); + Assert.assertNull(test.getEagerRelatedValue()); + Assert.assertTrue(test.getManyRelatedValues().isEmpty()); + + testRepo.delete(test.getId()); + tests = testRepo.findAll(new QueryParams()); + Assert.assertEquals(0, tests.size()); + } + + @Test + public void testInformationBuilder() { + EntityManager em = null; + JpaResourceInformationBuilder builder = new JpaResourceInformationBuilder(module.getMetaLookup(), em, + module.getEntityClasses()); + ResourceInformation info = builder.build(TestEntity.class); + Set relationshipFields = info.getRelationshipFields(); + Assert.assertEquals(0, relationshipFields.size()); + } + + @Test(expected = ResourceNotFoundInitializationException.class) + public void testRelatedEntityNotAvailable() { + client.getRepository(RelatedEntity.class); + } + } \ No newline at end of file diff --git a/katharsis-jpa/src/test/java/io/katharsis/jpa/JpaQueryParamsEndToEndTest.java b/katharsis-jpa/src/test/java/io/katharsis/jpa/JpaQueryParamsEndToEndTest.java index 7d8c8a9e..384f0bf3 100644 --- a/katharsis-jpa/src/test/java/io/katharsis/jpa/JpaQueryParamsEndToEndTest.java +++ b/katharsis-jpa/src/test/java/io/katharsis/jpa/JpaQueryParamsEndToEndTest.java @@ -1,240 +1,242 @@ -package io.katharsis.jpa; - -import java.io.Serializable; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import javax.persistence.OptimisticLockException; - -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -import io.katharsis.client.ResourceRepositoryStub; -import io.katharsis.jpa.model.RelatedEntity; -import io.katharsis.jpa.model.TestEmbeddedIdEntity; -import io.katharsis.jpa.model.TestEntity; -import io.katharsis.jpa.model.TestIdEmbeddable; -import io.katharsis.jpa.model.VersionedEntity; -import io.katharsis.queryParams.QueryParams; - -public class JpaQueryParamsEndToEndTest extends AbstractJpaJerseyTest { - - protected ResourceRepositoryStub testRepo; - - @Override - @Before - public void setup() { - super.setup(); - testRepo = client.getRepository(TestEntity.class); - } - - @Test - public void testIncludeRelations() throws InstantiationException, IllegalAccessException { - addTestWithOneRelation(); - - List list = findAll("include[test]", TestEntity.ATTR_oneRelatedValue); - Assert.assertEquals(1, list.size()); - for (TestEntity test : list) { - Assert.assertNotNull(test.getOneRelatedValue()); - } - } - - @Test - public void testIncludeNoRelations() throws InstantiationException, IllegalAccessException { - addTestWithOneRelation(); - - List list = testRepo.findAll(new QueryParams()); - Assert.assertEquals(1, list.size()); - for (TestEntity test : list) { - // in the future we may get proxies here - Assert.assertNull(test.getOneRelatedValue()); - } - } - - @Test - public void testFindEmpty() { - List list = testRepo.findAll(new QueryParams()); - Assert.assertTrue(list.isEmpty()); - } - - @Test - public void testFindNull() { - TestEntity test = testRepo.findOne(1L, new QueryParams()); - Assert.assertNull(test); - } - - @Test - public void testSaveAndFind() { - TestEntity task = new TestEntity(); - task.setId(1L); - task.setStringValue("test"); - testRepo.save(task); - - // check retrievable with findAll - List list = testRepo.findAll(new QueryParams()); - Assert.assertEquals(1, list.size()); - TestEntity savedTask = list.get(0); - Assert.assertEquals(task.getId(), savedTask.getId()); - Assert.assertEquals(task.getStringValue(), savedTask.getStringValue()); - - // check retrievable with findAll(ids) - list = testRepo.findAll(Arrays.asList(1L), new QueryParams()); - Assert.assertEquals(1, list.size()); - savedTask = list.get(0); - Assert.assertEquals(task.getId(), savedTask.getId()); - Assert.assertEquals(task.getStringValue(), savedTask.getStringValue()); - - // check retrievable with findOne - savedTask = testRepo.findOne(1L, new QueryParams()); - Assert.assertEquals(task.getId(), savedTask.getId()); - Assert.assertEquals(task.getStringValue(), savedTask.getStringValue()); - } - - @Test - public void testOptimisticLocking() { - ResourceRepositoryStub repo = client.getRepository(VersionedEntity.class); - VersionedEntity entity = new VersionedEntity(); - entity.setId(1L); - entity.setLongValue(13L); - VersionedEntity saved = repo.save(entity); - Assert.assertEquals(0, saved.getVersion()); - - saved.setLongValue(14L); - saved = repo.save(saved); - Assert.assertEquals(1, saved.getVersion()); - - saved.setLongValue(15L); - saved = repo.save(saved); - Assert.assertEquals(2, saved.getVersion()); - - saved.setLongValue(16L); - saved.setVersion(saved.getVersion() - 1); - try { - saved = repo.save(saved); - Assert.fail(); - } - catch (OptimisticLockException e) { - // ok - } - - VersionedEntity persisted = repo.findOne(1L, new QueryParams()); - Assert.assertEquals(2, persisted.getVersion()); - Assert.assertEquals(15L, persisted.getLongValue()); - } - - @Test - public void testDelete() { - TestEntity test = new TestEntity(); - test.setId(1L); - test.setStringValue("test"); - testRepo.save(test); - - testRepo.delete(1L); - - List list = testRepo.findAll(new QueryParams()); - Assert.assertEquals(0, list.size()); - } - - private QueryParams includeOneRelatedValueParams() { - Map> params = new HashMap>(); - addParams(params, "include[test]", TestEntity.ATTR_oneRelatedValue); - QueryParams queryParams = queryParamsBuilder.buildQueryParams(params); - return queryParams; - } - - @Test - public void testSaveOneRelation() { - TestEntity test = addTestWithOneRelation(); - - TestEntity savedTest = testRepo.findOne(2L, includeOneRelatedValueParams()); - Assert.assertEquals(test.getId(), savedTest.getId()); - Assert.assertEquals(test.getStringValue(), savedTest.getStringValue()); - Assert.assertNotNull(savedTest.getOneRelatedValue()); - Assert.assertEquals(1L, savedTest.getOneRelatedValue().getId().longValue()); - } - - @Test - public void testEagerOneRelation() { - ResourceRepositoryStub relatedRepo = client.getRepository(RelatedEntity.class); - RelatedEntity related = new RelatedEntity(); - related.setId(1L); - related.setStringValue("project"); - relatedRepo.save(related); - - TestEntity test = new TestEntity(); - test.setId(2L); - test.setStringValue("test"); - test.setEagerRelatedValue(related); - testRepo.save(test); - - TestEntity savedTest = testRepo.findOne(2L, new QueryParams()); - Assert.assertEquals(test.getId(), savedTest.getId()); - Assert.assertEquals(test.getStringValue(), savedTest.getStringValue()); - Assert.assertNull(savedTest.getOneRelatedValue()); - Assert.assertNotNull(savedTest.getEagerRelatedValue()); - Assert.assertEquals(1L, savedTest.getEagerRelatedValue().getId().longValue()); - } - - @Test - public void testEmbeddableIds() throws InstantiationException, IllegalAccessException { - ResourceRepositoryStub rep = client.getRepository(TestEmbeddedIdEntity.class); - - // add - TestEmbeddedIdEntity entity = new TestEmbeddedIdEntity(); - entity.setId(new TestIdEmbeddable(13, "test")); - entity.setLongValue(100L); - rep.save(entity); - - List list = rep.findAll(new QueryParams()); - Assert.assertEquals(1, list.size()); - TestEmbeddedIdEntity savedEntity = list.get(0); - Assert.assertNotNull(savedEntity); - Assert.assertEquals(100L, savedEntity.getLongValue()); - Assert.assertEquals(13, savedEntity.getId().getEmbIntValue().intValue()); - Assert.assertEquals("test", savedEntity.getId().getEmbStringValue()); - - // update - savedEntity.setLongValue(101L); - rep.save(savedEntity); - list = rep.findAll(new QueryParams()); - Assert.assertEquals(1, list.size()); - savedEntity = list.get(0); - Assert.assertEquals(101L, savedEntity.getLongValue()); - - // delete - rep.delete(entity.getId()); - list = rep.findAll(new QueryParams()); - Assert.assertEquals(0, list.size()); - } - - private TestEntity addTestWithOneRelation() { - ResourceRepositoryStub relatedRepo = client.getRepository(RelatedEntity.class); - RelatedEntity related = new RelatedEntity(); - related.setId(1L); - related.setStringValue("project"); - relatedRepo.save(related); - - TestEntity test = new TestEntity(); - test.setId(2L); - test.setStringValue("test"); - test.setOneRelatedValue(related); - testRepo.save(test, includeOneRelatedValueParams()); - return test; - } - - private List findAll(String paramKey, String paramValue) { - Map> params = new HashMap>(); - addParams(params, paramKey, paramValue); - QueryParams queryParams = queryParamsBuilder.buildQueryParams(params); - return testRepo.findAll(queryParams); - } - - private void addParams(Map> params, String key, String value) { - params.put(key, new HashSet(Arrays.asList(value))); - } +package io.katharsis.jpa; + +import java.io.Serializable; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.persistence.OptimisticLockException; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import io.katharsis.client.ResourceRepositoryStub; +import io.katharsis.jpa.model.RelatedEntity; +import io.katharsis.jpa.model.TestEmbeddedIdEntity; +import io.katharsis.jpa.model.TestEntity; +import io.katharsis.jpa.model.TestIdEmbeddable; +import io.katharsis.jpa.model.VersionedEntity; +import io.katharsis.queryParams.QueryParams; + +public class JpaQueryParamsEndToEndTest extends AbstractJpaJerseyTest { + + protected ResourceRepositoryStub testRepo; + + @Override + @Before + public void setup() { + super.setup(); + testRepo = client.getRepository(TestEntity.class); + } + + @Test + public void testIncludeRelations() throws InstantiationException, IllegalAccessException { + addTestWithOneRelation(); + + List list = findAll("include[test]", TestEntity.ATTR_oneRelatedValue); + Assert.assertEquals(1, list.size()); + for (TestEntity test : list) { + Assert.assertNotNull(test.getOneRelatedValue()); + } + } + + @Test + public void testIncludeNoRelations() throws InstantiationException, IllegalAccessException { + addTestWithOneRelation(); + + List list = testRepo.findAll(new QueryParams()); + Assert.assertEquals(1, list.size()); + for (TestEntity test : list) { + // in the future we may get proxies here + Assert.assertNull(test.getOneRelatedValue()); + } + } + + @Test + public void testFindEmpty() { + List list = testRepo.findAll(new QueryParams()); + Assert.assertTrue(list.isEmpty()); + } + + @Test + public void testFindNull() { + TestEntity test = testRepo.findOne(1L, new QueryParams()); + Assert.assertNull(test); + } + + @Test + public void testSaveAndFind() { + TestEntity task = new TestEntity(); + task.setId(1L); + task.setStringValue("test"); + testRepo.save(task); + + // check retrievable with findAll + List list = testRepo.findAll(new QueryParams()); + Assert.assertEquals(1, list.size()); + TestEntity savedTask = list.get(0); + Assert.assertEquals(task.getId(), savedTask.getId()); + Assert.assertEquals(task.getStringValue(), savedTask.getStringValue()); + + // check retrievable with findAll(ids) + list = testRepo.findAll(Arrays.asList(1L), new QueryParams()); + Assert.assertEquals(1, list.size()); + savedTask = list.get(0); + Assert.assertEquals(task.getId(), savedTask.getId()); + Assert.assertEquals(task.getStringValue(), savedTask.getStringValue()); + + // check retrievable with findOne + savedTask = testRepo.findOne(1L, new QueryParams()); + Assert.assertEquals(task.getId(), savedTask.getId()); + Assert.assertEquals(task.getStringValue(), savedTask.getStringValue()); + } + + @Test + public void testOptimisticLocking() { + ResourceRepositoryStub repo = client.getRepository(VersionedEntity.class); + VersionedEntity entity = new VersionedEntity(); + entity.setId(1L); + entity.setLongValue(13L); + VersionedEntity saved = repo.save(entity); + Assert.assertEquals(0, saved.getVersion()); + + saved.setLongValue(14L); + saved = repo.save(saved); + Assert.assertEquals(1, saved.getVersion()); + + saved.setLongValue(15L); + saved = repo.save(saved); + Assert.assertEquals(2, saved.getVersion()); + + saved.setLongValue(16L); + saved.setVersion(saved.getVersion() - 1); + try { + saved = repo.save(saved); + Assert.fail(); + } + catch (OptimisticLockException e) { + // ok + } + + VersionedEntity persisted = repo.findOne(1L, new QueryParams()); + Assert.assertEquals(2, persisted.getVersion()); + Assert.assertEquals(15L, persisted.getLongValue()); + } + + @Test + public void testDelete() { + TestEntity test = new TestEntity(); + test.setId(1L); + test.setStringValue("test"); + testRepo.save(test); + + testRepo.delete(1L); + + List list = testRepo.findAll(new QueryParams()); + Assert.assertEquals(0, list.size()); + } + + private QueryParams includeOneRelatedValueParams() { + Map> params = new HashMap>(); + addParams(params, "include[test]", TestEntity.ATTR_oneRelatedValue); + QueryParams queryParams = queryParamsBuilder.buildQueryParams(params); + return queryParams; + } + + @Test + public void testSaveOneRelation() { + TestEntity test = addTestWithOneRelation(); + + TestEntity savedTest = testRepo.findOne(2L, includeOneRelatedValueParams()); + Assert.assertEquals(test.getId(), savedTest.getId()); + Assert.assertEquals(test.getStringValue(), savedTest.getStringValue()); + Assert.assertNotNull(savedTest.getOneRelatedValue()); + Assert.assertEquals(1L, savedTest.getOneRelatedValue().getId().longValue()); + } + + @Test + public void testEagerOneRelation() { + ResourceRepositoryStub relatedRepo = client.getRepository(RelatedEntity.class); + RelatedEntity related = new RelatedEntity(); + related.setId(1L); + related.setStringValue("project"); + relatedRepo.save(related); + + TestEntity test = new TestEntity(); + test.setId(2L); + test.setStringValue("test"); + test.setEagerRelatedValue(related); + testRepo.save(test); + + TestEntity savedTest = testRepo.findOne(2L, new QueryParams()); + Assert.assertEquals(test.getId(), savedTest.getId()); + Assert.assertEquals(test.getStringValue(), savedTest.getStringValue()); + Assert.assertNull(savedTest.getOneRelatedValue()); + + // TODO should @JsonApiIncludeByDefault trigger this? + // Assert.assertNotNull(savedTest.getEagerRelatedValue()); + // Assert.assertEquals(1L, savedTest.getEagerRelatedValue().getId().longValue()); + } + + @Test + public void testEmbeddableIds() throws InstantiationException, IllegalAccessException { + ResourceRepositoryStub rep = client.getRepository(TestEmbeddedIdEntity.class); + + // add + TestEmbeddedIdEntity entity = new TestEmbeddedIdEntity(); + entity.setId(new TestIdEmbeddable(13, "test")); + entity.setLongValue(100L); + rep.save(entity); + + List list = rep.findAll(new QueryParams()); + Assert.assertEquals(1, list.size()); + TestEmbeddedIdEntity savedEntity = list.get(0); + Assert.assertNotNull(savedEntity); + Assert.assertEquals(100L, savedEntity.getLongValue()); + Assert.assertEquals(13, savedEntity.getId().getEmbIntValue().intValue()); + Assert.assertEquals("test", savedEntity.getId().getEmbStringValue()); + + // update + savedEntity.setLongValue(101L); + rep.save(savedEntity); + list = rep.findAll(new QueryParams()); + Assert.assertEquals(1, list.size()); + savedEntity = list.get(0); + Assert.assertEquals(101L, savedEntity.getLongValue()); + + // delete + rep.delete(entity.getId()); + list = rep.findAll(new QueryParams()); + Assert.assertEquals(0, list.size()); + } + + private TestEntity addTestWithOneRelation() { + ResourceRepositoryStub relatedRepo = client.getRepository(RelatedEntity.class); + RelatedEntity related = new RelatedEntity(); + related.setId(1L); + related.setStringValue("project"); + relatedRepo.save(related); + + TestEntity test = new TestEntity(); + test.setId(2L); + test.setStringValue("test"); + test.setOneRelatedValue(related); + testRepo.save(test, includeOneRelatedValueParams()); + return test; + } + + private List findAll(String paramKey, String paramValue) { + Map> params = new HashMap>(); + addParams(params, paramKey, paramValue); + QueryParams queryParams = queryParamsBuilder.buildQueryParams(params); + return testRepo.findAll(queryParams); + } + + private void addParams(Map> params, String key, String value) { + params.put(key, new HashSet(Arrays.asList(value))); + } } \ No newline at end of file diff --git a/katharsis-jpa/src/test/java/io/katharsis/jpa/JpaQuerySpecEndToEndTest.java b/katharsis-jpa/src/test/java/io/katharsis/jpa/JpaQuerySpecEndToEndTest.java index a101a215..213d4efe 100644 --- a/katharsis-jpa/src/test/java/io/katharsis/jpa/JpaQuerySpecEndToEndTest.java +++ b/katharsis-jpa/src/test/java/io/katharsis/jpa/JpaQuerySpecEndToEndTest.java @@ -1,389 +1,391 @@ -package io.katharsis.jpa; - -import java.io.Serializable; -import java.util.Arrays; -import java.util.List; - -import javax.persistence.OptimisticLockException; - -import org.junit.Assert; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; - -import io.katharsis.client.QuerySpecRelationshipRepositoryStub; -import io.katharsis.client.QuerySpecResourceRepositoryStub; -import io.katharsis.client.ResourceRepositoryStub; -import io.katharsis.client.response.JsonLinksInformation; -import io.katharsis.client.response.JsonMetaInformation; -import io.katharsis.client.response.ResourceList; -import io.katharsis.jpa.model.RelatedEntity; -import io.katharsis.jpa.model.TestEmbeddedIdEntity; -import io.katharsis.jpa.model.TestEntity; -import io.katharsis.jpa.model.TestIdEmbeddable; -import io.katharsis.jpa.model.VersionedEntity; -import io.katharsis.queryspec.QuerySpec; - -public class JpaQuerySpecEndToEndTest extends AbstractJpaJerseyTest { - - private QuerySpecResourceRepositoryStub testRepo; - - @Override - @Before - public void setup() { - super.setup(); - testRepo = client.getQuerySpecRepository(TestEntity.class); - } - - @Test - public void testIncludeRelations() throws InstantiationException, IllegalAccessException { - addTestWithOneRelation(); - - QuerySpec querySpec = new QuerySpec(TestEntity.class); - querySpec.includeRelation(Arrays.asList(TestEntity.ATTR_oneRelatedValue)); - List list = testRepo.findAll(querySpec); - - Assert.assertEquals(1, list.size()); - for (TestEntity test : list) { - Assert.assertNotNull(test.getOneRelatedValue()); - } - } - - @Test - public void testFindOneTargetWithNullResult() throws InstantiationException, IllegalAccessException { - TestEntity test = new TestEntity(); - test.setId(2L); - test.setStringValue("test"); - testRepo.save(test); - - QuerySpecRelationshipRepositoryStub relRepo = client - .getQuerySpecRepository(TestEntity.class, RelatedEntity.class); - - RelatedEntity related = relRepo.findOneTarget(test.getId(), TestEntity.ATTR_oneRelatedValue, - new QuerySpec(RelatedEntity.class)); - Assert.assertNull(related); - } - - @Test - public void testFindOneTarget() throws InstantiationException, IllegalAccessException { - TestEntity test = addTestWithOneRelation(); - - QuerySpecRelationshipRepositoryStub relRepo = client - .getQuerySpecRepository(TestEntity.class, RelatedEntity.class); - - RelatedEntity related = relRepo.findOneTarget(test.getId(), TestEntity.ATTR_oneRelatedValue, - new QuerySpec(RelatedEntity.class)); - Assert.assertNotNull(related); - } - - @Test - public void testAddManyRelationWithRelationshipRepository() throws InstantiationException, IllegalAccessException { - testAddManyRelation(false); - } - - @Test - @Ignore - // TODO bidirectionality not properly handled, see - // ResourceUpsert should make use of relationship repositories #130 - public void testAddManyRelationWithResourceSave() throws InstantiationException, IllegalAccessException { - testAddManyRelation(true); - } - - private void testAddManyRelation(boolean onSave) throws InstantiationException, IllegalAccessException { - QuerySpecResourceRepositoryStub relatedRepo = client.getQuerySpecRepository(RelatedEntity.class); - RelatedEntity related1 = new RelatedEntity(); - related1.setId(1L); - related1.setStringValue("related1"); - relatedRepo.save(related1); - - RelatedEntity related2 = new RelatedEntity(); - related2.setId(2L); - related2.setStringValue("related2"); - relatedRepo.save(related2); - - TestEntity test = new TestEntity(); - test.setId(3L); - test.setStringValue("test"); - if (onSave) { - test.setManyRelatedValues(Arrays.asList(related1, related2)); - } - testRepo.save(test, includeManyRelatedValueParams()); - - // query relation - QuerySpecRelationshipRepositoryStub relRepo = client - .getQuerySpecRepository(TestEntity.class, RelatedEntity.class); - if (!onSave) { - relRepo.addRelations(test, Arrays.asList(1L, 2L), TestEntity.ATTR_manyRelatedValues); - } - List related = relRepo.findManyTargets(test.getId(), TestEntity.ATTR_manyRelatedValues, - new QuerySpec(RelatedEntity.class)); - Assert.assertEquals(2, related.size()); - - // query relation in opposite direction - QuerySpecRelationshipRepositoryStub backRelRepo = client - .getQuerySpecRepository(RelatedEntity.class, TestEntity.class); - test = backRelRepo.findOneTarget(2L, RelatedEntity.ATTR_testEntity, new QuerySpec(TestEntity.class)); - Assert.assertNotNull(test); - Assert.assertEquals(3L, test.getId().longValue()); - } - - @Test - public void testIncludeNoRelations() throws InstantiationException, IllegalAccessException { - addTestWithOneRelation(); - - List list = testRepo.findAll(new QuerySpec(TestEntity.class)); - Assert.assertEquals(1, list.size()); - for (TestEntity test : list) { - // in the future we may get proxies here - Assert.assertNull(test.getOneRelatedValue()); - } - } - - @Test - public void testFindEmpty() { - List list = testRepo.findAll(new QuerySpec(TestEntity.class)); - Assert.assertTrue(list.isEmpty()); - } - - @Test - public void testFindNull() { - TestEntity test = testRepo.findOne(1L, new QuerySpec(TestEntity.class)); - Assert.assertNull(test); - } - - @Test - public void testSaveAndFind() { - TestEntity task = new TestEntity(); - task.setId(1L); - task.setStringValue("test"); - testRepo.save(task); - - // check retrievable with findAll - List list = testRepo.findAll(new QuerySpec(TestEntity.class)); - Assert.assertEquals(1, list.size()); - TestEntity savedTask = list.get(0); - Assert.assertEquals(task.getId(), savedTask.getId()); - Assert.assertEquals(task.getStringValue(), savedTask.getStringValue()); - - // check retrievable with findAll(ids) - list = testRepo.findAll(Arrays.asList(1L), new QuerySpec(TestEntity.class)); - Assert.assertEquals(1, list.size()); - savedTask = list.get(0); - Assert.assertEquals(task.getId(), savedTask.getId()); - Assert.assertEquals(task.getStringValue(), savedTask.getStringValue()); - - // check retrievable with findOne - savedTask = testRepo.findOne(1L, new QuerySpec(TestEntity.class)); - Assert.assertEquals(task.getId(), savedTask.getId()); - Assert.assertEquals(task.getStringValue(), savedTask.getStringValue()); - } - - @Test - public void testRootPaging() { - for (long i = 0; i < 5; i++) { - TestEntity task = new TestEntity(); - task.setId(i); - task.setStringValue("test"); - testRepo.save(task); - } - - QuerySpec querySpec = new QuerySpec(TestEntity.class); - querySpec.setOffset(2L); - querySpec.setLimit(2L); - - ResourceList list = testRepo.findAll(querySpec); - Assert.assertEquals(2, list.size()); - Assert.assertEquals(2, list.get(0).getId().intValue()); - Assert.assertEquals(3, list.get(1).getId().intValue()); - - JsonMetaInformation meta = list.getMetaInformation(JsonMetaInformation.class); - JsonLinksInformation links = list.getLinksInformation(JsonLinksInformation.class); - Assert.assertNotNull(meta); - Assert.assertNotNull(links); - - String baseUri = getBaseUri().toString(); - Assert.assertEquals(baseUri + "test/?page[limit]=2", links.asJsonNode().get("first").asText()); - Assert.assertEquals(baseUri + "test/?page[limit]=2&page[offset]=4", links.asJsonNode().get("last").asText()); - Assert.assertEquals(baseUri + "test/?page[limit]=2", links.asJsonNode().get("prev").asText()); - Assert.assertEquals(baseUri + "test/?page[limit]=2&page[offset]=4", links.asJsonNode().get("next").asText()); - } - - @Test - public void testRelationPaging() { - TestEntity test = new TestEntity(); - test.setId(1L); - test.setStringValue("test"); - testRepo.save(test); - - ResourceRepositoryStub relatedRepo = client.getRepository(RelatedEntity.class); - QuerySpecRelationshipRepositoryStub relRepo = client - .getQuerySpecRepository(TestEntity.class, RelatedEntity.class); - for(long i = 0; i < 5;i++){ - RelatedEntity related1 = new RelatedEntity(); - related1.setId(i); - related1.setStringValue("related" + i); - relatedRepo.save(related1); - - relRepo.addRelations(test, Arrays.asList(i), TestEntity.ATTR_manyRelatedValues); - } - - QuerySpec querySpec = new QuerySpec(RelatedEntity.class); - querySpec.setOffset(2L); - querySpec.setLimit(2L); - - ResourceList list = relRepo.findManyTargets(test.getId(), TestEntity.ATTR_manyRelatedValues, querySpec); - Assert.assertEquals(2, list.size()); - Assert.assertEquals(2, list.get(0).getId().intValue()); - Assert.assertEquals(3, list.get(1).getId().intValue()); - - JsonMetaInformation meta = list.getMetaInformation(JsonMetaInformation.class); - JsonLinksInformation links = list.getLinksInformation(JsonLinksInformation.class); - Assert.assertNotNull(meta); - Assert.assertNotNull(links); - - String baseUri = getBaseUri().toString(); - Assert.assertEquals(baseUri + "test/1/relationships/manyRelatedValues/?page[limit]=2", links.asJsonNode().get("first").asText()); - Assert.assertEquals(baseUri + "test/1/relationships/manyRelatedValues/?page[limit]=2&page[offset]=4", links.asJsonNode().get("last").asText()); - Assert.assertEquals(baseUri + "test/1/relationships/manyRelatedValues/?page[limit]=2", links.asJsonNode().get("prev").asText()); - Assert.assertEquals(baseUri + "test/1/relationships/manyRelatedValues/?page[limit]=2&page[offset]=4", links.asJsonNode().get("next").asText()); - } - - @Test - public void testOptimisticLocking() { - QuerySpecResourceRepositoryStub repo = client - .getQuerySpecRepository(VersionedEntity.class); - VersionedEntity entity = new VersionedEntity(); - entity.setId(1L); - entity.setLongValue(13L); - VersionedEntity saved = repo.save(entity); - Assert.assertEquals(0, saved.getVersion()); - - saved.setLongValue(14L); - saved = repo.save(saved); - Assert.assertEquals(1, saved.getVersion()); - - saved.setLongValue(15L); - saved = repo.save(saved); - Assert.assertEquals(2, saved.getVersion()); - - saved.setLongValue(16L); - saved.setVersion(saved.getVersion() - 1); - try { - saved = repo.save(saved); - Assert.fail(); - } - catch (OptimisticLockException e) { - // ok - } - - VersionedEntity persisted = repo.findOne(1L, new QuerySpec(VersionedEntity.class)); - Assert.assertEquals(2, persisted.getVersion()); - Assert.assertEquals(15L, persisted.getLongValue()); - } - - @Test - public void testDelete() { - TestEntity test = new TestEntity(); - test.setId(1L); - test.setStringValue("test"); - testRepo.save(test); - - testRepo.delete(1L); - - List list = testRepo.findAll(new QuerySpec(TestEntity.class)); - Assert.assertEquals(0, list.size()); - } - - private QuerySpec includeOneRelatedValueParams() { - QuerySpec querySpec = new QuerySpec(TestEntity.class); - querySpec.includeRelation(Arrays.asList(TestEntity.ATTR_oneRelatedValue)); - return querySpec; - } - - private QuerySpec includeManyRelatedValueParams() { - QuerySpec querySpec = new QuerySpec(TestEntity.class); - querySpec.includeRelation(Arrays.asList(TestEntity.ATTR_manyRelatedValues)); - return querySpec; - } - - @Test - public void testSaveOneRelation() { - TestEntity test = addTestWithOneRelation(); - - TestEntity savedTest = testRepo.findOne(2L, includeOneRelatedValueParams()); - Assert.assertEquals(test.getId(), savedTest.getId()); - Assert.assertEquals(test.getStringValue(), savedTest.getStringValue()); - Assert.assertNotNull(savedTest.getOneRelatedValue()); - Assert.assertEquals(1L, savedTest.getOneRelatedValue().getId().longValue()); - } - - @Test - public void testEagerOneRelation() { - ResourceRepositoryStub relatedRepo = client.getRepository(RelatedEntity.class); - RelatedEntity related = new RelatedEntity(); - related.setId(1L); - related.setStringValue("project"); - relatedRepo.save(related); - - TestEntity test = new TestEntity(); - test.setId(2L); - test.setStringValue("test"); - test.setEagerRelatedValue(related); - testRepo.save(test); - - TestEntity savedTest = testRepo.findOne(2L, new QuerySpec(TestEntity.class)); - Assert.assertEquals(test.getId(), savedTest.getId()); - Assert.assertEquals(test.getStringValue(), savedTest.getStringValue()); - Assert.assertNull(savedTest.getOneRelatedValue()); - Assert.assertNotNull(savedTest.getEagerRelatedValue()); - Assert.assertEquals(1L, savedTest.getEagerRelatedValue().getId().longValue()); - } - - @Test - public void testEmbeddableIds() throws InstantiationException, IllegalAccessException { - QuerySpecResourceRepositoryStub rep = client - .getQuerySpecRepository(TestEmbeddedIdEntity.class); - - // add - TestEmbeddedIdEntity entity = new TestEmbeddedIdEntity(); - entity.setId(new TestIdEmbeddable(13, "test")); - entity.setLongValue(100L); - rep.save(entity); - - List list = rep.findAll(new QuerySpec(TestEntity.class)); - Assert.assertEquals(1, list.size()); - TestEmbeddedIdEntity savedEntity = list.get(0); - Assert.assertNotNull(savedEntity); - Assert.assertEquals(100L, savedEntity.getLongValue()); - Assert.assertEquals(13, savedEntity.getId().getEmbIntValue().intValue()); - Assert.assertEquals("test", savedEntity.getId().getEmbStringValue()); - - // update - savedEntity.setLongValue(101L); - rep.save(savedEntity); - list = rep.findAll(new QuerySpec(TestEntity.class)); - Assert.assertEquals(1, list.size()); - savedEntity = list.get(0); - Assert.assertEquals(101L, savedEntity.getLongValue()); - - // delete - rep.delete(entity.getId()); - list = rep.findAll(new QuerySpec(TestEntity.class)); - Assert.assertEquals(0, list.size()); - } - - private TestEntity addTestWithOneRelation() { - QuerySpecResourceRepositoryStub relatedRepo = client.getQuerySpecRepository(RelatedEntity.class); - RelatedEntity related = new RelatedEntity(); - related.setId(1L); - related.setStringValue("project"); - relatedRepo.save(related); - - TestEntity test = new TestEntity(); - test.setId(2L); - test.setStringValue("test"); - test.setOneRelatedValue(related); - testRepo.save(test, includeOneRelatedValueParams()); - return test; - } +package io.katharsis.jpa; + +import java.io.Serializable; +import java.util.Arrays; +import java.util.List; + +import javax.persistence.OptimisticLockException; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; + +import io.katharsis.client.QuerySpecRelationshipRepositoryStub; +import io.katharsis.client.QuerySpecResourceRepositoryStub; +import io.katharsis.client.ResourceRepositoryStub; +import io.katharsis.client.response.JsonLinksInformation; +import io.katharsis.client.response.JsonMetaInformation; +import io.katharsis.client.response.ResourceList; +import io.katharsis.jpa.model.RelatedEntity; +import io.katharsis.jpa.model.TestEmbeddedIdEntity; +import io.katharsis.jpa.model.TestEntity; +import io.katharsis.jpa.model.TestIdEmbeddable; +import io.katharsis.jpa.model.VersionedEntity; +import io.katharsis.queryspec.QuerySpec; + +public class JpaQuerySpecEndToEndTest extends AbstractJpaJerseyTest { + + private QuerySpecResourceRepositoryStub testRepo; + + @Override + @Before + public void setup() { + super.setup(); + testRepo = client.getQuerySpecRepository(TestEntity.class); + } + + @Test + public void testIncludeRelations() throws InstantiationException, IllegalAccessException { + addTestWithOneRelation(); + + QuerySpec querySpec = new QuerySpec(TestEntity.class); + querySpec.includeRelation(Arrays.asList(TestEntity.ATTR_oneRelatedValue)); + List list = testRepo.findAll(querySpec); + + Assert.assertEquals(1, list.size()); + for (TestEntity test : list) { + Assert.assertNotNull(test.getOneRelatedValue()); + } + } + + @Test + public void testFindOneTargetWithNullResult() throws InstantiationException, IllegalAccessException { + TestEntity test = new TestEntity(); + test.setId(2L); + test.setStringValue("test"); + testRepo.save(test); + + QuerySpecRelationshipRepositoryStub relRepo = client + .getQuerySpecRepository(TestEntity.class, RelatedEntity.class); + + RelatedEntity related = relRepo.findOneTarget(test.getId(), TestEntity.ATTR_oneRelatedValue, + new QuerySpec(RelatedEntity.class)); + Assert.assertNull(related); + } + + @Test + public void testFindOneTarget() throws InstantiationException, IllegalAccessException { + TestEntity test = addTestWithOneRelation(); + + QuerySpecRelationshipRepositoryStub relRepo = client + .getQuerySpecRepository(TestEntity.class, RelatedEntity.class); + + RelatedEntity related = relRepo.findOneTarget(test.getId(), TestEntity.ATTR_oneRelatedValue, + new QuerySpec(RelatedEntity.class)); + Assert.assertNotNull(related); + } + + @Test + public void testAddManyRelationWithRelationshipRepository() throws InstantiationException, IllegalAccessException { + testAddManyRelation(false); + } + + @Test + @Ignore + // TODO bidirectionality not properly handled, see + // ResourceUpsert should make use of relationship repositories #130 + public void testAddManyRelationWithResourceSave() throws InstantiationException, IllegalAccessException { + testAddManyRelation(true); + } + + private void testAddManyRelation(boolean onSave) throws InstantiationException, IllegalAccessException { + QuerySpecResourceRepositoryStub relatedRepo = client.getQuerySpecRepository(RelatedEntity.class); + RelatedEntity related1 = new RelatedEntity(); + related1.setId(1L); + related1.setStringValue("related1"); + relatedRepo.save(related1); + + RelatedEntity related2 = new RelatedEntity(); + related2.setId(2L); + related2.setStringValue("related2"); + relatedRepo.save(related2); + + TestEntity test = new TestEntity(); + test.setId(3L); + test.setStringValue("test"); + if (onSave) { + test.setManyRelatedValues(Arrays.asList(related1, related2)); + } + testRepo.save(test, includeManyRelatedValueParams()); + + // query relation + QuerySpecRelationshipRepositoryStub relRepo = client + .getQuerySpecRepository(TestEntity.class, RelatedEntity.class); + if (!onSave) { + relRepo.addRelations(test, Arrays.asList(1L, 2L), TestEntity.ATTR_manyRelatedValues); + } + List related = relRepo.findManyTargets(test.getId(), TestEntity.ATTR_manyRelatedValues, + new QuerySpec(RelatedEntity.class)); + Assert.assertEquals(2, related.size()); + + // query relation in opposite direction + QuerySpecRelationshipRepositoryStub backRelRepo = client + .getQuerySpecRepository(RelatedEntity.class, TestEntity.class); + test = backRelRepo.findOneTarget(2L, RelatedEntity.ATTR_testEntity, new QuerySpec(TestEntity.class)); + Assert.assertNotNull(test); + Assert.assertEquals(3L, test.getId().longValue()); + } + + @Test + public void testIncludeNoRelations() throws InstantiationException, IllegalAccessException { + addTestWithOneRelation(); + + List list = testRepo.findAll(new QuerySpec(TestEntity.class)); + Assert.assertEquals(1, list.size()); + for (TestEntity test : list) { + // in the future we may get proxies here + Assert.assertNull(test.getOneRelatedValue()); + } + } + + @Test + public void testFindEmpty() { + List list = testRepo.findAll(new QuerySpec(TestEntity.class)); + Assert.assertTrue(list.isEmpty()); + } + + @Test + public void testFindNull() { + TestEntity test = testRepo.findOne(1L, new QuerySpec(TestEntity.class)); + Assert.assertNull(test); + } + + @Test + public void testSaveAndFind() { + TestEntity task = new TestEntity(); + task.setId(1L); + task.setStringValue("test"); + testRepo.save(task); + + // check retrievable with findAll + List list = testRepo.findAll(new QuerySpec(TestEntity.class)); + Assert.assertEquals(1, list.size()); + TestEntity savedTask = list.get(0); + Assert.assertEquals(task.getId(), savedTask.getId()); + Assert.assertEquals(task.getStringValue(), savedTask.getStringValue()); + + // check retrievable with findAll(ids) + list = testRepo.findAll(Arrays.asList(1L), new QuerySpec(TestEntity.class)); + Assert.assertEquals(1, list.size()); + savedTask = list.get(0); + Assert.assertEquals(task.getId(), savedTask.getId()); + Assert.assertEquals(task.getStringValue(), savedTask.getStringValue()); + + // check retrievable with findOne + savedTask = testRepo.findOne(1L, new QuerySpec(TestEntity.class)); + Assert.assertEquals(task.getId(), savedTask.getId()); + Assert.assertEquals(task.getStringValue(), savedTask.getStringValue()); + } + + @Test + public void testRootPaging() { + for (long i = 0; i < 5; i++) { + TestEntity task = new TestEntity(); + task.setId(i); + task.setStringValue("test"); + testRepo.save(task); + } + + QuerySpec querySpec = new QuerySpec(TestEntity.class); + querySpec.setOffset(2L); + querySpec.setLimit(2L); + + ResourceList list = testRepo.findAll(querySpec); + Assert.assertEquals(2, list.size()); + Assert.assertEquals(2, list.get(0).getId().intValue()); + Assert.assertEquals(3, list.get(1).getId().intValue()); + + JsonMetaInformation meta = list.getMetaInformation(JsonMetaInformation.class); + JsonLinksInformation links = list.getLinksInformation(JsonLinksInformation.class); + Assert.assertNotNull(meta); + Assert.assertNotNull(links); + + String baseUri = getBaseUri().toString(); + Assert.assertEquals(baseUri + "test/?page[limit]=2", links.asJsonNode().get("first").asText()); + Assert.assertEquals(baseUri + "test/?page[limit]=2&page[offset]=4", links.asJsonNode().get("last").asText()); + Assert.assertEquals(baseUri + "test/?page[limit]=2", links.asJsonNode().get("prev").asText()); + Assert.assertEquals(baseUri + "test/?page[limit]=2&page[offset]=4", links.asJsonNode().get("next").asText()); + } + + @Test + public void testRelationPaging() { + TestEntity test = new TestEntity(); + test.setId(1L); + test.setStringValue("test"); + testRepo.save(test); + + ResourceRepositoryStub relatedRepo = client.getRepository(RelatedEntity.class); + QuerySpecRelationshipRepositoryStub relRepo = client + .getQuerySpecRepository(TestEntity.class, RelatedEntity.class); + for(long i = 0; i < 5;i++){ + RelatedEntity related1 = new RelatedEntity(); + related1.setId(i); + related1.setStringValue("related" + i); + relatedRepo.save(related1); + + relRepo.addRelations(test, Arrays.asList(i), TestEntity.ATTR_manyRelatedValues); + } + + QuerySpec querySpec = new QuerySpec(RelatedEntity.class); + querySpec.setOffset(2L); + querySpec.setLimit(2L); + + ResourceList list = relRepo.findManyTargets(test.getId(), TestEntity.ATTR_manyRelatedValues, querySpec); + Assert.assertEquals(2, list.size()); + Assert.assertEquals(2, list.get(0).getId().intValue()); + Assert.assertEquals(3, list.get(1).getId().intValue()); + + JsonMetaInformation meta = list.getMetaInformation(JsonMetaInformation.class); + JsonLinksInformation links = list.getLinksInformation(JsonLinksInformation.class); + Assert.assertNotNull(meta); + Assert.assertNotNull(links); + + String baseUri = getBaseUri().toString(); + Assert.assertEquals(baseUri + "test/1/relationships/manyRelatedValues/?page[limit]=2", links.asJsonNode().get("first").asText()); + Assert.assertEquals(baseUri + "test/1/relationships/manyRelatedValues/?page[limit]=2&page[offset]=4", links.asJsonNode().get("last").asText()); + Assert.assertEquals(baseUri + "test/1/relationships/manyRelatedValues/?page[limit]=2", links.asJsonNode().get("prev").asText()); + Assert.assertEquals(baseUri + "test/1/relationships/manyRelatedValues/?page[limit]=2&page[offset]=4", links.asJsonNode().get("next").asText()); + } + + @Test + public void testOptimisticLocking() { + QuerySpecResourceRepositoryStub repo = client + .getQuerySpecRepository(VersionedEntity.class); + VersionedEntity entity = new VersionedEntity(); + entity.setId(1L); + entity.setLongValue(13L); + VersionedEntity saved = repo.save(entity); + Assert.assertEquals(0, saved.getVersion()); + + saved.setLongValue(14L); + saved = repo.save(saved); + Assert.assertEquals(1, saved.getVersion()); + + saved.setLongValue(15L); + saved = repo.save(saved); + Assert.assertEquals(2, saved.getVersion()); + + saved.setLongValue(16L); + saved.setVersion(saved.getVersion() - 1); + try { + saved = repo.save(saved); + Assert.fail(); + } + catch (OptimisticLockException e) { + // ok + } + + VersionedEntity persisted = repo.findOne(1L, new QuerySpec(VersionedEntity.class)); + Assert.assertEquals(2, persisted.getVersion()); + Assert.assertEquals(15L, persisted.getLongValue()); + } + + @Test + public void testDelete() { + TestEntity test = new TestEntity(); + test.setId(1L); + test.setStringValue("test"); + testRepo.save(test); + + testRepo.delete(1L); + + List list = testRepo.findAll(new QuerySpec(TestEntity.class)); + Assert.assertEquals(0, list.size()); + } + + private QuerySpec includeOneRelatedValueParams() { + QuerySpec querySpec = new QuerySpec(TestEntity.class); + querySpec.includeRelation(Arrays.asList(TestEntity.ATTR_oneRelatedValue)); + return querySpec; + } + + private QuerySpec includeManyRelatedValueParams() { + QuerySpec querySpec = new QuerySpec(TestEntity.class); + querySpec.includeRelation(Arrays.asList(TestEntity.ATTR_manyRelatedValues)); + return querySpec; + } + + @Test + public void testSaveOneRelation() { + TestEntity test = addTestWithOneRelation(); + + TestEntity savedTest = testRepo.findOne(2L, includeOneRelatedValueParams()); + Assert.assertEquals(test.getId(), savedTest.getId()); + Assert.assertEquals(test.getStringValue(), savedTest.getStringValue()); + Assert.assertNotNull(savedTest.getOneRelatedValue()); + Assert.assertEquals(1L, savedTest.getOneRelatedValue().getId().longValue()); + } + + @Test + public void testEagerOneRelation() { + ResourceRepositoryStub relatedRepo = client.getRepository(RelatedEntity.class); + RelatedEntity related = new RelatedEntity(); + related.setId(1L); + related.setStringValue("project"); + relatedRepo.save(related); + + TestEntity test = new TestEntity(); + test.setId(2L); + test.setStringValue("test"); + test.setEagerRelatedValue(related); + testRepo.save(test); + + TestEntity savedTest = testRepo.findOne(2L, new QuerySpec(TestEntity.class)); + Assert.assertEquals(test.getId(), savedTest.getId()); + Assert.assertEquals(test.getStringValue(), savedTest.getStringValue()); + Assert.assertNull(savedTest.getOneRelatedValue()); + + // TOOD should @JsonApiIncludeByDefault trigger this? + // Assert.assertNotNull(savedTest.getEagerRelatedValue()); + // Assert.assertEquals(1L, savedTest.getEagerRelatedValue().getId().longValue()); + } + + @Test + public void testEmbeddableIds() throws InstantiationException, IllegalAccessException { + QuerySpecResourceRepositoryStub rep = client + .getQuerySpecRepository(TestEmbeddedIdEntity.class); + + // add + TestEmbeddedIdEntity entity = new TestEmbeddedIdEntity(); + entity.setId(new TestIdEmbeddable(13, "test")); + entity.setLongValue(100L); + rep.save(entity); + + List list = rep.findAll(new QuerySpec(TestEntity.class)); + Assert.assertEquals(1, list.size()); + TestEmbeddedIdEntity savedEntity = list.get(0); + Assert.assertNotNull(savedEntity); + Assert.assertEquals(100L, savedEntity.getLongValue()); + Assert.assertEquals(13, savedEntity.getId().getEmbIntValue().intValue()); + Assert.assertEquals("test", savedEntity.getId().getEmbStringValue()); + + // update + savedEntity.setLongValue(101L); + rep.save(savedEntity); + list = rep.findAll(new QuerySpec(TestEntity.class)); + Assert.assertEquals(1, list.size()); + savedEntity = list.get(0); + Assert.assertEquals(101L, savedEntity.getLongValue()); + + // delete + rep.delete(entity.getId()); + list = rep.findAll(new QuerySpec(TestEntity.class)); + Assert.assertEquals(0, list.size()); + } + + private TestEntity addTestWithOneRelation() { + QuerySpecResourceRepositoryStub relatedRepo = client.getQuerySpecRepository(RelatedEntity.class); + RelatedEntity related = new RelatedEntity(); + related.setId(1L); + related.setStringValue("project"); + relatedRepo.save(related); + + TestEntity test = new TestEntity(); + test.setId(2L); + test.setStringValue("test"); + test.setOneRelatedValue(related); + testRepo.save(test, includeOneRelatedValueParams()); + return test; + } } \ No newline at end of file diff --git a/katharsis-jpa/src/test/java/io/katharsis/jpa/mapping/DtoMappingTest.java b/katharsis-jpa/src/test/java/io/katharsis/jpa/mapping/DtoMappingTest.java new file mode 100644 index 00000000..d155da77 --- /dev/null +++ b/katharsis-jpa/src/test/java/io/katharsis/jpa/mapping/DtoMappingTest.java @@ -0,0 +1,265 @@ +package io.katharsis.jpa.mapping; + +import java.io.Serializable; +import java.util.Arrays; +import java.util.List; + +import javax.persistence.EntityManager; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import com.querydsl.core.types.Expression; +import com.querydsl.jpa.JPAExpressions; +import com.querydsl.jpa.impl.JPAQuery; + +import io.katharsis.client.QuerySpecRelationshipRepositoryStub; +import io.katharsis.client.QuerySpecResourceRepositoryStub; +import io.katharsis.client.response.ResourceList; +import io.katharsis.jpa.AbstractJpaJerseyTest; +import io.katharsis.jpa.JpaModule; +import io.katharsis.jpa.JpaRepositoryFilterBase; +import io.katharsis.jpa.model.QTestEntity; +import io.katharsis.jpa.model.RelatedEntity; +import io.katharsis.jpa.model.TestEntity; +import io.katharsis.jpa.model.dto.RelatedDTO; +import io.katharsis.jpa.model.dto.TestDTO; +import io.katharsis.jpa.query.JpaQuery; +import io.katharsis.jpa.query.querydsl.QuerydslExpressionFactory; +import io.katharsis.jpa.query.querydsl.QuerydslQueryFactory; +import io.katharsis.queryspec.FilterOperator; +import io.katharsis.queryspec.FilterSpec; +import io.katharsis.queryspec.QuerySpec; + +/** + * Example of how to do DTO mapping and computed attributes. + */ +public class DtoMappingTest extends AbstractJpaJerseyTest { + + private QuerySpecResourceRepositoryStub testRepo; + + @Override + @Before + public void setup() { + super.setup(); + testRepo = client.getQuerySpecRepository(TestEntity.class); + } + + @Override + protected void setupModule(JpaModule module, boolean server) { + super.setupModule(module, server); + + if (server) { + EntityManager entityManager = module.getEntityManager(); + QuerydslExpressionFactory basicComputedValueFactory = new QuerydslExpressionFactory() { + + @Override + public Expression getExpression(QTestEntity parent, JPAQuery jpaQuery) { + return parent.stringValue.upper(); + } + }; + QuerydslExpressionFactory complexComputedValueFactory = new QuerydslExpressionFactory() { + + @Override + public Expression getExpression(QTestEntity parent, JPAQuery jpaQuery) { + QTestEntity root = QTestEntity.testEntity; + QTestEntity sub = new QTestEntity("subquery"); + return JPAExpressions.select(sub.id.count()).from(sub).where(sub.id.lt(root.id)); + } + }; + + QuerydslQueryFactory queryFactory = (QuerydslQueryFactory) module.getQueryFactory(); + queryFactory.registerComputedAttribute(TestEntity.class, TestDTO.ATTR_COMPUTED_UPPER_STRING_VALUE, String.class, + basicComputedValueFactory); + queryFactory.registerComputedAttribute(TestEntity.class, TestDTO.ATTR_COMPUTED_NUMBER_OF_SMALLER_IDS, Long.class, + complexComputedValueFactory); + module.addMappedEntityClass(TestEntity.class, TestDTO.class, new TestDTOMapper(entityManager)); + module.addMappedEntityClass(RelatedEntity.class, RelatedDTO.class, new RelatedDTOMapper(entityManager)); + + module.addFilter(new JpaRepositoryFilterBase() { + + @Override + public JpaQuery filterQuery(Object repository, QuerySpec querySpec, JpaQuery query) { + query.setDistinct(true); + return query; + } + }); + } + } + + @Test + public void testReadAndUpdateFromEntity() throws InstantiationException, IllegalAccessException { + // create as regular entity + TestEntity test = new TestEntity(); + test.setId(2L); + test.setStringValue("test"); + testRepo.save(test); + + // query as regular entity (you may want to disable that in a real application) + List list = testRepo.findAll(new QuerySpec(TestEntity.class)); + Assert.assertEquals(1, list.size()); + + // query the mapped DTO + QuerySpecResourceRepositoryStub dtoRepo = client.getQuerySpecRepository(TestDTO.class); + List dtos = dtoRepo.findAll(new QuerySpec(TestDTO.class)); + Assert.assertEquals(1, dtos.size()); + TestDTO dto = dtos.get(0); + Assert.assertEquals(2L, dto.getId().longValue()); + Assert.assertEquals("test", dto.getStringValue()); + Assert.assertEquals("TEST", dto.getComputedUpperStringValue()); + + // update the mapped dto + dto.setStringValue("newValue"); + dtoRepo.save(dto); + + // read again + dto = dtoRepo.findOne(2L, new QuerySpec(TestDTO.class)); + Assert.assertEquals(2L, dto.getId().longValue()); + Assert.assertEquals("newValue", dto.getStringValue()); + Assert.assertEquals("NEWVALUE", dto.getComputedUpperStringValue()); + + } + + @Test + public void testMappedOneRelation() { + QuerySpecResourceRepositoryStub testRepo = client.getQuerySpecRepository(TestDTO.class); + QuerySpecResourceRepositoryStub relatedRepo = client.getQuerySpecRepository(RelatedDTO.class); + QuerySpecRelationshipRepositoryStub relRepo = client + .getQuerySpecRepository(TestDTO.class, RelatedDTO.class); + + TestDTO test = new TestDTO(); + test.setId(2L); + test.setStringValue("createdDto"); + test = testRepo.save(test); + + RelatedDTO related = new RelatedDTO(); + related.setId(3L); + related.setStringValue("createdDto"); + related = relatedRepo.save(related); + + relRepo.setRelation(test, related.getId(), TestEntity.ATTR_oneRelatedValue); + + // test relationship access + RelatedDTO actualRelated = relRepo.findOneTarget(test.getId(), TestEntity.ATTR_oneRelatedValue, + new QuerySpec(RelatedDTO.class)); + Assert.assertNotNull(actualRelated); + Assert.assertEquals(related.getId(), actualRelated.getId()); + + // test include + QuerySpec querySpec = new QuerySpec(TestDTO.class); + querySpec.includeRelation(Arrays.asList(TestEntity.ATTR_oneRelatedValue)); + ResourceList list = testRepo.findAll(querySpec); + Assert.assertEquals(1, list.size()); + TestDTO actualTest = list.get(0); + actualRelated = actualTest.getOneRelatedValue(); + Assert.assertNotNull(actualRelated); + Assert.assertEquals(related.getId(), actualRelated.getId()); + } + + @Test + public void testMappedManyRelation() { + QuerySpecResourceRepositoryStub testRepo = client.getQuerySpecRepository(TestDTO.class); + QuerySpecResourceRepositoryStub relatedRepo = client.getQuerySpecRepository(RelatedDTO.class); + QuerySpecRelationshipRepositoryStub relRepo = client + .getQuerySpecRepository(TestDTO.class, RelatedDTO.class); + + TestDTO test = new TestDTO(); + test.setId(2L); + test.setStringValue("createdDto"); + test = testRepo.save(test); + + RelatedDTO related1 = new RelatedDTO(); + related1.setId(1L); + related1.setStringValue("related1"); + related1 = relatedRepo.save(related1); + + RelatedDTO related2 = new RelatedDTO(); + related2.setId(2L); + related2.setStringValue("related2"); + related2 = relatedRepo.save(related2); + + Assert.assertEquals(1, testRepo.findAll(new QuerySpec(TestDTO.class)).size()); + relRepo.addRelations(test, Arrays.asList(related1.getId(), related2.getId()), TestEntity.ATTR_manyRelatedValues); + Assert.assertEquals(1, testRepo.findAll(new QuerySpec(TestDTO.class)).size()); + + // test relationship access + List actualRelatedList = relRepo.findManyTargets(test.getId(), TestEntity.ATTR_manyRelatedValues, + new QuerySpec(RelatedDTO.class)); + Assert.assertEquals(2, actualRelatedList.size()); + + // test include + Assert.assertEquals(1, testRepo.findAll(new QuerySpec(TestDTO.class)).size()); + + // TODO distinct problem in H2 to investigate + // QuerySpec querySpec = new QuerySpec(TestDTO.class); + // querySpec.includeRelation(Arrays.asList(TestEntity.ATTR_manyRelatedValues)); + // ResourceList list = testRepo.findAll(querySpec); + // Assert.assertEquals(1, list.size()); + // TestDTO actualTest = list.get(0); + // actualRelatedList = actualTest.getManyRelatedValues(); + // Assert.assertEquals(2, actualRelatedList.size()); + + // test removal + // TODO DELETE request with body not supported by jersey? + // relRepo.removeRelations(test, Arrays.asList(related2.getId()), TestEntity.ATTR_manyRelatedValues); + // actualRelatedList = relRepo.findManyTargets(test.getId(), TestEntity.ATTR_manyRelatedValues, + // new QuerySpec(RelatedDTO.class)); + // Assert.assertEquals(1, actualRelatedList.size()); + // Assert.assertEquals(related1.getId(), actualRelatedList.get(0).getId()); + } + + @Test + public void testInsertDeleteDto() { + QuerySpecResourceRepositoryStub dtoRepo = client.getQuerySpecRepository(TestDTO.class); + + // create a entity with a DTO and check properly saved + TestDTO dto = new TestDTO(); + dto.setId(2L); + dto.setStringValue("createdDto"); + dto = dtoRepo.save(dto); + Assert.assertEquals("createdDto", dto.getStringValue()); + Assert.assertEquals("CREATEDDTO", dto.getComputedUpperStringValue()); + + // check both exists + ResourceList dtos = dtoRepo.findAll(new QuerySpec(TestDTO.class)); + Assert.assertEquals(1, dtos.size()); + dto = dtos.get(0); + Assert.assertEquals("createdDto", dto.getStringValue()); + Assert.assertEquals("CREATEDDTO", dto.getComputedUpperStringValue()); + + // test delete + dtoRepo.delete(dto.getId()); + dtos = dtoRepo.findAll(new QuerySpec(TestDTO.class)); + Assert.assertEquals(0, dtos.size()); + } + + @Test + public void testSubQueryComputation() { + QuerySpecResourceRepositoryStub dtoRepo = client.getQuerySpecRepository(TestDTO.class); + + int n = 5; + for (long i = 0; i < n; i++) { + TestDTO dto = new TestDTO(); + dto.setId(i + 100); + dto.setStringValue(Long.toString(i)); + dtoRepo.save(dto); + } + + // select, sort, filter by complex subquery + QuerySpec querySpec = new QuerySpec(TestDTO.class); + querySpec.addFilter(new FilterSpec(Arrays.asList(TestDTO.ATTR_COMPUTED_NUMBER_OF_SMALLER_IDS), FilterOperator.LT, 4)); + + // TODO enable querySpec parser + // querySpec.addSort(new SortSpec(Arrays.asList(TestDTO.ATTR_COMPUTED_NUMBER_OF_SMALLER_IDS), Direction.DESC)); + + ResourceList dtos = dtoRepo.findAll(querySpec); + Assert.assertEquals(4, dtos.size()); + for (int i = 0; i < dtos.size(); i++) { + TestDTO dto = dtos.get(i); + int j = i;// 4 - i; + Assert.assertEquals(100 + j, dto.getId().longValue()); + Assert.assertEquals(j, dto.getComputedNumberOfSmallerIds()); + } + } +} \ No newline at end of file diff --git a/katharsis-jpa/src/test/java/io/katharsis/jpa/mapping/RelatedDTOMapper.java b/katharsis-jpa/src/test/java/io/katharsis/jpa/mapping/RelatedDTOMapper.java new file mode 100644 index 00000000..dc29f68f --- /dev/null +++ b/katharsis-jpa/src/test/java/io/katharsis/jpa/mapping/RelatedDTOMapper.java @@ -0,0 +1,36 @@ +package io.katharsis.jpa.mapping; + +import javax.persistence.EntityManager; + +import io.katharsis.jpa.model.RelatedEntity; +import io.katharsis.jpa.model.dto.RelatedDTO; +import io.katharsis.jpa.query.Tuple; + +public class RelatedDTOMapper implements JpaMapper { + + private EntityManager em; + + public RelatedDTOMapper(EntityManager em) { + this.em = em; + } + + @Override + public RelatedDTO map(Tuple tuple) { + RelatedDTO dto = new RelatedDTO(); + RelatedEntity entity = tuple.get(0, RelatedEntity.class); + dto.setId(entity.getId()); + dto.setStringValue(entity.getStringValue()); + return dto; + } + + @Override + public RelatedEntity unmap(RelatedDTO dto) { + RelatedEntity entity = em.find(RelatedEntity.class, dto.getId()); + if (entity == null) { + entity = new RelatedEntity(); + entity.setId(dto.getId()); + } + entity.setStringValue(dto.getStringValue()); + return entity; + } +} diff --git a/katharsis-jpa/src/test/java/io/katharsis/jpa/mapping/TestDTOMapper.java b/katharsis-jpa/src/test/java/io/katharsis/jpa/mapping/TestDTOMapper.java new file mode 100644 index 00000000..e48ae363 --- /dev/null +++ b/katharsis-jpa/src/test/java/io/katharsis/jpa/mapping/TestDTOMapper.java @@ -0,0 +1,45 @@ +package io.katharsis.jpa.mapping; + +import javax.persistence.EntityManager; + +import io.katharsis.jpa.model.TestEntity; +import io.katharsis.jpa.model.dto.TestDTO; +import io.katharsis.jpa.query.Tuple; + +/** + * you may consider the use of MapStructor or similar tooling to + * automate this in a real application. + */ +public class TestDTOMapper implements JpaMapper { + + private EntityManager em; + + public TestDTOMapper(EntityManager em) { + this.em = em; + } + + @Override + public TestDTO map(Tuple tuple) { + TestDTO dto = new TestDTO(); + TestEntity entity = tuple.get(0, TestEntity.class); + dto.setId(entity.getId()); + dto.setStringValue(entity.getStringValue()); + dto.setComputedUpperStringValue(tuple.get("computedUpperStringValue", String.class)); + dto.setComputedNumberOfSmallerIds(tuple.get("computedNumberOfSmallerIds", Long.class)); + return dto; + } + + @Override + public TestEntity unmap(TestDTO dto) { + TestEntity entity = em.find(TestEntity.class, dto.getId()); + if (entity == null) { + entity = new TestEntity(); + entity.setId(dto.getId()); + } + entity.setStringValue(dto.getStringValue()); + + // real application may or may not choose to do something + // with the computed attribute. Usually they do not. + return entity; + } +} diff --git a/katharsis-jpa/src/test/java/io/katharsis/jpa/model/dto/RelatedDTO.java b/katharsis-jpa/src/test/java/io/katharsis/jpa/model/dto/RelatedDTO.java new file mode 100644 index 00000000..76504d52 --- /dev/null +++ b/katharsis-jpa/src/test/java/io/katharsis/jpa/model/dto/RelatedDTO.java @@ -0,0 +1,29 @@ +package io.katharsis.jpa.model.dto; + +import io.katharsis.resource.annotations.JsonApiId; +import io.katharsis.resource.annotations.JsonApiResource; + +@JsonApiResource(type = "relatedDTO") +public class RelatedDTO { + + @JsonApiId + private Long id; + + private String stringValue; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getStringValue() { + return stringValue; + } + + public void setStringValue(String stringValue) { + this.stringValue = stringValue; + } +} diff --git a/katharsis-jpa/src/test/java/io/katharsis/jpa/model/dto/TestDTO.java b/katharsis-jpa/src/test/java/io/katharsis/jpa/model/dto/TestDTO.java new file mode 100644 index 00000000..993402c3 --- /dev/null +++ b/katharsis-jpa/src/test/java/io/katharsis/jpa/model/dto/TestDTO.java @@ -0,0 +1,82 @@ +package io.katharsis.jpa.model.dto; + +import java.util.List; + +import io.katharsis.resource.annotations.JsonApiId; +import io.katharsis.resource.annotations.JsonApiLookupIncludeAutomatically; +import io.katharsis.resource.annotations.JsonApiResource; +import io.katharsis.resource.annotations.JsonApiToMany; +import io.katharsis.resource.annotations.JsonApiToOne; + +@JsonApiResource(type = "testDTO") +public class TestDTO { + + public static final String ATTR_COMPUTED_UPPER_STRING_VALUE = "computedUpperStringValue"; + + public static String ATTR_COMPUTED_NUMBER_OF_SMALLER_IDS = "computedNumberOfSmallerIds"; + + @JsonApiId + private Long id; + + private String stringValue; + + private String computedUpperStringValue; + + private long computedNumberOfSmallerIds; + + @JsonApiToOne + @JsonApiLookupIncludeAutomatically + private RelatedDTO oneRelatedValue; + + @JsonApiToMany + @JsonApiLookupIncludeAutomatically + private List manyRelatedValues; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public RelatedDTO getOneRelatedValue() { + return oneRelatedValue; + } + + public void setOneRelatedValue(RelatedDTO oneRelatedValue) { + this.oneRelatedValue = oneRelatedValue; + } + + public String getStringValue() { + return stringValue; + } + + public void setStringValue(String stringValue) { + this.stringValue = stringValue; + } + + public String getComputedUpperStringValue() { + return computedUpperStringValue; + } + + public void setComputedUpperStringValue(String computedUpperStringValue) { + this.computedUpperStringValue = computedUpperStringValue; + } + + public long getComputedNumberOfSmallerIds() { + return computedNumberOfSmallerIds; + } + + public void setComputedNumberOfSmallerIds(long computedNumberOfSmallerIds) { + this.computedNumberOfSmallerIds = computedNumberOfSmallerIds; + } + + public List getManyRelatedValues() { + return manyRelatedValues; + } + + public void setManyRelatedValues(List manyRelatedValues) { + this.manyRelatedValues = manyRelatedValues; + } +} diff --git a/katharsis-jpa/src/test/java/io/katharsis/jpa/query/AbstractJpaTest.java b/katharsis-jpa/src/test/java/io/katharsis/jpa/query/AbstractJpaTest.java index b66b54cf..38eb95ae 100644 --- a/katharsis-jpa/src/test/java/io/katharsis/jpa/query/AbstractJpaTest.java +++ b/katharsis-jpa/src/test/java/io/katharsis/jpa/query/AbstractJpaTest.java @@ -1,189 +1,189 @@ -package io.katharsis.jpa.query; - -import java.util.List; - -import javax.persistence.EntityManager; -import javax.persistence.EntityManagerFactory; -import javax.persistence.PersistenceContext; - -import org.junit.Before; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import org.springframework.transaction.PlatformTransactionManager; - -import com.fasterxml.jackson.databind.ObjectMapper; - -import io.katharsis.jpa.JpaModule; -import io.katharsis.jpa.internal.meta.MetaLookup; -import io.katharsis.jpa.model.JoinedTableBaseEntity; -import io.katharsis.jpa.model.JoinedTableChildEntity; -import io.katharsis.jpa.model.RelatedEntity; -import io.katharsis.jpa.model.SingleTableBaseEntity; -import io.katharsis.jpa.model.SingleTableChildEntity; -import io.katharsis.jpa.model.TablePerClassBaseEntity; -import io.katharsis.jpa.model.TablePerClassChildEntity; -import io.katharsis.jpa.model.TestAnyType; -import io.katharsis.jpa.model.TestEmbeddable; -import io.katharsis.jpa.model.TestEmbeddedIdEntity; -import io.katharsis.jpa.model.TestEntity; -import io.katharsis.jpa.model.TestIdEmbeddable; -import io.katharsis.jpa.model.TestNestedEmbeddable; -import io.katharsis.jpa.query.criteria.JpaCriteriaQueryFactory; -import io.katharsis.jpa.util.SpringTransactionRunner; -import io.katharsis.jpa.util.TestConfig; -import io.katharsis.module.ModuleRegistry; -import io.katharsis.resource.registry.ConstantServiceUrlProvider; -import io.katharsis.resource.registry.ResourceRegistry; - -@RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration(classes = TestConfig.class) -public abstract class AbstractJpaTest { - - @PersistenceContext - protected EntityManager em; - - @Autowired - protected EntityManagerFactory emFactory; - - protected JpaModule module; - protected JpaQueryFactory queryFactory; - - protected int numTestEntities = 5; - - @Autowired - protected PlatformTransactionManager txManager; - - @Autowired - private SpringTransactionRunner transactionRunner; - - protected ResourceRegistry resourceRegistry; - - @Before - public void setup() { - - resourceRegistry = new ResourceRegistry(new ConstantServiceUrlProvider("http://localhost:1234")); - ModuleRegistry moduleRegistry = new ModuleRegistry(); - module = new JpaModule(emFactory, em, transactionRunner); - setupModule(module); - moduleRegistry.addModule(module); - moduleRegistry.init(new ObjectMapper(), resourceRegistry); - - queryFactory = createQueryFactory(em); - module.setQueryFactory(queryFactory); - - clear(); - for (int i = 0; i < numTestEntities; i++) { - - TestEmbeddedIdEntity idEntity = new TestEmbeddedIdEntity(); - idEntity.setId(new TestIdEmbeddable(i, "test" + i)); - idEntity.setLongValue(100L + i); - em.persist(idEntity); - - RelatedEntity related = new RelatedEntity(); - related.setId(100L + i); - related.setStringValue("related" + i); - em.persist(related); - - TestAnyType anyValue = new TestAnyType(); - if (i == 0) - anyValue.setValue("first"); - else - anyValue.setValue(i); - - TestEmbeddable embValue = new TestEmbeddable(); - embValue.setEmbIntValue(i); - embValue.setEmbStringValue("emb" + i); - embValue.setNestedValue(new TestNestedEmbeddable(i == 0)); - // embValue.setRelatedValue(related); - embValue.setAnyValue(anyValue); - - TestEntity test = new TestEntity(); - test.setStringValue("test" + i); - test.setId((long) i); - test.setLongValue(i); - test.setEmbValue(embValue); - // test.setLocalTimeValue(LocalTime.now()); - // test.setOffsetDateTimeValue(OffsetDateTime.now()); - // test.setOffsetTimeValue(OffsetTime.now()); - // test.setLocalDateTimeValue(LocalDateTime.now()); - // test.setLocalDateValue(LocalDate.now()); - - // do not include relation/map for last value to check for proper - // left join sorting - if (i != numTestEntities - 1) { - test.setOneRelatedValue(related); - test.getMapValue().put("a", "a" + i); - test.getMapValue().put("b", "b" + i); - test.getMapValue().put("c", "c" + i); - } - em.persist(test); - - // inheritance - SingleTableBaseEntity singleTableBase = new SingleTableBaseEntity(); - singleTableBase.setId((long) i); - singleTableBase.setStringValue("base" + i); - em.persist(singleTableBase); - SingleTableChildEntity singleTableChild = new SingleTableChildEntity(); - singleTableChild.setId((long) i + numTestEntities); - singleTableChild.setStringValue("child" + i); - singleTableChild.setIntValue(i); - em.persist(singleTableChild); - - JoinedTableBaseEntity joinedTableBase = new JoinedTableBaseEntity(); - joinedTableBase.setId((long) i); - joinedTableBase.setStringValue("base" + i); - em.persist(joinedTableBase); - JoinedTableChildEntity joinedTableChild = new JoinedTableChildEntity(); - joinedTableChild.setId((long) i + numTestEntities); - joinedTableChild.setStringValue("child" + i); - joinedTableChild.setIntValue(i); - em.persist(joinedTableChild); - - TablePerClassBaseEntity tablePerClassBase = new TablePerClassBaseEntity(); - tablePerClassBase.setId((long) i); - tablePerClassBase.setStringValue("base" + i); - em.persist(tablePerClassBase); - TablePerClassChildEntity tablePerClassChild = new TablePerClassChildEntity(); - tablePerClassChild.setId((long) i + numTestEntities); - tablePerClassChild.setStringValue("child" + i); - tablePerClassChild.setIntValue(i); - em.persist(tablePerClassChild); - } - em.flush(); - em.clear(); - - queryFactory = createQueryFactory(em); - } - - /** - * Implement this to switch between Criteria und QueryDSL. - */ - protected abstract JpaQueryFactory createQueryFactory(EntityManager em); - - protected void setupModule(JpaModule module2) { - } - - private void clear() { - clear(em, createQueryFactory(em)); - } - - public static void clear(EntityManager em) { - clear(em, JpaCriteriaQueryFactory.newInstance(new MetaLookup(), em)); - } - - public static void clear(EntityManager em, JpaQueryFactory factory) { - clear(em, factory.query(RelatedEntity.class).buildExecutor().getResultList()); - clear(em, factory.query(TestEntity.class).buildExecutor().getResultList()); - em.flush(); - em.clear(); - } - - private static void clear(EntityManager em, List list) { - for (Object obj : list) { - em.remove(obj); - } - } - -} +package io.katharsis.jpa.query; + +import java.util.List; + +import javax.persistence.EntityManager; +import javax.persistence.EntityManagerFactory; +import javax.persistence.PersistenceContext; + +import org.junit.Before; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.transaction.PlatformTransactionManager; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import io.katharsis.jpa.JpaModule; +import io.katharsis.jpa.internal.meta.MetaLookup; +import io.katharsis.jpa.model.JoinedTableBaseEntity; +import io.katharsis.jpa.model.JoinedTableChildEntity; +import io.katharsis.jpa.model.RelatedEntity; +import io.katharsis.jpa.model.SingleTableBaseEntity; +import io.katharsis.jpa.model.SingleTableChildEntity; +import io.katharsis.jpa.model.TablePerClassBaseEntity; +import io.katharsis.jpa.model.TablePerClassChildEntity; +import io.katharsis.jpa.model.TestAnyType; +import io.katharsis.jpa.model.TestEmbeddable; +import io.katharsis.jpa.model.TestEmbeddedIdEntity; +import io.katharsis.jpa.model.TestEntity; +import io.katharsis.jpa.model.TestIdEmbeddable; +import io.katharsis.jpa.model.TestNestedEmbeddable; +import io.katharsis.jpa.query.criteria.JpaCriteriaQueryFactory; +import io.katharsis.jpa.util.SpringTransactionRunner; +import io.katharsis.jpa.util.TestConfig; +import io.katharsis.module.ModuleRegistry; +import io.katharsis.resource.registry.ConstantServiceUrlProvider; +import io.katharsis.resource.registry.ResourceRegistry; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = TestConfig.class) +public abstract class AbstractJpaTest { + + @PersistenceContext + protected EntityManager em; + + @Autowired + protected EntityManagerFactory emFactory; + + protected JpaModule module; + protected JpaQueryFactory queryFactory; + + protected int numTestEntities = 5; + + @Autowired + protected PlatformTransactionManager txManager; + + @Autowired + private SpringTransactionRunner transactionRunner; + + protected ResourceRegistry resourceRegistry; + + @Before + public void setup() { + + resourceRegistry = new ResourceRegistry(new ConstantServiceUrlProvider("http://localhost:1234")); + ModuleRegistry moduleRegistry = new ModuleRegistry(); + module = JpaModule.newServerModule(emFactory, em, transactionRunner); + setupModule(module); + moduleRegistry.addModule(module); + moduleRegistry.init(new ObjectMapper(), resourceRegistry); + + queryFactory = createQueryFactory(em); + module.setQueryFactory(queryFactory); + + clear(); + for (int i = 0; i < numTestEntities; i++) { + + TestEmbeddedIdEntity idEntity = new TestEmbeddedIdEntity(); + idEntity.setId(new TestIdEmbeddable(i, "test" + i)); + idEntity.setLongValue(100L + i); + em.persist(idEntity); + + RelatedEntity related = new RelatedEntity(); + related.setId(100L + i); + related.setStringValue("related" + i); + em.persist(related); + + TestAnyType anyValue = new TestAnyType(); + if (i == 0) + anyValue.setValue("first"); + else + anyValue.setValue(i); + + TestEmbeddable embValue = new TestEmbeddable(); + embValue.setEmbIntValue(i); + embValue.setEmbStringValue("emb" + i); + embValue.setNestedValue(new TestNestedEmbeddable(i == 0)); + // embValue.setRelatedValue(related); + embValue.setAnyValue(anyValue); + + TestEntity test = new TestEntity(); + test.setStringValue("test" + i); + test.setId((long) i); + test.setLongValue(i); + test.setEmbValue(embValue); + // test.setLocalTimeValue(LocalTime.now()); + // test.setOffsetDateTimeValue(OffsetDateTime.now()); + // test.setOffsetTimeValue(OffsetTime.now()); + // test.setLocalDateTimeValue(LocalDateTime.now()); + // test.setLocalDateValue(LocalDate.now()); + + // do not include relation/map for last value to check for proper + // left join sorting + if (i != numTestEntities - 1) { + test.setOneRelatedValue(related); + test.getMapValue().put("a", "a" + i); + test.getMapValue().put("b", "b" + i); + test.getMapValue().put("c", "c" + i); + } + em.persist(test); + + // inheritance + SingleTableBaseEntity singleTableBase = new SingleTableBaseEntity(); + singleTableBase.setId((long) i); + singleTableBase.setStringValue("base" + i); + em.persist(singleTableBase); + SingleTableChildEntity singleTableChild = new SingleTableChildEntity(); + singleTableChild.setId((long) i + numTestEntities); + singleTableChild.setStringValue("child" + i); + singleTableChild.setIntValue(i); + em.persist(singleTableChild); + + JoinedTableBaseEntity joinedTableBase = new JoinedTableBaseEntity(); + joinedTableBase.setId((long) i); + joinedTableBase.setStringValue("base" + i); + em.persist(joinedTableBase); + JoinedTableChildEntity joinedTableChild = new JoinedTableChildEntity(); + joinedTableChild.setId((long) i + numTestEntities); + joinedTableChild.setStringValue("child" + i); + joinedTableChild.setIntValue(i); + em.persist(joinedTableChild); + + TablePerClassBaseEntity tablePerClassBase = new TablePerClassBaseEntity(); + tablePerClassBase.setId((long) i); + tablePerClassBase.setStringValue("base" + i); + em.persist(tablePerClassBase); + TablePerClassChildEntity tablePerClassChild = new TablePerClassChildEntity(); + tablePerClassChild.setId((long) i + numTestEntities); + tablePerClassChild.setStringValue("child" + i); + tablePerClassChild.setIntValue(i); + em.persist(tablePerClassChild); + } + em.flush(); + em.clear(); + + queryFactory = createQueryFactory(em); + } + + /** + * Implement this to switch between Criteria und QueryDSL. + */ + protected abstract JpaQueryFactory createQueryFactory(EntityManager em); + + protected void setupModule(JpaModule module2) { + } + + private void clear() { + clear(em, createQueryFactory(em)); + } + + public static void clear(EntityManager em) { + clear(em, JpaCriteriaQueryFactory.newInstance(new MetaLookup(), em)); + } + + public static void clear(EntityManager em, JpaQueryFactory factory) { + clear(em, factory.query(RelatedEntity.class).buildExecutor().getResultList()); + clear(em, factory.query(TestEntity.class).buildExecutor().getResultList()); + em.flush(); + em.clear(); + } + + private static void clear(EntityManager em, List list) { + for (Object obj : list) { + em.remove(obj); + } + } + +} diff --git a/katharsis-jpa/src/test/java/io/katharsis/jpa/query/VirtualAttributeTestBase.java b/katharsis-jpa/src/test/java/io/katharsis/jpa/query/ComputedAttributeTestBase.java similarity index 91% rename from katharsis-jpa/src/test/java/io/katharsis/jpa/query/VirtualAttributeTestBase.java rename to katharsis-jpa/src/test/java/io/katharsis/jpa/query/ComputedAttributeTestBase.java index d74a0677..8d8a0476 100644 --- a/katharsis-jpa/src/test/java/io/katharsis/jpa/query/VirtualAttributeTestBase.java +++ b/katharsis-jpa/src/test/java/io/katharsis/jpa/query/ComputedAttributeTestBase.java @@ -1,47 +1,47 @@ -package io.katharsis.jpa.query; - -import static org.junit.Assert.assertEquals; - -import java.util.Arrays; -import java.util.List; - -import org.junit.Assert; -import org.junit.Test; -import org.springframework.transaction.annotation.Transactional; - -import io.katharsis.jpa.model.TestEntity; -import io.katharsis.queryspec.Direction; -import io.katharsis.queryspec.FilterOperator; - -@Transactional -public abstract class VirtualAttributeTestBase extends AbstractJpaTest { - - protected static final String ATTR_VIRTUAL_VALUE = "virtualValue"; - - private JpaQuery builder() { - return queryFactory.query(TestEntity.class); - } - - @Test - public void testEqualsFilter() { - - assertEquals((Long) 1L, builder().addFilter(ATTR_VIRTUAL_VALUE, FilterOperator.EQ, "TEST1").buildExecutor() - .getUniqueResult(false).getId()); - - } - - @Test - public void testSelection() { - JpaQuery query = builder(); - query.addSelection(Arrays.asList(ATTR_VIRTUAL_VALUE)); - query.addSortBy(Arrays.asList(TestEntity.ATTR_stringValue), Direction.ASC); - - List resultList = query.buildExecutor().getResultTuples(); - Assert.assertEquals(5, resultList.size()); - for (int i = 0; i < resultList.size(); i++) { - Tuple tuple = resultList.get(i); - Assert.assertEquals("TEST" + i, tuple.get(ATTR_VIRTUAL_VALUE, String.class)); - } - } - -} +package io.katharsis.jpa.query; + +import static org.junit.Assert.assertEquals; + +import java.util.Arrays; +import java.util.List; + +import org.junit.Assert; +import org.junit.Test; +import org.springframework.transaction.annotation.Transactional; + +import io.katharsis.jpa.model.TestEntity; +import io.katharsis.queryspec.Direction; +import io.katharsis.queryspec.FilterOperator; + +@Transactional +public abstract class ComputedAttributeTestBase extends AbstractJpaTest { + + protected static final String ATTR_VIRTUAL_VALUE = "virtualValue"; + + private JpaQuery builder() { + return queryFactory.query(TestEntity.class); + } + + @Test + public void testEqualsFilter() { + + assertEquals((Long) 1L, builder().addFilter(ATTR_VIRTUAL_VALUE, FilterOperator.EQ, "TEST1").buildExecutor() + .getUniqueResult(false).getId()); + + } + + @Test + public void testSelection() { + JpaQuery query = builder(); + query.addSelection(Arrays.asList(ATTR_VIRTUAL_VALUE)); + query.addSortBy(Arrays.asList(TestEntity.ATTR_stringValue), Direction.ASC); + + List resultList = query.buildExecutor().getResultTuples(); + Assert.assertEquals(5, resultList.size()); + for (int i = 0; i < resultList.size(); i++) { + Tuple tuple = resultList.get(i); + Assert.assertEquals("TEST" + i, tuple.get(ATTR_VIRTUAL_VALUE, String.class)); + } + } + +} diff --git a/katharsis-jpa/src/test/java/io/katharsis/jpa/query/criteria/VirtualAttributeCriteriaTest.java b/katharsis-jpa/src/test/java/io/katharsis/jpa/query/criteria/ComputedAttributeCriteriaTest.java similarity index 79% rename from katharsis-jpa/src/test/java/io/katharsis/jpa/query/criteria/VirtualAttributeCriteriaTest.java rename to katharsis-jpa/src/test/java/io/katharsis/jpa/query/criteria/ComputedAttributeCriteriaTest.java index 9c213b0a..bf1f8aa1 100644 --- a/katharsis-jpa/src/test/java/io/katharsis/jpa/query/criteria/VirtualAttributeCriteriaTest.java +++ b/katharsis-jpa/src/test/java/io/katharsis/jpa/query/criteria/ComputedAttributeCriteriaTest.java @@ -1,33 +1,33 @@ -package io.katharsis.jpa.query.criteria; - -import javax.persistence.EntityManager; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.CriteriaQuery; -import javax.persistence.criteria.Expression; -import javax.persistence.criteria.From; -import javax.persistence.criteria.Path; - -import io.katharsis.jpa.model.TestEntity; -import io.katharsis.jpa.query.JpaQueryFactory; -import io.katharsis.jpa.query.VirtualAttributeTestBase; - -public class VirtualAttributeCriteriaTest extends VirtualAttributeTestBase { - - @Override - protected JpaQueryFactory createQueryFactory(final EntityManager em) { - JpaCriteriaQueryFactory factory = JpaCriteriaQueryFactory.newInstance(module.getMetaLookup(), em); - - factory.registerVirtualAttribute(TestEntity.class, ATTR_VIRTUAL_VALUE, String.class, - new JpaCriteriaExpressionFactory>() { - - @Override - public Expression getExpression(From parent, CriteriaQuery query) { - CriteriaBuilder builder = em.getCriteriaBuilder(); - Path stringValue = parent.get(TestEntity.ATTR_stringValue); - return builder.upper(stringValue); - } - }); - - return factory; - } -} +package io.katharsis.jpa.query.criteria; + +import javax.persistence.EntityManager; +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Expression; +import javax.persistence.criteria.From; +import javax.persistence.criteria.Path; + +import io.katharsis.jpa.model.TestEntity; +import io.katharsis.jpa.query.JpaQueryFactory; +import io.katharsis.jpa.query.ComputedAttributeTestBase; + +public class ComputedAttributeCriteriaTest extends ComputedAttributeTestBase { + + @Override + protected JpaQueryFactory createQueryFactory(final EntityManager em) { + JpaCriteriaQueryFactory factory = JpaCriteriaQueryFactory.newInstance(module.getMetaLookup(), em); + + factory.registerComputedAttribute(TestEntity.class, ATTR_VIRTUAL_VALUE, String.class, + new JpaCriteriaExpressionFactory>() { + + @Override + public Expression getExpression(From parent, CriteriaQuery query) { + CriteriaBuilder builder = em.getCriteriaBuilder(); + Path stringValue = parent.get(TestEntity.ATTR_stringValue); + return builder.upper(stringValue); + } + }); + + return factory; + } +} diff --git a/katharsis-jpa/src/test/java/io/katharsis/jpa/query/querydsl/VirtualAttributeQuerydslTest.java b/katharsis-jpa/src/test/java/io/katharsis/jpa/query/querydsl/ComputedAttributeQuerydslTest.java similarity index 73% rename from katharsis-jpa/src/test/java/io/katharsis/jpa/query/querydsl/VirtualAttributeQuerydslTest.java rename to katharsis-jpa/src/test/java/io/katharsis/jpa/query/querydsl/ComputedAttributeQuerydslTest.java index 6fcaeab7..f9ea8067 100644 --- a/katharsis-jpa/src/test/java/io/katharsis/jpa/query/querydsl/VirtualAttributeQuerydslTest.java +++ b/katharsis-jpa/src/test/java/io/katharsis/jpa/query/querydsl/ComputedAttributeQuerydslTest.java @@ -1,30 +1,30 @@ -package io.katharsis.jpa.query.querydsl; - -import javax.persistence.EntityManager; - -import com.querydsl.core.types.Expression; -import com.querydsl.jpa.impl.JPAQuery; - -import io.katharsis.jpa.model.QTestEntity; -import io.katharsis.jpa.model.TestEntity; -import io.katharsis.jpa.query.JpaQueryFactory; -import io.katharsis.jpa.query.VirtualAttributeTestBase; - -public class VirtualAttributeQuerydslTest extends VirtualAttributeTestBase { - - @Override - protected JpaQueryFactory createQueryFactory(EntityManager em) { - QuerydslQueryFactory factory = QuerydslQueryFactory.newInstance(module.getMetaLookup(), em); - - factory.registerVirtualAttribute(TestEntity.class, ATTR_VIRTUAL_VALUE, String.class, - new QuerydslExpressionFactory() { - - @Override - public Expression getExpression(QTestEntity test, JPAQuery query) { - return test.stringValue.toUpperCase(); - } - }); - - return factory; - } -} +package io.katharsis.jpa.query.querydsl; + +import javax.persistence.EntityManager; + +import com.querydsl.core.types.Expression; +import com.querydsl.jpa.impl.JPAQuery; + +import io.katharsis.jpa.model.QTestEntity; +import io.katharsis.jpa.model.TestEntity; +import io.katharsis.jpa.query.JpaQueryFactory; +import io.katharsis.jpa.query.ComputedAttributeTestBase; + +public class ComputedAttributeQuerydslTest extends ComputedAttributeTestBase { + + @Override + protected JpaQueryFactory createQueryFactory(EntityManager em) { + QuerydslQueryFactory factory = QuerydslQueryFactory.newInstance(module.getMetaLookup(), em); + + factory.registerComputedAttribute(TestEntity.class, ATTR_VIRTUAL_VALUE, String.class, + new QuerydslExpressionFactory() { + + @Override + public Expression getExpression(QTestEntity test, JPAQuery query) { + return test.stringValue.toUpperCase(); + } + }); + + return factory; + } +} diff --git a/katharsis-jpa/src/test/java/io/katharsis/jpa/repository/JpaListenerTest.java b/katharsis-jpa/src/test/java/io/katharsis/jpa/repository/JpaListenerTest.java new file mode 100644 index 00000000..ba56a13c --- /dev/null +++ b/katharsis-jpa/src/test/java/io/katharsis/jpa/repository/JpaListenerTest.java @@ -0,0 +1,95 @@ +package io.katharsis.jpa.repository; + +import java.util.List; + +import javax.persistence.EntityManager; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; +import org.springframework.transaction.annotation.Transactional; + +import io.katharsis.jpa.JpaEntityRepository; +import io.katharsis.jpa.JpaRepositoryFilter; +import io.katharsis.jpa.internal.meta.MetaLookup; +import io.katharsis.jpa.model.TestEntity; +import io.katharsis.jpa.query.AbstractJpaTest; +import io.katharsis.jpa.query.JpaQuery; +import io.katharsis.jpa.query.JpaQueryExecutor; +import io.katharsis.jpa.query.JpaQueryFactory; +import io.katharsis.jpa.query.Tuple; +import io.katharsis.jpa.query.querydsl.QuerydslQueryFactory; +import io.katharsis.queryspec.QuerySpec; + +@Transactional +public class JpaListenerTest extends AbstractJpaTest { + + private JpaEntityRepository repo; + + @Override + @Before + public void setup() { + super.setup(); + repo = new JpaEntityRepository<>(module, TestEntity.class); + repo.setResourceRegistry(resourceRegistry); + } + + @SuppressWarnings("unchecked") + @Test + public void test() throws InstantiationException, IllegalAccessException { + + TestFilter filter = Mockito.spy(new TestFilter()); + module.addFilter(filter); + + QuerySpec querySpec = new QuerySpec(TestEntity.class); + List list = repo.findAll(querySpec); + Assert.assertEquals(5, list.size()); + + Mockito.verify(filter, Mockito.times(1)).filterQuerySpec(Mockito.eq(repo), Mockito.eq(querySpec)); + Mockito.verify(filter, Mockito.times(1)).filterResults(Mockito.eq(repo), Mockito.eq(querySpec), Mockito.eq(list)); + Mockito.verify(filter, Mockito.times(1)).filterExecutor(Mockito.eq(repo), Mockito.eq(querySpec), + Mockito.any(JpaQueryExecutor.class)); + Mockito.verify(filter, Mockito.times(1)).filterTuples(Mockito.eq(repo), Mockito.eq(querySpec), Mockito.anyList()); + Mockito.verify(filter, Mockito.times(1)).filterQuery(Mockito.eq(repo), Mockito.eq(querySpec), + Mockito.any(JpaQuery.class)); + } + + @Override + protected JpaQueryFactory createQueryFactory(EntityManager em) { + return QuerydslQueryFactory.newInstance(new MetaLookup(), em); + } + + class TestFilter implements JpaRepositoryFilter { + + @Override + public boolean accept(Class resourceType) { + return true; + } + + @Override + public QuerySpec filterQuerySpec(Object repository, QuerySpec querySpec) { + return querySpec; + } + + @Override + public JpaQuery filterQuery(Object repository, QuerySpec querySpec, JpaQuery query) { + return query; + } + + @Override + public JpaQueryExecutor filterExecutor(Object repository, QuerySpec querySpec, JpaQueryExecutor executor) { + return executor; + } + + @Override + public List filterTuples(Object repository, QuerySpec querySpec, List tuples) { + return tuples; + } + + @Override + public List filterResults(Object repository, QuerySpec querySpec, List resources) { + return resources; + } + } +}