| /** |
| * Copyright 2009-2013 Oy Vaadin Ltd |
| * |
| * Licensed 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.eclipse.osbp.jpa.services.metadata; |
| |
| import java.io.ObjectStreamException; |
| import java.io.Serializable; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| import javax.persistence.EntityManager; |
| import javax.persistence.EntityNotFoundException; |
| import javax.persistence.LockModeType; |
| import javax.persistence.Query; |
| import javax.persistence.TransactionRequiredException; |
| import javax.persistence.TypedQuery; |
| import javax.persistence.criteria.CriteriaBuilder; |
| import javax.persistence.criteria.CriteriaQuery; |
| import javax.persistence.criteria.Join; |
| import javax.persistence.criteria.JoinType; |
| import javax.persistence.criteria.Order; |
| import javax.persistence.criteria.Path; |
| import javax.persistence.criteria.Predicate; |
| import javax.persistence.criteria.Root; |
| |
| import org.eclipse.osbp.jpa.services.filters.LAdvancedFilterableSupport; |
| import org.eclipse.osbp.jpa.services.filters.LAnd; |
| import org.eclipse.osbp.jpa.services.filters.LCompare.Equal; |
| import org.eclipse.osbp.jpa.services.filters.LCompare.Greater; |
| import org.eclipse.osbp.jpa.services.filters.LCompare.Less; |
| import org.eclipse.osbp.jpa.services.filters.LOr; |
| import org.eclipse.osbp.runtime.common.filter.IJPQL; |
| import org.eclipse.osbp.runtime.common.filter.ILFilter; |
| import org.eclipse.osbp.runtime.common.filter.IQuery; |
| import org.eclipse.osbp.runtime.common.filter.SortBy; |
| |
| /** |
| * A read-only entity provider that works with a local {@link EntityManager}. |
| * Most important features and limitations: |
| * <ul> |
| * <li>Does not do any internal caching, all information is always accessed |
| * directly from the EntityManager</li> |
| * <li>Explicitly detaches entities by default (see |
| * {@link #isEntitiesDetached() }) |
| * <ul> |
| * <li>Performs a serialize-deserialize cycle to clone entities in order to |
| * explicitly detach them from the persistence context (<b>This is |
| * ugly!</b>).</li> |
| * </ul> |
| * </li> |
| * <li>Uses lazy-loading of entities (when using detached entities, references |
| * and collections within the entities should be configured to be fetched |
| * eagerly, though)</li> |
| * </ul> |
| * |
| * This entity provider does not perform very well, as every method call results |
| * in at least one query being sent to the entity manager. If speed is desired, |
| * <code>CachingLocalEntityProvider</code> should be used instead. However, this |
| * entity provider consumes less memory than the caching provider. |
| * |
| * @author Petter Holmstr��m (Vaadin Ltd) |
| * @since 1.0 |
| */ |
| public class EntityDelegate<T> implements Serializable { |
| |
| private static final long serialVersionUID = 1601796410565144708L; |
| private transient EntityManager entityManager; |
| private EntityClassMetadata<T> entityClassMetadata; |
| private boolean entitiesDetached = true; |
| private Serializable serializableEntityManager; |
| private QueryModifierDelegate queryModifierDelegate; |
| private int maxResults; |
| |
| /** |
| * Creates a new <code>LocalEntityProvider</code>. |
| * |
| * @param entityClass |
| * the entity class (must not be null). |
| * @param entityManager |
| * the entity manager to use (must not be null). |
| */ |
| public EntityDelegate(Class<T> entityClass, EntityManager entityManager, int maxResults) { |
| this(entityClass); |
| this.maxResults = maxResults; |
| assert entityManager != null : "entityManager must not be null"; |
| setEntityManager(entityManager); |
| } |
| |
| /** |
| * Creates a new <code>LocalEntityProvider</code>. The entity manager or an |
| * entity manager provider must be set using |
| * {@link #setEntityManager(javax.persistence.EntityManager)} or |
| * <code>setEntityManagerProvider(com.vaadin.addon.jpacontainer.EntityManagerProvider)</Code> |
| * respectively. |
| * |
| * @param entityClass |
| * the entity class (must not be null). |
| */ |
| public EntityDelegate(Class<T> entityClass) { |
| assert entityClass != null : "entityClass must not be null"; |
| this.entityClassMetadata = MetadataFactory.getInstance().getEntityClassMetadata(entityClass); |
| } |
| |
| // TODO Test serialization of entity manager |
| protected Object writeReplace() throws ObjectStreamException { |
| if (entityManager != null && entityManager instanceof Serializable) { |
| serializableEntityManager = (Serializable) entityManager; |
| } |
| return this; |
| } |
| |
| protected Object readResolve() throws ObjectStreamException { |
| if (serializableEntityManager != null) { |
| this.entityManager = (EntityManager) serializableEntityManager; |
| } |
| return this; |
| } |
| |
| /** |
| * Sets the entity manager. |
| * |
| * @param entityManager |
| * the entity manager to set. |
| */ |
| public void setEntityManager(EntityManager entityManager) { |
| this.entityManager = entityManager; |
| } |
| |
| /** |
| * Gets the metadata for the entity class. |
| * |
| * @return the metadata (never null). |
| */ |
| public EntityClassMetadata<T> getEntityClassMetadata() { |
| return this.entityClassMetadata; |
| } |
| |
| /** |
| * Gets the entity manager. If no entity manager has been set, the one |
| * returned by the registered entity manager provider is returned. |
| * |
| * @return the entity manager. |
| */ |
| public EntityManager getEntityManager() { |
| return entityManager; |
| } |
| |
| /** |
| * Gets the entity manager. |
| * |
| * @return the entity manager (never null). |
| * @throws IllegalStateException |
| * if no entity manager is set. |
| */ |
| protected EntityManager doGetEntityManager() throws IllegalStateException { |
| if (getEntityManager() == null) { |
| throw new IllegalStateException("No entity manager specified"); |
| } |
| return getEntityManager(); |
| } |
| |
| /** |
| * Creates a copy of <code>original</code> and adds an entry for the primary |
| * key to the end of the list. |
| * |
| * @param original |
| * the original list of sorting instructions (must not be null, |
| * but may be empty). |
| * @return a new list with the added entry for the primary key. |
| */ |
| protected List<SortBy> addPrimaryKeyToSortList(List<SortBy> original) { |
| if (sortByListContainsPrimaryKey(original)) { |
| return original; |
| } |
| ArrayList<SortBy> newList = new ArrayList<SortBy>(); |
| newList.addAll(original); |
| if (getEntityClassMetadata().hasEmbeddedIdentifier()) { |
| for (String p : getEntityClassMetadata().getIdentifierProperty().getTypeMetadata() |
| .getPersistentPropertyNames()) { |
| newList.add(new SortBy(getEntityClassMetadata().getIdentifierProperty().getName() + "." + p, true)); |
| } |
| } else { |
| newList.add(new SortBy(getEntityClassMetadata().getIdentifierProperty().getName(), true)); |
| } |
| return Collections.unmodifiableList(newList); |
| } |
| |
| /** |
| * @param original |
| * @return |
| */ |
| private boolean sortByListContainsPrimaryKey(List<SortBy> original) { |
| for (SortBy sb : original) { |
| EntityClassMetadata<T> metadata = getEntityClassMetadata(); |
| if (metadata.hasEmbeddedIdentifier()) { |
| if (sb.getPropertyId().equals(metadata.getIdentifierProperty().getTypeMetadata() |
| .getPersistentPropertyNames().iterator().next())) { |
| return true; |
| } |
| } else { |
| if (sb.getPropertyId().equals(metadata.getIdentifierProperty().getName())) { |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Translates SortBy instances, which possibly contain nested properties |
| * (e.g. name.firstName, name.lastName) into Order instances which can be |
| * used in a CriteriaQuery. |
| * |
| * @param sortBy |
| * the SortBy instance to translate |
| * @param swapSortOrder |
| * swaps the specified sort order if true. |
| * @param cb |
| * the {@link CriteriaBuilder} to use |
| * @param root |
| * the {@link CriteriaQuery} {@link Root} to be used. |
| * @return |
| */ |
| protected Order translateSortBy(SortBy sortBy, boolean swapSortOrder, CriteriaBuilder cb, Root<T> root) { |
| String sortedPropId = sortBy.getPropertyId().toString(); |
| // First split the id and build a Path. |
| String[] idStrings = sortedPropId.split("\\."); |
| Path<T> path = null; |
| if (idStrings.length > 1 && !isEmbedded(idStrings[0])) { |
| // This is a nested property, we need to LEFT JOIN |
| path = root.join(idStrings[0], JoinType.LEFT); |
| for (int i = 1; i < idStrings.length; i++) { |
| if (i < idStrings.length - 1) { |
| path = ((Join<?, ?>) path).join(idStrings[i], JoinType.LEFT); |
| } else { |
| path = path.get(idStrings[i]); |
| } |
| } |
| } else { |
| // non-nested or embedded, we can select as usual |
| path = LAdvancedFilterableSupport.getPropertyPathTyped(root, sortedPropId); |
| } |
| |
| // Make and return the Order instances. |
| if (sortBy.isAscending() != swapSortOrder) { |
| return cb.asc(path); |
| } else { |
| return cb.desc(path); |
| } |
| } |
| |
| /** |
| * @param propertyId |
| * @return |
| */ |
| private boolean isEmbedded(String propertyId) { |
| return entityClassMetadata.getProperty(propertyId).getPropertyKind() == PropertyKind.EMBEDDED; |
| } |
| |
| /** |
| * Creates a filtered query that does not do any sorting. |
| * |
| * see createFilteredQuery(com.vaadin.addon.jpacontainer.EntityContainer, |
| * java.util.List, |
| * org.eclipse.osbp.dsl.dto.lib.services.filters.ILFilter.data.Container.IFilter, |
| * java.util.List, boolean) |
| * |
| * @param fieldsToSelect |
| * the fields to select (must not be null). |
| * @param filter |
| * the filter to apply, or null if no filters should be applied. |
| * @return the query (never null). |
| */ |
| protected TypedQuery<Object> createUnsortedFilteredQuery(List<String> fieldsToSelect, ILFilter filter, |
| LockModeType lockModeType) { |
| return createFilteredPropertyQuery(fieldsToSelect, filter, null, false, lockModeType); |
| } |
| |
| /** |
| * Creates a filtered, optionally sorted, query. |
| * |
| * @param fieldsToSelect |
| * the fields to select (must not be null). |
| * @param filter |
| * the filter to apply, or null if no filters should be applied. |
| * @param sortBy |
| * the fields to sort by (must include at least one field), or |
| * null if the result should not be sorted at all. |
| * @param swapSortOrder |
| * true to swap the sort order, false to use the sort order |
| * specified in <code>sortBy</code>. Only applies if |
| * <code>sortBy</code> is not null. |
| * @return the query (never null). |
| */ |
| protected TypedQuery<Object> createFilteredPropertyQuery(List<String> fieldsToSelect, ILFilter filter, |
| List<SortBy> sortBy, boolean swapSortOrder, LockModeType lockModeType) { |
| assert fieldsToSelect != null : "fieldsToSelect must not be null"; |
| assert sortBy == null || !sortBy.isEmpty() : "sortBy must be either null or non-empty"; |
| |
| CriteriaBuilder cb = doGetEntityManager().getCriteriaBuilder(); |
| CriteriaQuery<Object> query = cb.createQuery(); |
| Root<T> root = query.from(entityClassMetadata.getMappedClass()); |
| |
| tellDelegateQueryWillBeBuilt(cb, query); |
| |
| List<Predicate> predicates = new ArrayList<Predicate>(); |
| if (filter != null) { |
| predicates.add(FilterConverter.convertFilter(filter, cb, root)); |
| } |
| tellDelegateFiltersWillBeAdded(cb, query, predicates); |
| if (!predicates.isEmpty()) { |
| query.where(CollectionUtil.toArray(Predicate.class, predicates)); |
| } |
| tellDelegateFiltersWereAdded(cb, query); |
| |
| List<Order> orderBy = new ArrayList<Order>(); |
| if (sortBy != null && sortBy.size() > 0) { |
| for (SortBy sortedProperty : sortBy) { |
| orderBy.add(translateSortBy(sortedProperty, swapSortOrder, cb, root)); |
| } |
| } |
| tellDelegateOrderByWillBeAdded(cb, query, orderBy); |
| query.orderBy(orderBy); |
| tellDelegateOrderByWereAdded(cb, query); |
| |
| if (fieldsToSelect.size() > 1 || getEntityClassMetadata().hasEmbeddedIdentifier()) { |
| List<Path<?>> paths = new ArrayList<Path<?>>(); |
| for (String fieldPath : fieldsToSelect) { |
| paths.add(LAdvancedFilterableSupport.getPropertyPathTyped(root, fieldPath)); |
| } |
| query.multiselect(paths.toArray(new Path<?>[paths.size()])); |
| } else { |
| query.select(LAdvancedFilterableSupport.getPropertyPathTyped(root, fieldsToSelect.get(0))); |
| } |
| tellDelegateQueryHasBeenBuilt(cb, query); |
| TypedQuery<Object> tq = doGetEntityManager().createQuery(query); |
| tq.setLockMode(lockModeType); |
| return tq; |
| } |
| |
| // /** |
| // * Creates a filtered, optionally sorted, query. |
| // * |
| // * @param filter |
| // * the filter to apply, or null if no filters should be applied. |
| // * @param sortBy |
| // * the fields to sort by (must include at least one field), or |
| // * null if the result should not be sorted at all. |
| // * @param swapSortOrder |
| // * true to swap the sort order, false to use the sort order |
| // * specified in <code>sortBy</code>. Only applies if |
| // * <code>sortBy</code> is not null. |
| // * @return the query (never null). |
| // */ |
| // protected TypedQuery<Long> createFilteredCountQuery(ILFilter filter, |
| // List<SortBy> sortBy, boolean swapSortOrder) { |
| // assert sortBy == null || !sortBy.isEmpty() : |
| // "sortBy must be either null or non-empty"; |
| // |
| // CriteriaBuilder cb = doGetEntityManager().getCriteriaBuilder(); |
| // CriteriaQuery<Long> query = cb.createQuery(Long.class); |
| // Root<T> root = query.from(entityClassMetadata.getMappedClass()); |
| // |
| // tellDelegateQueryWillBeBuilt(cb, query); |
| // |
| // List<Predicate> predicates = new ArrayList<Predicate>(); |
| // if (filter != null) { |
| // predicates.add(FilterConverter.convertFilter(filter, cb, root)); |
| // } |
| // tellDelegateFiltersWillBeAdded(cb, query, predicates); |
| // if (!predicates.isEmpty()) { |
| // query.where(CollectionUtil.toArray(Predicate.class, predicates)); |
| // } |
| // tellDelegateFiltersWereAdded(cb, query); |
| // |
| // // List<Order> orderBy = new ArrayList<Order>(); |
| // // if (sortBy != null && sortBy.size() > 0) { |
| // // for (SortBy sortedProperty : sortBy) { |
| // // orderBy.add(translateSortBy(sortedProperty, swapSortOrder, cb, |
| // // root)); |
| // // } |
| // // } |
| // // tellDelegateOrderByWillBeAdded(cb, query, orderBy); |
| // // query.orderBy(orderBy); |
| // // tellDelegateOrderByWereAdded(cb, query); |
| // |
| // String entityIdPropertyName = getEntityClassMetadata() |
| // .getIdentifierProperty().getName(); |
| // query.select(cb.count(root.get(entityIdPropertyName))); |
| // // query.groupBy(root.get(entityIdPropertyName)); |
| // |
| // tellDelegateQueryHasBeenBuilt(cb, query); |
| // return doGetEntityManager().createQuery(query); |
| // } |
| |
| /** |
| * Creates a filtered, optionally sorted, query. |
| * |
| * @param filter |
| * the filter to apply, or null if no filters should be applied. |
| * @param sortBy |
| * the fields to sort by (must include at least one field), or |
| * null if the result should not be sorted at all. |
| * @param swapSortOrder |
| * true to swap the sort order, false to use the sort order |
| * specified in <code>sortBy</code>. Only applies if |
| * <code>sortBy</code> is not null. |
| * @return the query (never null). |
| */ |
| @SuppressWarnings("unchecked") |
| protected TypedQuery<T> createFilteredEntityQuery(ILFilter filter, List<SortBy> sortBy, boolean swapSortOrder, |
| LockModeType lockModeType) { |
| assert sortBy == null || !sortBy.isEmpty() : "sortBy must be either null or non-empty"; |
| |
| CriteriaBuilder cb = doGetEntityManager().getCriteriaBuilder(); |
| CriteriaQuery<Object> query = cb.createQuery(); |
| Root<T> root = query.from(entityClassMetadata.getMappedClass()); |
| |
| tellDelegateQueryWillBeBuilt(cb, query); |
| |
| List<Predicate> predicates = new ArrayList<Predicate>(); |
| if (filter != null) { |
| predicates.add(FilterConverter.convertFilter(filter, cb, root)); |
| } |
| tellDelegateFiltersWillBeAdded(cb, query, predicates); |
| if (!predicates.isEmpty()) { |
| query.where(CollectionUtil.toArray(Predicate.class, predicates)); |
| } |
| tellDelegateFiltersWereAdded(cb, query); |
| |
| List<Order> orderBy = new ArrayList<Order>(); |
| if (sortBy != null && sortBy.size() > 0) { |
| for (SortBy sortedProperty : sortBy) { |
| orderBy.add(translateSortBy(sortedProperty, swapSortOrder, cb, root)); |
| } |
| } |
| tellDelegateOrderByWillBeAdded(cb, query, orderBy); |
| query.orderBy(orderBy); |
| tellDelegateOrderByWereAdded(cb, query); |
| |
| query.select(root); |
| |
| tellDelegateQueryHasBeenBuilt(cb, query); |
| TypedQuery<T> tq = (TypedQuery<T>) doGetEntityManager().createQuery(query); |
| tq.setLockMode(lockModeType); |
| return tq; |
| } |
| |
| protected boolean doContainsEntityIdentifier(Object entityId, ILFilter filter, LockModeType lockModeType) { |
| assert entityId != null : "entityId must not be null"; |
| String entityIdPropertyName = getEntityClassMetadata().getIdentifierProperty().getName(); |
| |
| CriteriaBuilder cb = doGetEntityManager().getCriteriaBuilder(); |
| CriteriaQuery<Long> query = cb.createQuery(Long.class); |
| Root<T> root = query.from(getEntityClassMetadata().getMappedClass()); |
| |
| tellDelegateQueryWillBeBuilt(cb, query); |
| |
| List<Predicate> predicates = new ArrayList<Predicate>(); |
| predicates.add(cb.equal(root.get(entityIdPropertyName), cb.literal(entityId))); |
| if (filter != null) { |
| predicates.add(FilterConverter.convertFilter(filter, cb, root)); |
| } |
| tellDelegateFiltersWillBeAdded(cb, query, predicates); |
| if (!predicates.isEmpty()) { |
| query.where(CollectionUtil.toArray(Predicate.class, predicates)); |
| } |
| tellDelegateFiltersWereAdded(cb, query); |
| |
| if (getEntityClassMetadata().hasEmbeddedIdentifier()) { |
| /* |
| * Hibernate will generate SQL for "count(obj)" that does not run on |
| * HSQLDB. "count(*)" works fine, but then EclipseLink won't work. |
| * With this hack, this method should work with both Hibernate and |
| * EclipseLink. |
| */ |
| query.select(cb.count(root.get(entityIdPropertyName).get(getEntityClassMetadata().getIdentifierProperty() |
| .getTypeMetadata().getPersistentPropertyNames().iterator().next()))); |
| } else { |
| |
| query.select(cb.count(root.get(entityIdPropertyName))); |
| } |
| tellDelegateQueryHasBeenBuilt(cb, query); |
| TypedQuery<Long> tq = doGetEntityManager().createQuery(query); |
| tq.setLockMode(lockModeType); |
| return tq.getSingleResult() == 1; |
| } |
| |
| public boolean containsEntityIdentifier(Object entityId, IQuery query, LockModeType lockModeType) { |
| return doContainsEntityIdentifier(entityId, query.getFilter(), lockModeType); |
| } |
| |
| protected T doGetEntity(Object entityId, LockModeType lockModeType) { |
| assert entityId != null : "entityId must not be null"; |
| T entity = doGetEntityManager().find(getEntityClassMetadata().getMappedClass(), entityId, lockModeType); |
| return detachEntity(entity); |
| } |
| |
| public T getEntity(Object entityId, LockModeType lockModeType) { |
| return doGetEntity(entityId, lockModeType); |
| } |
| |
| protected Object doGetEntityIdentifierAt(ILFilter filter, List<SortBy> sortBy, int index, |
| LockModeType lockModeType) { |
| if (sortBy == null) { |
| sortBy = Collections.emptyList(); |
| } |
| TypedQuery<Object> query = createFilteredPropertyQuery( |
| Arrays.asList(getEntityClassMetadata().getIdentifierProperty().getName()), filter, |
| addPrimaryKeyToSortList(sortBy), false, lockModeType); |
| if (lockModeType == LockModeType.NONE || lockModeType == LockModeType.OPTIMISTIC) { |
| query.setMaxResults(1); |
| } |
| query.setFirstResult(index); |
| List<?> result = query.getResultList(); |
| if (result.isEmpty()) { |
| return null; |
| } else { |
| return result.get(0); |
| } |
| } |
| |
| public Object getEntityIdentifierAt(IQuery query, int index, LockModeType lockModeType) { |
| return doGetEntityIdentifierAt(query.getFilter(), query.getSortOrder().getSortBy(), index, lockModeType); |
| } |
| |
| protected int doGetEntityCount(ILFilter filter, LockModeType lockModeType) { |
| String entityIdPropertyName = getEntityClassMetadata().getIdentifierProperty().getName(); |
| |
| CriteriaBuilder cb = doGetEntityManager().getCriteriaBuilder(); |
| CriteriaQuery<Long> query = cb.createQuery(Long.class); |
| Root<T> root = query.from(getEntityClassMetadata().getMappedClass()); |
| |
| tellDelegateQueryWillBeBuilt(cb, query); |
| |
| List<Predicate> predicates = new ArrayList<Predicate>(); |
| if (filter != null) { |
| predicates.add(FilterConverter.convertFilter(filter, cb, root)); |
| } |
| tellDelegateFiltersWillBeAdded(cb, query, predicates); |
| if (!predicates.isEmpty()) { |
| query.where(CollectionUtil.toArray(Predicate.class, predicates)); |
| } |
| tellDelegateFiltersWereAdded(cb, query); |
| |
| if (getEntityClassMetadata().hasEmbeddedIdentifier()) { |
| /* |
| * Hibernate will generate SQL for "count(obj)" that does not run on |
| * HSQLDB. "count(*)" works fine, but then EclipseLink won't work. |
| * With this hack, this method should work with both Hibernate and |
| * EclipseLink. |
| */ |
| |
| query.select(cb.count(root.get(entityIdPropertyName).get(getEntityClassMetadata().getIdentifierProperty() |
| .getTypeMetadata().getPersistentPropertyNames().iterator().next()))); |
| } else { |
| query.select(cb.count(root.get(entityIdPropertyName))); |
| } |
| tellDelegateQueryHasBeenBuilt(cb, query); |
| TypedQuery<Long> tq = doGetEntityManager().createQuery(query); |
| tq.setLockMode(lockModeType); |
| return tq.getSingleResult().intValue(); |
| } |
| |
| public int getEntityCount(IQuery query, LockModeType lockModeType) { |
| return doGetEntityCount(query.getFilter(), lockModeType); |
| } |
| |
| // |
| // public int getEntityIndex(Object entityId, IQuery query) { |
| // TypedQuery<Long> tQuery = createSiblingIndexQuery(entityId, |
| // query.getFilter(), query.getSortOrder().getSortBy(), true); |
| // return tQuery.getSingleResult().intValue(); |
| // } |
| |
| protected Object doGetFirstEntityIdentifier(ILFilter filter, List<SortBy> sortBy, LockModeType lockModeType) { |
| if (sortBy == null) { |
| sortBy = Collections.emptyList(); |
| } |
| |
| List<String> keyFields = Arrays.asList(getEntityClassMetadata().getIdentifierProperty().getName()); |
| // if (getEntityClassMetadata().hasEmbeddedIdentifier()) { |
| // keyFields = new ArrayList<String>(); |
| // for (String p : getEntityClassMetadata().getIdentifierProperty() |
| // .getTypeMetadata().getPersistentPropertyNames()) { |
| // keyFields.add(getEntityClassMetadata().getIdentifierProperty() |
| // .getName() + "." + p); |
| // } |
| // } |
| TypedQuery<Object> query = createFilteredPropertyQuery(keyFields, filter, addPrimaryKeyToSortList(sortBy), |
| false, lockModeType); |
| if (lockModeType == LockModeType.NONE || lockModeType == LockModeType.OPTIMISTIC) { |
| query.setMaxResults(1); |
| } |
| List<?> result = query.getResultList(); |
| if (result.isEmpty()) { |
| return null; |
| } else { |
| return result.get(0); |
| } |
| } |
| |
| public Object getFirstEntityIdentifier(IQuery query, LockModeType lockModeType) { |
| return doGetFirstEntityIdentifier(query.getFilter(), query.getSortOrder().getSortBy(), lockModeType); |
| } |
| |
| protected T doGetFirstEntity(ILFilter filter, List<SortBy> sortBy, LockModeType lockModeType) { |
| if (sortBy == null) { |
| sortBy = Collections.emptyList(); |
| } |
| |
| TypedQuery<T> query = createFilteredEntityQuery(filter, addPrimaryKeyToSortList(sortBy), false, lockModeType); |
| if (lockModeType == LockModeType.NONE || lockModeType == LockModeType.OPTIMISTIC) { |
| query.setMaxResults(1); |
| } |
| List<T> result = query.getResultList(); |
| if (result.isEmpty()) { |
| return null; |
| } else { |
| return result.get(0); |
| } |
| } |
| |
| public T getFirstEntity(IQuery query, LockModeType lockModeType) { |
| return doGetFirstEntity(query.getFilter(), query.getSortOrder().getSortBy(), lockModeType); |
| } |
| |
| protected Object doGetLastEntityIdentifier(ILFilter filter, List<SortBy> sortBy, LockModeType lockModeType) { |
| if (sortBy == null) { |
| sortBy = Collections.emptyList(); |
| } |
| // The last 'true' parameter switches the sort order -> the last row is |
| // the first result. |
| TypedQuery<Object> query = createFilteredPropertyQuery( |
| Arrays.asList(getEntityClassMetadata().getIdentifierProperty().getName()), filter, |
| addPrimaryKeyToSortList(sortBy), true, lockModeType); |
| if (lockModeType == LockModeType.NONE || lockModeType == LockModeType.OPTIMISTIC) { |
| query.setMaxResults(1); |
| } |
| List<?> result = query.getResultList(); |
| if (result.isEmpty()) { |
| return null; |
| } else { |
| return result.get(0); |
| } |
| } |
| |
| public Object getLastEntityIdentifier(IQuery query, LockModeType lockModeType) { |
| return doGetLastEntityIdentifier(query.getFilter(), query.getSortOrder().getSortBy(), lockModeType); |
| } |
| |
| protected T doGetLastEntity(ILFilter filter, List<SortBy> sortBy, LockModeType lockModeType) { |
| if (sortBy == null) { |
| sortBy = Collections.emptyList(); |
| } |
| // The last 'true' parameter switches the sort order -> the last row is |
| // the first result. |
| TypedQuery<T> query = createFilteredEntityQuery(filter, addPrimaryKeyToSortList(sortBy), true, lockModeType); |
| if (lockModeType == LockModeType.NONE || lockModeType == LockModeType.OPTIMISTIC) { |
| query.setMaxResults(1); |
| } |
| List<T> result = query.getResultList(); |
| if (result.isEmpty()) { |
| return null; |
| } else { |
| return result.get(0); |
| } |
| } |
| |
| public T getLastEntity(IQuery query, LockModeType lockModeType) { |
| return doGetLastEntity(query.getFilter(), query.getSortOrder().getSortBy(), lockModeType); |
| } |
| |
| /** |
| * If <code>backwards</code> is false, this method will return the |
| * identifier of the entity next to the entity identified by |
| * <code>entityId</code>. If true, this method will return the identifier of |
| * the entity previous to the entity identified by <code>entityId</code>. |
| * <code>filter</code> and <code>sortBy</code> is used to define and limit |
| * the list of entities to be used for determining the sibling. |
| * |
| * @param entityId |
| * the identifier of the entity whose sibling to retrieve (must |
| * not be null). |
| * @param filter |
| * an optional filter to limit the entities (may be null). |
| * @param sortBy |
| * the order in which the list should be sorted (must not be |
| * null). |
| * @param backwards |
| * true to fetch the previous sibling, false to fetch the next |
| * sibling. |
| * @return the identifier of the "sibling". |
| */ |
| protected Object getSibling(Object entityId, ILFilter filter, List<SortBy> sortBy, boolean backwards, |
| LockModeType lockModeType) { |
| TypedQuery<Object> query = createSiblingQuery(entityId, filter, sortBy, backwards, lockModeType); |
| if (lockModeType == LockModeType.NONE || lockModeType == LockModeType.OPTIMISTIC) { |
| query.setMaxResults(1); |
| } |
| List<?> result = query.getResultList(); |
| return result.get(0); |
| } |
| |
| /** |
| * This method creates a query that can be used to fetch the siblings of a |
| * specific entity. If <code>backwards</code> is false, the query will begin |
| * with the entity next to the entity identified by <code>entityId</code>. |
| * If <code>backwards</code> is false, the query will begin with the entity |
| * prior to the entity identified by <code>entityId</code>. |
| * |
| * @param entityId |
| * the identifier of the entity whose sibling to retrieve (must |
| * not be null). |
| * @param filter |
| * an optional filter to limit the entities (may be null). |
| * @param sortBy |
| * the order in which the list should be sorted (must not be |
| * null). |
| * @param backwards |
| * true to fetch the previous sibling, false to fetch the next |
| * sibling. |
| * @return the query that will return the sibling and all the subsequent |
| * entities unless limited. |
| */ |
| protected TypedQuery<Object> createSiblingQuery(Object entityId, ILFilter filter, List<SortBy> sortBy, |
| boolean backwards, LockModeType lockModeType) { |
| assert entityId != null : "entityId must not be null"; |
| assert sortBy != null : "sortBy must not be null"; |
| ILFilter limitingFilter; |
| sortBy = addPrimaryKeyToSortList(sortBy); |
| if (sortBy.size() == 1) { |
| // The list is sorted by primary key |
| if (backwards) { |
| limitingFilter = new Less(getEntityClassMetadata().getIdentifierProperty().getName(), entityId); |
| } else { |
| limitingFilter = new Greater(getEntityClassMetadata().getIdentifierProperty().getName(), entityId); |
| } |
| } else { |
| // We have to fetch the values of the sorted fields |
| T currentEntity = getEntity(entityId, lockModeType); |
| if (currentEntity == null) { |
| throw new EntityNotFoundException("No entity found with the ID " + entityId); |
| } |
| // Collect the values into a map for easy access |
| Map<Object, Object> filterValues = new HashMap<Object, Object>(); |
| for (SortBy sb : sortBy) { |
| filterValues.put(sb.getPropertyId(), |
| getEntityClassMetadata().getPropertyValue(currentEntity, sb.getPropertyId().toString())); |
| } |
| // Now we can build a filter that limits the query to the entities |
| // below entityId |
| List<ILFilter> orFilters = new ArrayList<ILFilter>(); |
| for (int i = sortBy.size() - 1; i >= 0; i--) { |
| // TODO Document this code snippet once it works |
| // TODO What happens with null values? |
| List<ILFilter> caseFilters = new ArrayList<ILFilter>(); |
| SortBy sb; |
| for (int j = 0; j < i; j++) { |
| sb = sortBy.get(j); |
| caseFilters.add(new Equal(sb.getPropertyId(), filterValues.get(sb.getPropertyId()))); |
| } |
| sb = sortBy.get(i); |
| if (sb.isAscending() ^ backwards) { |
| caseFilters.add(new Greater(sb.getPropertyId(), filterValues.get(sb.getPropertyId()))); |
| } else { |
| caseFilters.add(new Less(sb.getPropertyId(), filterValues.get(sb.getPropertyId()))); |
| } |
| orFilters.add(new LAnd(CollectionUtil.toArray(ILFilter.class, caseFilters))); |
| } |
| limitingFilter = new LOr(CollectionUtil.toArray(ILFilter.class, orFilters)); |
| } |
| // Now, we can create the query |
| ILFilter queryFilter; |
| if (filter == null) { |
| queryFilter = limitingFilter; |
| } else { |
| queryFilter = new LAnd(filter, limitingFilter); |
| } |
| TypedQuery<Object> query = createFilteredPropertyQuery( |
| Arrays.asList(getEntityClassMetadata().getIdentifierProperty().getName()), queryFilter, sortBy, |
| backwards, lockModeType); |
| return query; |
| } |
| |
| // /** |
| // * This method creates a query that can be used to fetch the siblings of a |
| // * specific entity. If <code>backwards</code> is false, the query will |
| // begin |
| // * with the entity next to the entity identified by <code>entityId</code>. |
| // * If <code>backwards</code> is false, the query will begin with the |
| // entity |
| // * prior to the entity identified by <code>entityId</code>. |
| // * |
| // * @param entityId |
| // * the identifier of the entity whose sibling to retrieve (must |
| // * not be null). |
| // * @param filter |
| // * an optional filter to limit the entities (may be null). |
| // * @param sortBy |
| // * the order in which the list should be sorted (must not be |
| // * null). |
| // * @param backwards |
| // * true to fetch the previous sibling, false to fetch the next |
| // * sibling. |
| // * @return the query that will return the sibling and all the subsequent |
| // * entities unless limited. |
| // */ |
| // protected TypedQuery<Long> createSiblingIndexQuery(Object entityId, |
| // ILFilter filter, List<SortBy> sortBy, boolean backwards) { |
| // assert entityId != null : "entityId must not be null"; |
| // assert sortBy != null : "sortBy must not be null"; |
| // ILFilter limitingFilter; |
| // sortBy = addPrimaryKeyToSortList(sortBy); |
| // if (sortBy.size() == 1) { |
| // // The list is sorted by primary key |
| // if (backwards) { |
| // limitingFilter = new Less(getEntityClassMetadata() |
| // .getIdentifierProperty().getName(), entityId); |
| // } else { |
| // limitingFilter = new Greater(getEntityClassMetadata() |
| // .getIdentifierProperty().getName(), entityId); |
| // } |
| // } else { |
| // // We have to fetch the values of the sorted fields |
| // T currentEntity = getEntity(entityId); |
| // if (currentEntity == null) { |
| // throw new EntityNotFoundException( |
| // "No entity found with the ID " + entityId); |
| // } |
| // // Collect the values into a map for easy access |
| // Map<Object, Object> filterValues = new HashMap<Object, Object>(); |
| // for (SortBy sb : sortBy) { |
| // filterValues.put( |
| // sb.getPropertyId(), |
| // getEntityClassMetadata().getPropertyValue( |
| // currentEntity, sb.getPropertyId().toString())); |
| // } |
| // // Now we can build a filter that limits the query to the entities |
| // // below entityId |
| // List<ILFilter> orFilters = new ArrayList<ILFilter>(); |
| // for (int i = sortBy.size() - 1; i >= 0; i--) { |
| // // TODO Document this code snippet once it works |
| // // TODO What happens with null values? |
| // List<ILFilter> caseFilters = new ArrayList<ILFilter>(); |
| // SortBy sb; |
| // for (int j = 0; j < i; j++) { |
| // sb = sortBy.get(j); |
| // caseFilters.add(new Equal(sb.getPropertyId(), filterValues |
| // .get(sb.getPropertyId()))); |
| // } |
| // sb = sortBy.get(i); |
| // if (sb.isAscending() ^ backwards) { |
| // caseFilters.add(new Greater(sb.getPropertyId(), |
| // filterValues.get(sb.getPropertyId()))); |
| // } else { |
| // caseFilters.add(new Less(sb.getPropertyId(), filterValues |
| // .get(sb.getPropertyId()))); |
| // } |
| // orFilters.add(new LAnd(CollectionUtil.toArray(ILFilter.class, |
| // caseFilters))); |
| // } |
| // limitingFilter = new LOr(CollectionUtil.toArray(ILFilter.class, |
| // orFilters)); |
| // } |
| // // Now, we can create the query |
| // ILFilter queryFilter; |
| // if (filter == null) { |
| // queryFilter = limitingFilter; |
| // } else { |
| // queryFilter = new LAnd(filter, limitingFilter); |
| // } |
| // TypedQuery<Long> query = createFilteredCountQuery(queryFilter, sortBy, |
| // backwards); |
| // return query; |
| // } |
| |
| protected Object doGetNextEntityIdentifier(Object entityId, ILFilter filter, List<SortBy> sortBy, |
| LockModeType lockModeType) { |
| if (sortBy == null) { |
| sortBy = Collections.emptyList(); |
| } |
| return getSibling(entityId, filter, sortBy, false, lockModeType); |
| } |
| |
| public Object getNextEntityIdentifier(Object entityId, IQuery query, LockModeType lockModeType) { |
| return doGetNextEntityIdentifier(entityId, query.getFilter(), query.getSortOrder().getSortBy(), lockModeType); |
| } |
| |
| protected T doGetNextEntity(Object entityId, ILFilter filter, List<SortBy> sortBy, LockModeType lockModeType) { |
| if (sortBy == null) { |
| sortBy = Collections.emptyList(); |
| } |
| Object resultId = getSibling(entityId, filter, sortBy, false, lockModeType); |
| if (resultId != null) { |
| return getEntity(resultId, lockModeType); |
| } |
| return null; |
| } |
| |
| public T getNextEntity(Object entityId, IQuery query, LockModeType lockModeType) { |
| return doGetNextEntity(entityId, query.getFilter(), query.getSortOrder().getSortBy(), lockModeType); |
| } |
| |
| protected Object doGetPreviousEntityIdentifier(Object entityId, ILFilter filter, List<SortBy> sortBy, |
| LockModeType lockModeType) { |
| if (sortBy == null) { |
| sortBy = Collections.emptyList(); |
| } |
| return getSibling(entityId, filter, sortBy, true, lockModeType); |
| } |
| |
| public Object getPreviousEntityIdentifier(Object entityId, IQuery query, LockModeType lockModeType) { |
| return doGetPreviousEntityIdentifier(entityId, query.getFilter(), query.getSortOrder().getSortBy(), |
| lockModeType); |
| } |
| |
| protected T doGetPreviousEntity(Object entityId, ILFilter filter, List<SortBy> sortBy, LockModeType lockModeType) { |
| if (sortBy == null) { |
| sortBy = Collections.emptyList(); |
| } |
| Object resultId = getSibling(entityId, filter, sortBy, true, lockModeType); |
| if (resultId != null) { |
| return getEntity(resultId, lockModeType); |
| } |
| return null; |
| } |
| |
| public T getPreviousEntity(Object entityId, IQuery query, LockModeType lockModeType) { |
| return doGetPreviousEntity(entityId, query.getFilter(), query.getSortOrder().getSortBy(), lockModeType); |
| } |
| |
| /** |
| * Detaches <code>entity</code> from the entity manager. If |
| * <code>entity</code> is null, then null is returned. If |
| * {@link #isEntitiesDetached() } is false, <code>entity</code> is returned |
| * directly. |
| * |
| * @param entity |
| * the entity to detach. |
| * @return the detached entity. |
| */ |
| protected T detachEntity(T entity) { |
| if (entity == null) { |
| return null; |
| } |
| if (isEntitiesDetached()) { |
| getEntityManager().detach(entity); |
| } |
| return entity; |
| } |
| |
| public boolean isEntitiesDetached() { |
| return entitiesDetached; |
| } |
| |
| public void setEntitiesDetached(boolean detached) throws UnsupportedOperationException { |
| this.entitiesDetached = detached; |
| } |
| |
| protected List<Object> doGetAllEntityIdentifiers(ILFilter filter, List<SortBy> sortBy, LockModeType lockModeType) { |
| if (sortBy == null) { |
| sortBy = Collections.emptyList(); |
| } |
| sortBy = addPrimaryKeyToSortList(sortBy); |
| TypedQuery<Object> query = createFilteredPropertyQuery( |
| Arrays.asList(getEntityClassMetadata().getIdentifierProperty().getName()), filter, sortBy, false, |
| lockModeType); |
| return Collections.unmodifiableList(query.getResultList()); |
| } |
| |
| public List<Object> getAllEntityIdentifiers(IQuery query, LockModeType lockModeType) { |
| return doGetAllEntityIdentifiers(query.getFilter(), query.getSortOrder().getSortBy(), lockModeType); |
| } |
| |
| protected List<T> doGetAllEntities(ILFilter filter, List<SortBy> sortBy, LockModeType lockModeType) { |
| if (sortBy == null) { |
| sortBy = Collections.emptyList(); |
| } |
| sortBy = addPrimaryKeyToSortList(sortBy); |
| TypedQuery<T> query = createFilteredEntityQuery(filter, sortBy, false, lockModeType); |
| if (lockModeType == LockModeType.NONE || lockModeType == LockModeType.OPTIMISTIC) { |
| query.setMaxResults(maxResults); |
| } |
| return Collections.unmodifiableList(query.getResultList()); |
| } |
| |
| public List<T> getAllEntities(IJPQL jpql) { |
| EntityManager em = getEntityManager(); |
| |
| try { |
| em.getTransaction().begin(); |
| Query query = getEntityManager().createQuery(jpql.getJPQL()); |
| for (Map.Entry<String, Object> param : jpql.getParameters().entrySet()) { |
| query.setParameter(param.getKey(), param.getValue()); |
| } |
| |
| @SuppressWarnings("unchecked") |
| List<T> result = new ArrayList<>(query.getResultList()); |
| |
| return result; |
| } finally { |
| em.getTransaction().rollback(); |
| } |
| } |
| |
| public List<?> getAllValues(IJPQL jpql) { |
| EntityManager em = getEntityManager(); |
| |
| try { |
| em.getTransaction().begin(); |
| Query query = getEntityManager().createQuery(jpql.getJPQL()); |
| for (Map.Entry<String, Object> param : jpql.getParameters().entrySet()) { |
| query.setParameter(param.getKey(), param.getValue()); |
| } |
| |
| @SuppressWarnings("unchecked") |
| List<T> result = new ArrayList<>(query.getResultList()); |
| |
| return result; |
| } finally { |
| em.getTransaction().rollback(); |
| } |
| } |
| |
| public List<T> getAllEntities(IQuery query, LockModeType lockModeType) { |
| return doGetAllEntities(query.getFilter(), query.getSortOrder().getSortBy(), lockModeType); |
| } |
| |
| protected List<T> doGetAllEntities(ILFilter filter, List<SortBy> sortBy, int startIndex, |
| LockModeType lockModeType) { |
| if (sortBy == null) { |
| sortBy = Collections.emptyList(); |
| } |
| sortBy = addPrimaryKeyToSortList(sortBy); |
| TypedQuery<T> query = createFilteredEntityQuery(filter, sortBy, false, lockModeType); |
| query.setFirstResult(startIndex); |
| if (lockModeType == LockModeType.NONE || lockModeType == LockModeType.OPTIMISTIC) { |
| query.setMaxResults(maxResults); |
| } |
| return Collections.unmodifiableList(query.getResultList()); |
| } |
| |
| public List<T> getAllEntities(IQuery query, int startIndex, LockModeType lockModeType) { |
| return doGetAllEntities(query.getFilter(), query.getSortOrder().getSortBy(), startIndex, lockModeType); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see |
| * com.vaadin.addon.jpacontainer.EntityProvider#setQueryModifierDelegate |
| * (com.vaadin.addon.jpacontainer.EntityProvider.QueryModifierDelegate) |
| */ |
| public void setQueryModifierDelegate(QueryModifierDelegate delegate) { |
| this.queryModifierDelegate = delegate; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see |
| * com.vaadin.addon.jpacontainer.EntityProvider#getQueryModifierDelegate() |
| */ |
| public QueryModifierDelegate getQueryModifierDelegate() { |
| return queryModifierDelegate; |
| } |
| |
| // QueryModifierDelegate helper methods |
| |
| private void tellDelegateQueryWillBeBuilt(CriteriaBuilder cb, CriteriaQuery<?> query) { |
| if (queryModifierDelegate != null) { |
| queryModifierDelegate.queryWillBeBuilt(cb, query); |
| } |
| } |
| |
| private void tellDelegateQueryHasBeenBuilt(CriteriaBuilder cb, CriteriaQuery<?> query) { |
| if (queryModifierDelegate != null) { |
| queryModifierDelegate.queryHasBeenBuilt(cb, query); |
| } |
| } |
| |
| private void tellDelegateFiltersWillBeAdded(CriteriaBuilder cb, CriteriaQuery<?> query, |
| List<Predicate> predicates) { |
| if (queryModifierDelegate != null) { |
| queryModifierDelegate.filtersWillBeAdded(cb, query, predicates); |
| } |
| } |
| |
| private void tellDelegateFiltersWereAdded(CriteriaBuilder cb, CriteriaQuery<?> query) { |
| if (queryModifierDelegate != null) { |
| queryModifierDelegate.filtersWereAdded(cb, query); |
| } |
| } |
| |
| private void tellDelegateOrderByWillBeAdded(CriteriaBuilder cb, CriteriaQuery<?> query, List<Order> orderBy) { |
| if (queryModifierDelegate != null) { |
| queryModifierDelegate.orderByWillBeAdded(cb, query, orderBy); |
| } |
| } |
| |
| private void tellDelegateOrderByWereAdded(CriteriaBuilder cb, CriteriaQuery<?> query) { |
| if (queryModifierDelegate != null) { |
| queryModifierDelegate.orderByWasAdded(cb, query); |
| } |
| } |
| |
| public Object getIdentifier(T entity) { |
| return entityClassMetadata.getPropertyValue(entity, entityClassMetadata.getIdentifierProperty().getName()); |
| } |
| |
| public T refreshEntity(T entity) { |
| if (getEntityManager().contains(entity)) { |
| try { |
| getEntityManager().refresh(entity); |
| } catch (IllegalArgumentException e) { |
| // detached, removed or something, get by id from em and refresh |
| // than non-detached object |
| entity = findAndRefresh(entity); |
| } catch (EntityNotFoundException e) { |
| return null; |
| } catch (TransactionRequiredException e) { |
| // TODO: handle exception, only in transactional? |
| } |
| } else { |
| entity = findAndRefresh(entity); |
| } |
| return entity; |
| } |
| |
| private T findAndRefresh(T entity) { |
| entity = getEntityManager().find(getEntityClassMetadata().getMappedClass(), getIdentifier(entity)); |
| if (entity != null) { |
| try { |
| // now try to refresh the attached entity |
| getEntityManager().refresh(entity); |
| entity = detachEntity(entity); |
| } catch (TransactionRequiredException e) { |
| // NOP |
| } catch (Exception e) { |
| throw new RuntimeException(e); |
| } |
| } |
| return entity; |
| } |
| |
| // /* |
| // * (non-Javadoc) |
| // * |
| // * @see |
| // * com.vaadin.addon.jpacontainer.EntityContainer#setLazyLoadingDelegate( |
| // * com.vaadin.addon.jpacontainer.EntityContainer.LazyLoadingDelegate) |
| // */ |
| // public void setLazyLoadingDelegate(LazyLoadingDelegate delegate) { |
| // lazyLoadingDelegate = delegate; |
| // if (lazyLoadingDelegate != null) { |
| // lazyLoadingDelegate.setEntityProvider(this); |
| // } |
| // } |
| // |
| // public LazyLoadingDelegate getLazyLoadingDelegate() { |
| // return lazyLoadingDelegate; |
| // } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see com.vaadin.addon.jpacontainer.EntityProvider#refresh() |
| */ |
| public void refresh() { |
| // Nothing to do in this implementation, since we don't keep any |
| // items/entities cached. |
| } |
| } |