/**
* This file is part of Pau's Asset Manager Project.
*
* Pau's Asset Manager Project is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Pau's Asset Manager Project is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Pau's Asset Manager Project. If not, see <http://www.gnu.org/licenses/>.
*/
package org.pau.assetmanager.business;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.NonUniqueResultException;
import javax.persistence.Query;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.pau.assetmanager.entities.EntityInterface;
import org.pau.assetmanager.utils.AssetManagerRuntimeException;
import org.pau.assetmanager.utils.PropertiesEntityManagerFactory;
import com.google.common.base.Function;
import com.google.common.base.Optional;
/**
* This class adds DAO capabilities in a functional way by using Guava
* easing the transaction management and the resource finalization
*
* @author Pau Carré Cardona
*
*/
public abstract class DaoFunction<F, T> implements Function<F, T> {
private static final Logger logger = LogManager.getLogger(DaoFunction.class);
/*
* This method is the apply method of the Function interface implemented for this class
* The method guarantees not only that the code in 'doInTransaction' is actually transactional
* but also that the resources are properly closed once the code in 'doInTransaction' finishes
*
* It is important to note that if the entity manager is managed inside of the 'doInTransaction' method
* the rules above are no longer guaranteed (i.e. do *not* begin nor commit transactions in 'doInTransaction')
*/
@Override
public T apply(F argument) {
EntityManager entityManager = null;
try {
entityManager = PropertiesEntityManagerFactory.getEntityManager();
entityManager.getTransaction().begin();
T result = doInTransaction(entityManager, argument);
entityManager.getTransaction().commit();
return result;
} finally {
if (entityManager != null && entityManager.isOpen()) {
if (entityManager.getTransaction().isActive()) {
try {
entityManager.getTransaction().rollback();
} finally {
entityManager.close();
}
} else {
entityManager.close();
}
}
}
}
/**
* This method is executed in the one that is actually executed and should contain the
* database login assuming that it is already in a transaction and without changing the
* general state of the entity manager (i.e. close or open) not its transaction (i.e. commit or roll back)
*
* @param argument argument of the function
* @param entityManager entity manager already in transaction
*
* @return the result of the function
*/
protected abstract T doInTransaction(EntityManager entityManager, F argument);
/**
* This method is used to delete a JPA pojo from the database
*
* @return the void function to remove the pojo
*/
public static <F extends EntityInterface> DaoFunction<F, Void> deleteDetachedFunction() {
return new DaoFunction<F, Void>() {
@Override
protected Void doInTransaction(
EntityManager entityManager, F argument) {
EntityInterface result = entityManager.find(
argument.getClass(), argument.getId());
entityManager.remove(result);
return null;
}
};
}
/**
* This method is used to persist a pojo in the database
*
* @return the void function to persist the pojo
*
*/
public static DaoFunction<Object, Void> persistFunction() {
return new DaoFunction<Object, Void>() {
@Override
protected Void doInTransaction(EntityManager entityManager, Object argument) {
entityManager.persist(argument);
return null;
}
};
}
/**
* This method is used to execute a native (SQL) update given an update query
*
* @return the void function to make a native update
*/
public static DaoFunction<String, Void> nativeUpdateFunction() {
return new DaoFunction<String, Void>() {
@Override
protected Void doInTransaction(EntityManager entityManager, String nativeQuery
) {
Query query = entityManager.createNativeQuery(nativeQuery);
query.executeUpdate();
return null;
}
};
}
/**
* This method is used to merge a pojo in the database
*
* @return the function 'Pojo to persist' -> 'Persisted Pojo' to merge a pojo in database
*/
public static <T> DaoFunction<T, T> mergeFunction() {
return new DaoFunction<T, T>() {
@Override
protected T doInTransaction(EntityManager entityManager,T argument
) {
argument = entityManager.merge(argument);
return argument;
}
};
}
/**
* This function is used to execute a JPA query that returns a list of Pojos
*
* @param argument 'Object[]{parameter_label_1, parameter_value_1, ... ,parameter_label_N, parameter_value_N}'
*
* @return A function of the form:
* query to be executed to retrieve the list -> 'List of pojos'
*/
public static <T> DaoFunction<String, List<T>> querySimpleListFunction(final Object... argument) {
return new DaoFunction<String, List<T>>() {
@SuppressWarnings("unchecked")
@Override
protected List<T> doInTransaction(EntityManager entityManager, String queryString
) {
Query query = entityManager.createQuery(queryString);
if (argument.length % 2 != 0) {
String message = "The arguments for the query should be a list of <String, Object> pairs";
logger.error(message);
throw new AssetManagerRuntimeException(message);
}
for (int index = 0; index < argument.length; index = index + 2) {
String label = (String) argument[index];
Object value = argument[index + 1];
query.setParameter(label, value);
}
return query.getResultList();
}
};
}
/**
* This function is used to execute a JPA query that returns a list of Pojos
*
* @param queryString query to be executed to retrieve the list
*
* @return A function of the form:
* 'Map< parameter_label, parameter_value >' -> 'List of pojos'
*/
public static <T> DaoFunction<Map<String, Serializable>, List<T>> queryListFunction(
final String queryString) {
return new DaoFunction<Map<String, Serializable>, List<T>>() {
@SuppressWarnings("unchecked")
@Override
protected List<T> doInTransaction(EntityManager entityManager,
Map<String, Serializable> argument
) {
Query query = entityManager.createQuery(queryString);
for (Entry<String, Serializable> entry : argument.entrySet()) {
query.setParameter(entry.getKey(), entry.getValue());
}
return query.getResultList();
}
};
}
/**
* This function is used to execute a JPA query that returns a single Pojo
*
* @param argument 'Object[]{parameter_label_1, parameter_value_1, ... ,parameter_label_N, parameter_value_N}'
*
* @return A function of the form:
* query to be executed to retrieve the pojo -> 'Optional pojo'
*/
public static <T> DaoFunction<String, Optional<T>> querySimpleUniqueFunction(
final Object... argument) {
return new DaoFunction<String, Optional<T>>() {
@SuppressWarnings("unchecked")
@Override
protected Optional<T> doInTransaction(EntityManager entityManager, String queryString
) {
Query query = entityManager.createQuery(queryString);
if (argument.length % 2 != 0) {
String message = "The arguments for the query should be a list of <String, Object> pairs";
throw new AssetManagerRuntimeException(message);
}
for (int index = 0; index < argument.length; index = index + 2) {
String label = (String) argument[index];
Object value = argument[index + 1];
query.setParameter(label, value);
}
try {
T result = (T) query.getSingleResult();
return Optional.of(result);
} catch (NoResultException e) {
logger.info("No result found searching a unique element", e);
return Optional.absent();
} catch(NonUniqueResultException nonUniqueResultException){
logger.error("Non unique result found searching a unique element", nonUniqueResultException);
throw new AssetManagerRuntimeException("Returned more that one element in the query", nonUniqueResultException);
}
}
};
}
/**
* This function is used to execute a JPA query that returns a single Pojo
*
* @param queryString query to be executed to retrieve the pojo
*
* @return A function of the form:
* 'Map< parameter_label, parameter_value >' -> 'Optional pojo'
*/
public static <T> DaoFunction<Map<String, Serializable>, Optional<T>> queryUniqueFunction(
final String queryString) {
return new DaoFunction<Map<String, Serializable>, Optional<T>>() {
@SuppressWarnings("unchecked")
@Override
protected Optional<T> doInTransaction(
EntityManager entityManager, Map<String, Serializable> argument
) {
Query query = entityManager.createQuery(queryString);
for (Entry<String, Serializable> entry : argument.entrySet()) {
query.setParameter(entry.getKey(), entry.getValue());
}
try {
T result = (T) query.getSingleResult();
return Optional.of(result);
} catch (NoResultException e) {
logger.info("No result from unique query", e);
return Optional.absent();
}
}
};
}
/**
* This function is used to make native queries (SQL) that return a list of objects.
*
* @param queryString query to be executed
*
* @return the function of the form:
* ' Map<'parameter_label','parameter_value'> -> 'List of pojos'
*/
public static <T> DaoFunction<Map<String, Serializable>, List<T>> nativeQueryListFunction(
final String queryString) {
return new DaoFunction<Map<String, Serializable>, List<T>>() {
@SuppressWarnings("unchecked")
@Override
protected List<T> doInTransaction(
EntityManager entityManager,Map<String, Serializable> argument
) {
Query query = entityManager.createNativeQuery(queryString);
for (Entry<String, Serializable> entry : argument.entrySet()) {
query.setParameter(entry.getKey(), entry.getValue());
}
return query.getResultList();
}
};
}
/**
* This function is used to make native queries (SQL) that return a list of objects.
*
* @param arguments 'Object[]{parameter_label_1, parameter_value_1, ... ,parameter_label_N, parameter_value_N}'
*
* @return the function of the form:
* 'query to be executed' -> 'List of pojos'
*/
public static <T> DaoFunction<String, List<T>> nativeQuerySimpleListFunction(
final Object... arguments) {
return new DaoFunction<String, List<T>>() {
@SuppressWarnings("unchecked")
@Override
protected List<T> doInTransaction(
EntityManager entityManager,String queryString
) {
Query query = entityManager.createNativeQuery(queryString);
if (arguments.length % 2 != 0) {
String message = "The arguments for the query should be a list of <String, Object> pairs";
throw new AssetManagerRuntimeException(message);
}
for (int index = 0; index < arguments.length; index = index + 2) {
String label = (String) arguments[index];
Object value = arguments[index + 1];
query.setParameter(label, value);
}
return query.getResultList();
}
};
}
/**
* This function is used to make native queries (SQL) that return an optional object.
*
* @param arguments 'Object[]{parameter_label_1, parameter_value_1, ... ,parameter_label_N, parameter_value_N}'
*
* @return the function of the form:
* 'query to be executed' -> 'Optional pojo'
*/
public static <T> DaoFunction<String, Optional<T>> nativeQuerySimpleUniqueFunction(
final Object... arguments) {
return new DaoFunction<String, Optional<T>>() {
@SuppressWarnings("unchecked")
@Override
protected Optional<T> doInTransaction(
EntityManager entityManager,String queryString
) {
Query query = entityManager.createNativeQuery(queryString);
if (arguments.length % 2 != 0) {
String message = "The arguments for the query should be a list of <String, Object> pairs";
throw new AssetManagerRuntimeException(message);
}
for (int index = 0; index < arguments.length; index = index + 2) {
String label = (String) arguments[index];
Object value = arguments[index + 1];
query.setParameter(label, value);
}
try {
T result = (T) query.getSingleResult();
return Optional.of(result);
} catch (NoResultException e) {
logger.info("No result from unique query", e);
return Optional.absent();
}
}
};
}
}