/*
* Copyright 2002-2007 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.internna.iwebmvc.dao;
import java.util.List;
import java.util.Map;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.queryParser.MultiFieldQueryParser;
import org.hibernate.Hibernate;
import org.hibernate.search.jpa.FullTextEntityManager;
import org.hibernate.search.jpa.FullTextQuery;
import org.hibernate.search.jpa.Search;
import org.internna.iwebmvc.IWebMvcException;
import org.internna.iwebmvc.model.DomainEntity;
import org.internna.iwebmvc.model.UUID;
import org.internna.iwebmvc.utils.Assert;
import org.internna.iwebmvc.utils.ClassUtils;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import static org.internna.iwebmvc.utils.StringUtils.hasText;
import static org.internna.iwebmvc.utils.StringUtils.capitalize;
/**
* JPA DAO implementation.
*
* @author Jose Noheda
* @since 1.0
*/
public abstract class AbstractOperationalDAO extends AbstractDAO {
protected abstract EntityManager getEntityManager();
protected abstract PlatformTransactionManager getTransactionManager();
@Transactional
@Override public final void create(DomainEntity entity, boolean flush) {
Assert.notNull(entity);
Assert.isNull(entity.getId());
getEntityManager().persist(entity);
if (flush) flush();
}
@Override
@Transactional(readOnly = true)
public final <T extends DomainEntity> List<T> findByNamedQuery(String query, int offset, int max, Map<String, Object> parameters) {
return findByQuery(getEntityManager().createNamedQuery(query), offset, max, parameters);
}
@Override
@Transactional(readOnly = true)
public final <T extends DomainEntity> List<T> findByQuery(String query, int offset, int max, Map<String, Object> parameters) {
return findByQuery(getEntityManager().createQuery(query), offset, max, parameters);
}
@SuppressWarnings("unchecked")
@Transactional(readOnly = true)
protected final <T extends DomainEntity> List<T> findByQuery(Query query, int offset, int max, Map<String, Object> parameters) {
Assert.notNull(query);
prepareQuery(query, offset, max, parameters);
return (List<T>) query.getResultList();
}
@Override
@Transactional(readOnly = true)
public final <T extends DomainEntity> T find(Class<T> entityClass, UUID pk) {
Assert.notNull(pk);
Assert.notNull(entityClass);
T entity = null;
try {
entity = getEntityManager().find(entityClass, pk);
} catch (Exception ex) {
if (logger.isWarnEnabled()) logger.warn("Could not retrieve entity [" + entityClass + "] with primary key [" + pk + "]: " + ex.getMessage());
}
return entity;
}
@Override
@Transactional
public final DomainEntity update(DomainEntity entity) {
Assert.notNull(entity);
if (entity.getId() == null) {
create(entity);
return entity;
}
return getEntityManager().merge(entity);
}
@Transactional
@Override public final void remove(DomainEntity entity) {
Assert.notNull(entity);
Assert.notNull(entity.getId());
EntityManager em = getEntityManager();
try {
em.remove(entity);
} catch (IllegalArgumentException ie) {
logger.debug("Error removing entity from DB: " + ie.getMessage() + ". Trying to reattach and then remove the entity");
remove(entity.getClass(), entity.getId());
}
}
@Override
@Transactional
public final <T extends DomainEntity> T getReference(Class<T> entityClass, UUID pk) {
Assert.notNull(pk);
Assert.notNull(entityClass);
return getEntityManager().getReference(entityClass, pk);
}
@Override
@Transactional
public final int executeUpdate(String update, Map<String, Object> parameters) {
Assert.hasText(update);
return setParameters(getEntityManager().createQuery(update), parameters).executeUpdate();
}
@Override
@SuppressWarnings("unchecked")
@Transactional(readOnly = true)
public final List<Object> executeQuery(String query, int offset, int max, Map<String, Object> parameters) {
Assert.hasText(query);
if (logger.isDebugEnabled()) logger.debug("Executing query [" + query + "]");
return prepareQuery(getEntityManager().createQuery(query), offset, max, parameters).getResultList();
}
@Override
@Transactional(readOnly = true)
public List<Object> executeNativeQuery(String query, int offset, int max, Map<String, Object> parameters) {
return setParameters(getEntityManager().createNativeQuery(query), parameters).getResultList();
}
@Transactional
@Override public final void flush() {
getEntityManager().flush();
}
@Override final public void clear() {
getEntityManager().clear();
}
@SuppressWarnings("unchecked")
@Transactional(readOnly = true)
@Override public final <T extends DomainEntity> List<T> search(Class<T> clazz, String query, int offset, int number) {
Assert.notNull(clazz);
Assert.hasText(query);
try {
FullTextEntityManager fullTextEntityManager = Search.getFullTextEntityManager(getEntityManager());
List<String> indexedFields = getAllIndexedFields(clazz, "");
MultiFieldQueryParser parser = new MultiFieldQueryParser(indexedFields.toArray(new String[indexedFields.size()]), new StandardAnalyzer());
FullTextQuery hq = fullTextEntityManager.createFullTextQuery(parser.parse(query.trim().replaceAll(" ", "* ") + "*"), clazz);
hq.setMaxResults(number > 0 ? number : MAX_RESULTS);
hq.setFirstResult(offset > 0 ? offset : 0);
return hq.getResultList();
} catch (Exception ex) {
throw new IWebMvcException("Could not perform search", ex);
}
}
@Transactional
@Override public final boolean setRollbackOnly() {
try {
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
return true;
} catch (Exception ex) {
if (logger.isDebugEnabled()) logger.debug("Could not set rollback mark for current transaction: " + ex.getMessage());
return false;
}
}
/**
* Merges the entity in a new session to initialize all the properties required.
*
* @param <T> anything (this method will only take into account DomainEntity)
* @param entity any
* @param properties any (if null it wil work with the base object)
* @return any
*/
@Transactional(readOnly = true)
@Override public <T> T initialize(T entity, String... properties) {
T instance = entity;
if (entity instanceof DomainEntity) {
instance = getEntityManager().merge(entity);
if (properties != null) {
for (String property : properties) {
if (hasText(property)) {
try {
Hibernate.initialize(ClassUtils.call("get" + capitalize(property), instance));
} catch (Exception ex) {
if (logger.isDebugEnabled()) logger.debug("Could not initialize property [" + property + "] of [" + entity.getClass() + "]: " + ex.getMessage());
}
}
}
} else {
Hibernate.initialize(instance);
}
}
return instance;
}
}