/*
* Copyright 2002-2016 the original author or authors.
*
* 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.springframework.integration.jpa.core;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import javax.persistence.Parameter;
import javax.persistence.Query;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.integration.jpa.support.JpaUtils;
import org.springframework.integration.jpa.support.parametersource.ParameterSource;
import org.springframework.integration.jpa.support.parametersource.PositionSupportingParameterSource;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* Class similar to JPA template limited to the operations required for the JPA adapters/gateway
* not using JpaTemplate as the class is deprecated since Spring 3.1
*
* @author Amol Nayak
* @author Gunnar Hillert
* @author Artem Bilan
*
* @since 2.2
*/
public class DefaultJpaOperations extends AbstractJpaOperations {
private static final Log logger = LogFactory.getLog(DefaultJpaOperations.class);
@Override
public void delete(Object entity) {
Assert.notNull(entity, "The entity must not be null!");
entityManager.remove(entity);
}
@Override
public void deleteInBatch(Iterable<?> entities) {
Assert.notNull(entities, "entities must not be null.");
Iterator<?> iterator = entities.iterator();
if (!iterator.hasNext()) {
return;
}
Class<?> entityClass = null;
for (Object object : entities) {
if (entityClass == null) {
entityClass = object.getClass();
}
else {
if (entityClass != object.getClass()) {
throw new IllegalArgumentException("entities must be of the same type.");
}
}
}
final String entityName = JpaUtils.getEntityName(entityManager, entityClass);
final String queryString = JpaUtils.getQueryString(JpaUtils.DELETE_ALL_QUERY_STRING, entityName);
JpaUtils.applyAndBind(queryString, entities, entityManager)
.executeUpdate();
}
@Override
public int executeUpdate(String updateQuery, ParameterSource source) {
Query query = entityManager.createQuery(updateQuery);
setParametersIfRequired(updateQuery, source, query);
return query.executeUpdate();
}
@Override
public int executeUpdateWithNamedQuery(String updateQuery, ParameterSource source) {
Query query = entityManager.createNamedQuery(updateQuery);
setParametersIfRequired(updateQuery, source, query);
return query.executeUpdate();
}
@Override
public int executeUpdateWithNativeQuery(String updateQuery, ParameterSource source) {
Query query = entityManager.createNativeQuery(updateQuery);
setParametersIfRequired(updateQuery, source, query);
return query.executeUpdate();
}
@Override
public <T> T find(Class<T> entityType, Object id) {
return entityManager.find(entityType, id);
}
private Query getQuery(String queryString, ParameterSource source) {
Query query = entityManager.createQuery(queryString);
setParametersIfRequired(queryString, source, query);
return query;
}
@Override
public List<?> getResultListForClass(Class<?> entityClass, int firstResult, int maxNumberOfResults) {
final String entityName = JpaUtils.getEntityName(entityManager, entityClass);
final Query query = entityManager.createQuery("select x from " + entityName + " x", entityClass);
if (firstResult > 0) {
query.setFirstResult(firstResult);
}
if (maxNumberOfResults > 0) {
query.setMaxResults(maxNumberOfResults);
}
return query.getResultList();
}
@Override
public List<?> getResultListForNamedQuery(String selectNamedQuery,
ParameterSource parameterSource, int firstResult, int maxNumberOfResults) {
final Query query = entityManager.createNamedQuery(selectNamedQuery);
setParametersIfRequired(selectNamedQuery, parameterSource, query);
if (firstResult > 0) {
query.setFirstResult(firstResult);
}
if (maxNumberOfResults > 0) {
query.setMaxResults(maxNumberOfResults);
}
return query.getResultList();
}
@Override
public List<?> getResultListForNativeQuery(String selectQuery, Class<?> entityClass,
ParameterSource parameterSource, int firstResult, int maxNumberOfResults) {
final Query query;
if (entityClass == null) {
query = entityManager.createNativeQuery(selectQuery);
}
else {
query = entityManager.createNativeQuery(selectQuery, entityClass);
}
setParametersIfRequired(selectQuery, parameterSource, query);
if (firstResult > 0) {
query.setFirstResult(firstResult);
}
if (maxNumberOfResults > 0) {
query.setMaxResults(maxNumberOfResults);
}
return query.getResultList();
}
@Override
public List<?> getResultListForQuery(String query, ParameterSource source) {
return getResultListForQuery(query, source, 0, 0);
}
@Override
public List<?> getResultListForQuery(String queryString, ParameterSource source,
int firstResult, int maxNumberOfResults) {
Query query = getQuery(queryString, source);
if (firstResult > 0) {
query.setFirstResult(firstResult);
}
if (maxNumberOfResults > 0) {
query.setMaxResults(maxNumberOfResults);
}
return query.getResultList();
}
@Override
public Object getSingleResultForQuery(String queryString, ParameterSource source) {
Query query = getQuery(queryString, source);
return query.getSingleResult();
}
@Override
public Object merge(Object entity) {
return this.merge(entity, 0, false);
}
@Override
public Object merge(Object entity, int flushSize, boolean clearOnFlush) {
Assert.notNull(entity, "The object to merge must not be null.");
return this.persistOrMerge(entity, true, flushSize, clearOnFlush);
}
@Override
public void persist(Object entity) {
this.persist(entity, 0, false);
}
@Override
public void persist(Object entity, int flushSize, boolean clearOnFlush) {
Assert.notNull(entity, "The object to persist must not be null.");
persistOrMerge(entity, false, flushSize, clearOnFlush);
}
private Object persistOrMerge(Object entity, boolean isMerge, int flushSize, boolean clearOnFlush) {
Object result = null;
if (entity instanceof Iterable) {
@SuppressWarnings("unchecked")
Iterable<Object> entities = (Iterable<Object>) entity;
int savedEntities = 0;
int nullEntities = 0;
List<Object> mergedEntities = new ArrayList<Object>();
for (Object iteratedEntity : entities) {
if (iteratedEntity == null) {
nullEntities++;
}
else {
if (isMerge) {
mergedEntities.add(entityManager.merge(iteratedEntity));
}
else {
entityManager.persist(iteratedEntity);
}
savedEntities++;
if (flushSize > 0 && savedEntities % flushSize == 0) {
entityManager.flush();
if (clearOnFlush) {
entityManager.clear();
}
}
}
}
if (logger.isDebugEnabled()) {
logger.debug(String.format("%s %s entities. %s NULL entities were ignored.",
isMerge ? "Merged" : "Persisted", savedEntities, nullEntities));
}
if (isMerge) {
result = mergedEntities;
}
}
else {
if (isMerge) {
result = entityManager.merge(entity);
}
else {
entityManager.persist(entity);
}
}
if (flushSize > 0) {
entityManager.flush();
if (clearOnFlush) {
entityManager.clear();
}
}
return result;
}
/**
* Given a JPQL query, this method gets all parameters defined in this query and
* use the {@link ParameterSource} to find their values and set them.
*
*/
private void setParametersIfRequired(String queryString, ParameterSource source, Query query) {
Set<Parameter<?>> parameters = query.getParameters();
if (parameters != null && !parameters.isEmpty()) {
if (source != null) {
for (Parameter<?> param:parameters) {
String paramName = param.getName();
Integer position = param.getPosition();
final Object paramValue;
if (position != null) {
if (source instanceof PositionSupportingParameterSource) {
paramValue = ((PositionSupportingParameterSource) source).getValueByPosition(position);
query.setParameter(position, paramValue);
}
else {
throw new JpaOperationFailedException("Positional Parameters are only support "
+ "for PositionSupportingParameterSources.", queryString);
}
}
else {
if (StringUtils.hasText(paramName)) {
paramValue = source.getValue(paramName);
query.setParameter(paramName, paramValue);
}
else {
throw new JpaOperationFailedException(
"This parameter does not contain a parameter name. " +
"Additionally it is not a positional parameter, neither.", queryString);
}
}
}
}
else {
throw new IllegalArgumentException("Query has parameters but no parameter source provided");
}
}
}
}