org.minijax.db.DefaultBaseDao Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of minijax-db Show documentation
Show all versions of minijax-db Show documentation
Minijax database and entity model
package org.minijax.db;
import static org.minijax.db.BaseDao.*;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceException;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The Dao class is the interface for all database access.
*/
public class DefaultBaseDao implements BaseDao {
private static final Logger LOG = LoggerFactory.getLogger(DefaultBaseDao.class);
@PersistenceContext
protected EntityManager em;
@Override
public EntityManager getEntityManager() {
return em;
}
/**
* Inserts a new instance in the database.
*
* @param obj The object to create.
* @return The instance with ID.
*/
@Override
public T create(final T obj) {
try {
em.getTransaction().begin();
em.persist(obj);
em.getTransaction().commit();
return obj;
} catch (final PersistenceException ex) {
throw convertRollbackToConflict(ex);
}
}
/**
* Retrieves an object by ID.
*
* @param id The ID.
* @return The object if found; null otherwise.
*/
@Override
public T read(final Class entityClass, final UUID id) {
return em.find(entityClass, id);
}
/**
* Finds a user by handle.
* Returns the user on success. Returns null on failure.
*
* @param handle The user's handle.
* @return The user on success; null on failure.
*/
@Override
public T readByHandle(final Class entityClass, final String handle) {
// Unfortunately @CacheIndex does not work with CriteriaBuilder, so using string query instead.
return firstOrNull(em.createQuery("SELECT e FROM " + entityClass.getSimpleName() + " e WHERE e.handle = :handle", entityClass)
.setParameter("handle", handle)
.getResultList());
}
/**
* Returns a page of objects.
*
* @param entityClass The entity class.
* @param page The page index (zero indexed).
* @param pageSize The page size.
* @return A page of objects.
*/
@Override
public List readPage(
final Class entityClass,
final int page,
final int pageSize) {
final CriteriaBuilder cb = em.getCriteriaBuilder();
final CriteriaQuery cq = cb.createQuery(entityClass);
final Root root = cq.from(entityClass);
cq.select(root);
cq.orderBy(cb.desc(root.get("id")));
return em.createQuery(cq)
.setFirstResult(page * pageSize)
.setMaxResults(pageSize)
.getResultList();
}
/**
* Updates an object.
*
* @param obj The object to update.
*/
@Override
public T update(final T obj) {
try {
em.getTransaction().begin();
em.merge(obj);
em.getTransaction().commit();
return obj;
} catch (final PersistenceException ex) {
throw convertRollbackToConflict(ex);
}
}
/**
* Soft deletes an object.
*
* The data is still in the database, but with deleted flag.
*
* @param obj The object to delete.
*/
@Override
public void delete(final T obj) {
obj.setDeleted(true);
update(obj);
}
/**
* Hard deletes an object.
*
* This purges the data from the database.
*
* @param obj The object to delete.
*/
@Override
public void purge(final T obj) {
@SuppressWarnings("unchecked")
final T actual = (T) em.find(obj.getClass(), obj.getId());
if (actual != null) {
em.getTransaction().begin();
em.remove(actual);
em.getTransaction().commit();
}
}
/**
* Counts all rows of a type.
*
* @param entityClass The entity class.
* @return The count of rows.
*/
@Override
public long countAll(final Class entityClass) {
final CriteriaBuilder cb = em.getCriteriaBuilder();
final CriteriaQuery cq = cb.createQuery(Long.class);
return em.createQuery(cq.select(cb.count(cq.from(entityClass)))).getSingleResult();
}
/*
* Private helper methods.
*/
/**
* Converts a JPA rollback exception into a conflict exception.
*
* @param ex The database rollback exception.
* @return A structured key/value conflict exception.
*/
private static ConflictException convertRollbackToConflict(final PersistenceException ex) {
final List patterns = Arrays.asList(
Pattern.compile("Duplicate entry '(?[^']+)' for key '(?[^']+)'"),
Pattern.compile("CONSTRAINT_INDEX_[a-zA-Z0-9_]+ ON PUBLIC\\.[a-zA-Z]+\\((?[a-zA-Z]+)\\) VALUES \\('(?[^']+)'"));
for (final Pattern pattern : patterns) {
final Matcher matcher = pattern.matcher(ex.getMessage());
if (matcher.find()) {
final String key = matcher.group("key").toLowerCase();
final String value = matcher.group("value");
return new ConflictException(key, value);
}
}
LOG.warn("Unrecognized RollbackException: {}", ex.getMessage(), ex);
throw ex;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy