/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.deltaspike.data.impl.handler; import java.io.Serializable; import java.lang.reflect.Method; import java.util.LinkedList; import java.util.List; import javax.persistence.EntityManager; import javax.persistence.LockModeType; import javax.persistence.Query; import javax.persistence.QueryHint; import org.apache.deltaspike.data.api.EntityGraph; import org.apache.deltaspike.data.api.SingleResultType; import org.apache.deltaspike.data.api.mapping.QueryInOutMapper; import org.apache.deltaspike.data.impl.graph.EntityGraphHelper; import org.apache.deltaspike.data.impl.meta.RepositoryMethod; import org.apache.deltaspike.data.impl.param.Parameters; import org.apache.deltaspike.data.impl.property.Property; import org.apache.deltaspike.data.impl.util.EntityUtils; import org.apache.deltaspike.data.impl.util.bean.Destroyable; import org.apache.deltaspike.data.spi.QueryInvocationContext; public class CdiQueryInvocationContext implements QueryInvocationContext { private final EntityManager entityManager; private final Parameters params; private final Class<?> entityClass; private final Object proxy; private final Method method; private final Object[] args; private final RepositoryMethod repoMethod; private final List<QueryStringPostProcessor> queryPostProcessors; private final List<JpaQueryPostProcessor> jpaPostProcessors; private final List<Destroyable> cleanup; private String queryString; public CdiQueryInvocationContext(Object proxy, Method method, Object[] args, RepositoryMethod repoMethod, EntityManager entityManager) { this.entityManager = entityManager; this.args = args == null ? new Object[]{} : args; this.params = Parameters.create(method, this.args, repoMethod); this.proxy = proxy; this.method = method; this.repoMethod = repoMethod; this.entityClass = repoMethod.getRepository().getEntityClass(); this.queryPostProcessors = new LinkedList<QueryStringPostProcessor>(); this.jpaPostProcessors = new LinkedList<JpaQueryPostProcessor>(); this.cleanup = new LinkedList<Destroyable>(); } public void initMapper() { if (hasQueryInOutMapper()) { QueryInOutMapper<?> mapper = getQueryInOutMapper(); params.applyMapper(mapper); for (int i = 0; i < args.length; i++) { if (mapper.mapsParameter(args[i])) { args[i] = mapper.mapParameter(args[i]); } } } } @Override public EntityManager getEntityManager() { return entityManager; } @Override public boolean isNew(Object entity) { try { Property<Serializable> versionProperty = repoMethod.getRepository().getRepositoryEntity().getVersionProperty(); if (versionProperty != null) { return versionProperty.getValue(entity) == null; } Property<Serializable> primaryKeyProperty = repoMethod.getRepository().getRepositoryEntity().getPrimaryKeyProperty(); if (EntityUtils.primaryKeyValue(entity, primaryKeyProperty) == null) { return true; } if (!entityManager.contains(entity) && countCheck(entity, primaryKeyProperty)) { return true; } return false; } catch (IllegalArgumentException e) { // Not an entity return false; } } @Override public Class<?> getEntityClass() { return entityClass; } @Override public Class<?> getRepositoryClass() { return repoMethod.getRepository().getRepositoryClass(); } public Object proceed() throws Exception { return method.invoke(proxy, args); } @Override public Method getMethod() { return method; } public Query applyRestrictions(Query query) { Parameters params = getParams(); Method method = getMethod(); if (params.hasSizeRestriction()) { query.setMaxResults(params.getSizeRestriciton()); } if (params.hasFirstResult()) { query.setFirstResult(params.getFirstResult()); } if (hasLockMode(method)) { query.setLockMode(extractLockMode(method)); } if (hasQueryHints(method)) { QueryHint[] hints = extractQueryHints(method); for (QueryHint hint : hints) { query.setHint(hint.name(), hint.value()); } } applyEntityGraph(query, method); query = applyJpaQueryPostProcessors(query); return query; } public Object[] getMethodParameters() { return args; } public void addQueryStringPostProcessor(QueryStringPostProcessor postProcessor) { queryPostProcessors.add(postProcessor); } public void addJpaQueryPostProcessor(JpaQueryPostProcessor postProcessor) { jpaPostProcessors.add(postProcessor); } public void removeJpaQueryPostProcessor(JpaQueryPostProcessor postProcessor) { jpaPostProcessors.remove(postProcessor); } public boolean hasQueryStringPostProcessors() { return !queryPostProcessors.isEmpty(); } public String applyQueryStringPostProcessors(String queryString) { String result = queryString; for (QueryStringPostProcessor processor : queryPostProcessors) { result = processor.postProcess(result); } return result; } public Query applyJpaQueryPostProcessors(Query query) { Query result = query; for (JpaQueryPostProcessor processor : jpaPostProcessors) { result = processor.postProcess(this, result); } return result; } public void addDestroyable(Destroyable destroyable) { cleanup.add(destroyable); } public void cleanup() { for (Destroyable destroy : cleanup) { destroy.destroy(); } cleanup.clear(); } public Object executeQuery(Query jpaQuery) { return repoMethod.getQueryProcessor().executeQuery(jpaQuery, this); } public Parameters getParams() { return params; } public RepositoryMethod getRepositoryMethod() { return repoMethod; } public String getQueryString() { return queryString; } public void setQueryString(String queryString) { this.queryString = queryString; } public List<QueryStringPostProcessor> getQueryStringPostProcessors() { return queryPostProcessors; } public boolean hasQueryInOutMapper() { return repoMethod.hasQueryInOutMapper(); } public QueryInOutMapper<?> getQueryInOutMapper() { return repoMethod.getQueryInOutMapperInstance(this); } public SingleResultType getSingleResultStyle() { SingleResultType baseSingleResultType = repoMethod.getSingleResultStyle(); if (repoMethod.isOptional() && baseSingleResultType == SingleResultType.JPA) { return SingleResultType.OPTIONAL; } else { return baseSingleResultType; } } public Object getProxy() { return proxy; } private boolean hasLockMode(Method method) { return extractLockMode(method) != null; } private LockModeType extractLockMode(Method method) { Class<org.apache.deltaspike.data.api.Query> query = org.apache.deltaspike.data.api.Query.class; if (method.isAnnotationPresent(query) && method.getAnnotation(query).lock() != LockModeType.NONE) { return method.getAnnotation(query).lock(); } return null; } private QueryHint[] extractQueryHints(Method method) { Class<org.apache.deltaspike.data.api.Query> query = org.apache.deltaspike.data.api.Query.class; if (method.isAnnotationPresent(query) && method.getAnnotation(query).hints().length > 0) { return method.getAnnotation(query).hints(); } return null; } private boolean hasQueryHints(Method method) { return extractQueryHints(method) != null; } private void applyEntityGraph(Query query, Method method) { EntityGraph entityGraphAnn = method.getAnnotation(EntityGraph.class); if (entityGraphAnn == null) { return; } Object graph = EntityGraphHelper.getEntityGraph(getEntityManager(), entityClass, entityGraphAnn); query.setHint(entityGraphAnn.type().getHintName(), graph); } private boolean countCheck(Object entity, Property<Serializable> primaryKeyProperty) { StringBuilder jpql = new StringBuilder("SELECT COUNT(e) FROM " + getEntityClass() .getSimpleName() + " e "); jpql.append("WHERE e."); jpql.append(primaryKeyProperty.getName()); jpql.append(" = :id"); final Query query = entityManager.createQuery(jpql.toString()); query.setParameter("id", EntityUtils.primaryKeyValue(entity, primaryKeyProperty)); final Long result = (Long) query.getSingleResult(); if (Long.valueOf(0).equals(result)) { return true; } return false; } public boolean isOptional() { return this.repoMethod.isOptional(); } }