package org.sigmah.server.dao.base;
/*
* #%L
* Sigmah
* %%
* Copyright (C) 2010 - 2016 URD
* %%
* This program 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.
*
* This program 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 this program. If not, see
* <http://www.gnu.org/licenses/gpl-3.0.html>.
* #L%
*/
import java.io.Serializable;
import java.sql.Connection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import org.hibernate.internal.SessionImpl;
import org.sigmah.server.domain.User;
import org.sigmah.server.domain.base.Entity;
import org.sigmah.server.domain.util.Deleteable;
import org.sigmah.server.inject.util.Injectors;
import com.google.inject.persist.Transactional;
/**
* <p>
* Abstract DAO implementation.
* </p>
* <p>
* Parent class of all DAO implementations.
* </p>
*
* @param <E>
* Entity type.
* @param <K>
* Entity id type (primary key).
* @author Denis Colliot (dcolliot@ideia.fr)
*/
public abstract class AbstractDAO<E extends Entity, K extends Serializable> extends EntityManagerProvider implements DAO<E, K> {
/**
* The entity class managed by DAO implementation.
*/
protected final Class<E> entityClass;
/**
* Initializes a new AbstractDAO.
* Populates the {@link #entityClass} attribute.
*/
@SuppressWarnings("unchecked")
protected AbstractDAO() {
this.entityClass = (Class<E>) Injectors.findGenericSuperClass(getClass()).getActualTypeArguments()[0];
}
/**
* {@inheritDoc}
*/
@Override
public final CriteriaBuilder getCriteriaBuilder() {
return em().getCriteriaBuilder();
}
// --------------------------------------------------------------------------------
//
// COUNT METHODS.
//
// --------------------------------------------------------------------------------
/**
* {@inheritDoc}
*/
@Override
public int countAll() {
final TypedQuery<Number> query = em().createQuery("SELECT COUNT(e) FROM " + entityClass.getName() + " e", Number.class);
return query.getSingleResult().intValue();
}
/**
* {@inheritDoc}
*/
@Override
public int countByCriteria(final CriteriaQuery<Number> criteriaQuery) {
final TypedQuery<Number> query = em().createQuery(criteriaQuery);
return query.getSingleResult().intValue();
}
// --------------------------------------------------------------------------------
//
// FIND METHODS.
//
// --------------------------------------------------------------------------------
/**
* {@inheritDoc}
*/
@Override
public E getReference(final K primaryKey) {
return em().getReference(entityClass, primaryKey);
}
/**
* {@inheritDoc}
*/
@Override
public List<E> findAll() {
return em().createQuery("SELECT e FROM " + entityClass.getName() + " e", entityClass).getResultList();
}
/**
* {@inheritDoc}
*/
@Override
public E findById(final K primaryKey) {
return em().find(entityClass, primaryKey);
}
/**
* {@inheritDoc}
*/
@Override
public List<E> findByIds(Set<K> primaryKeys) {
if (primaryKeys.isEmpty()) {
return Collections.emptyList();
}
TypedQuery<E> query = em().createQuery("SELECT e FROM " + entityClass.getName() + " e WHERE id IN (:ids)", entityClass);
query.setParameter("ids", primaryKeys);
return query.getResultList();
}
/**
* {@inheritDoc}
*/
@Override
public List<E> findByCriteria(final CriteriaQuery<E> criteriaQuery) {
return em().createQuery(criteriaQuery).getResultList();
}
// --------------------------------------------------------------------------------
//
// PERSIST METHODS.
//
// --------------------------------------------------------------------------------
/**
* {@inheritDoc}
*/
@Override
@Deprecated
@Transactional
public E persist(final E entity) {
return persist(entity, null);
}
/**
* {@inheritDoc}
*/
@Override
@Transactional
public E persist(final E entity, final User user) {
if (entity == null) {
return entity;
}
// TODO [DAO] Automatically set edition user and date into persisted entity.
// entity.setDateUpdated(new Date());
// entity.setUserUpdated(user.toString());
em().persist(entity);
return entity;
}
// --------------------------------------------------------------------------------
//
// REMOVE METHODS.
//
// --------------------------------------------------------------------------------
/**
* {@inheritDoc}
*/
@Override
@Deprecated
@Transactional
public void remove(final K primaryKey) {
remove(primaryKey, null);
}
/**
* {@inheritDoc}
*/
@Override
@Deprecated
@Transactional
public void remove(final E entity) {
remove(entity, null);
}
/**
* {@inheritDoc}
*/
@Override
@Transactional
public void remove(final K primaryKey, final User user) {
if (primaryKey == null) {
return;
}
remove(findById(primaryKey), user);
}
/**
* {@inheritDoc}
*/
@Override
@Transactional
public void remove(final E entity, final User user) {
if (entity instanceof Deleteable) {
final Deleteable deleteable = (Deleteable) entity;
deleteable.delete();
em().persist(deleteable); // Logical removal.
} else if (entity != null) {
em().remove(entity); // Physical removal.
}
}
// ------------------------------------------------------------------------------------------
//
// UTILITY METHODS.
//
// ------------------------------------------------------------------------------------------
/**
* Executes the given JPQL or native SQL {@code updateQuery}.
* Checks the transaction before execution (see {@link #checkTransaction(EntityManager)}).
*
* @param updateQuery
* The update JPQL or native SQL query.
* @return the number of elements updated/deleted.
*/
@Transactional
protected final int update(final Query updateQuery) {
if (updateQuery == null) {
throw new IllegalArgumentException("Update query is required.");
}
checkTransaction(em());
return updateQuery.executeUpdate();
}
/**
* Returns the given {@code em} inner session implementation.
*
* @param em
* The entity manager.
* @return the given {@code em} inner session implementation.
* @throws IllegalArgumentException
* If the given {@code em} is {@code null}.
*/
public static final org.hibernate.Session getSession(final EntityManager em) {
if (em == null) {
throw new IllegalArgumentException("Entity manager instance is required.");
}
return em.unwrap(org.hibernate.Session.class);
}
/**
* Returns the {@code java.sql.Connection} from the given {@code em}.
* Entity manager is flushed if active transaction is running.
*
* @param em
* The entity manager.
* @return the {@code java.sql.Connection} unwrapped from the given {@code em}.
*/
public static Connection getConnection(final EntityManager em) {
if (em == null) {
return null;
}
checkTransaction(em);
return em.unwrap(SessionImpl.class).connection();
}
/**
* Checks if active transaction is running. If so, given {@code em} is flushed in order to synchronize persistence
* context.
*
* @param em
* The entity manager.
*/
private static void checkTransaction(final EntityManager em) {
if (em == null) {
return;
}
if (em.getTransaction().isActive()) {
// Active transaction running, flushing em to synchronize context.
em.flush();
}
}
}