Commit 0d314806 authored by Martin Lowe's avatar Martin Lowe

Add call to get reference to DB object

This call can be used to help sync incoming data w/ foreign keys. If the
objects don't exist, an error is thrown.
Signed-off-by: Martin Lowe's avatarMartin Lowe <martin.lowe@eclipse-foundation.org>
parent eb28414f
......@@ -17,52 +17,60 @@ import org.eclipsefoundation.persistence.model.RDBMSQuery;
import io.quarkus.runtime.StartupEvent;
/**
* Interface for classes communicating with MongoDB. Assumes that reactive
* stream asynchronous calls are used rather than blocking methods.
* Interface for classes communicating with MongoDB. Assumes that reactive stream asynchronous calls are used rather
* than blocking methods.
*
* @author Martin Lowe
*/
public interface PersistenceDao extends HealthCheck {
/**
* Retrieves a list of typed results given the query passed.
*
* @param q the query object for the current operation
* @return a future result set of objects of type set in query
*/
<T extends BareNode> List<T> get(RDBMSQuery<T> q);
/**
* Retrieves a list of typed results given the query passed.
*
* @param q the query object for the current operation
* @return a future result set of objects of type set in query
*/
<T extends BareNode> List<T> get(RDBMSQuery<T> q);
/**
* Adds a list of typed documents to the currently active database and schema,
* using the query object to access the document type.
*
* @param <T> the type of document to post
* @param q the query object for the current operation
* @param documents the list of typed documents to add to the database instance.
* @return a future Void result indicating success on return.
*/
<T extends BareNode> List<T> add(RDBMSQuery<T> q, List<T> documents);
/**
* Adds a list of typed documents to the currently active database and schema, using the query object to access the
* document type.
*
* @param <T> the type of document to post
* @param q the query object for the current operation
* @param documents the list of typed documents to add to the database instance.
* @return a future Void result indicating success on return.
*/
<T extends BareNode> List<T> add(RDBMSQuery<T> q, List<T> documents);
/**
* Deletes documents that match the given query.
*
* @param <T> the type of document that is being deleted
* @param q the query object for the current operation
* @return a future deletion result indicating whether the operation was
* successful
*/
<T extends BareNode> void delete(RDBMSQuery<T> q);
/**
* Deletes documents that match the given query.
*
* @param <T> the type of document that is being deleted
* @param q the query object for the current operation
* @return a future deletion result indicating whether the operation was successful
*/
<T extends BareNode> void delete(RDBMSQuery<T> q);
/**
* Counts the number of filtered results of the given document type present.
*
* @param q the query object for the current operation
* @return a long result representing the number of results available for the
* given query and docuement type.
*/
Long count(RDBMSQuery<?> q);
/**
* Counts the number of filtered results of the given document type present.
*
* @param q the query object for the current operation
* @return a long result representing the number of results available for the given query and docuement type.
*/
Long count(RDBMSQuery<?> q);
default void startup(@Observes StartupEvent event) {
// intentionally empty
}
/**
* Retrieves a reference of an object to be used in operations on the server. This object is a proxy meant to help
* build FK relationships, but can be used in other operations as well.
*
* @param id the ID of the object to retrieve
* @param type the type of object that should be retrieved
* @return a reference to the DB object if found, null otherwise
*/
<T extends BareNode> T getReference(Object id, Class<T> type);
default void startup(@Observes StartupEvent event) {
// intentionally empty
}
}
......@@ -32,145 +32,153 @@ import org.slf4j.LoggerFactory;
* @author Martin Lowe
*/
public class DefaultHibernateDao implements PersistenceDao {
private static final Logger LOGGER = LoggerFactory.getLogger(DefaultHibernateDao.class);
@Inject
EntityManager em;
@ConfigProperty(name = "eclipse.db.default.limit")
int defaultLimit;
@ConfigProperty(name = "eclipse.db.default.limit.max")
int defaultMax;
@ConfigProperty(name = "eclipse.db.maintenance", defaultValue = "false")
boolean maintenanceFlag;
@Override
public <T extends BareNode> List<T> get(RDBMSQuery<T> q) {
if (maintenanceFlag) {
throw new MaintenanceException();
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Querying DB using the following query: {}", q.getFilter().getSelectSql());
}
// build base query
TypedQuery<T> query = em.createQuery(q.getFilter().getSelectSql(), q.getDocType());
// add ordinal parameters
int ord = 1;
for (Clause c : q.getFilter().getClauses()) {
for (Object param : c.getParams()) {
query.setParameter(ord++, param);
}
}
// check if result set should be limited
if (q.getDTOFilter().useLimit()) {
query = query.setFirstResult(getOffset(q)).setMaxResults(getLimit(q));
}
// run the query
return query.getResultList();
}
@Transactional
@Override
public <T extends BareNode> List<T> add(RDBMSQuery<T> q, List<T> documents) {
if (maintenanceFlag) {
throw new MaintenanceException();
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Adding {} documents to DB of type {}", documents.size(), q.getDocType().getSimpleName());
}
// for each doc, check if update or create
List<T> updatedDocs = new ArrayList<>(documents.size());
for (T doc : documents) {
T ref = doc;
if (doc.getId() != null) {
// ensure this object exists before merging on it
if (em.find(q.getDocType(), doc.getId()) != null) {
LOGGER.debug("Merging document with existing document with id '{}'", doc.getId());
ref = em.merge(doc);
} else {
LOGGER.debug("Persisting new document with id '{}'", doc.getId());
em.persist(doc);
}
} else {
LOGGER.debug("Persisting new document with generated UUID ID");
em.persist(doc);
}
// add the ref to the output list
updatedDocs.add(ref);
}
return updatedDocs;
}
@Transactional
@Override
public <T extends BareNode> void delete(RDBMSQuery<T> q) {
if (maintenanceFlag) {
throw new MaintenanceException();
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Removing documents from DB using the following query: {}", q);
}
// retrieve results for the given deletion query to delete using entity manager
List<T> results = get(q);
if (results.isEmpty()) {
throw new NoResultException("Could not find any documents with given filters");
}
// remove all matched documents
results.forEach(em::remove);
}
@Transactional
@Override
public Long count(RDBMSQuery<?> q) {
if (maintenanceFlag) {
throw new MaintenanceException();
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Counting documents in DB that match the following query: {}", q.getFilter().getCountSql());
}
// build base query
TypedQuery<Long> query = em.createQuery(q.getFilter().getCountSql(), Long.class);
// add ordinal parameters
int ord = 1;
for (Clause c : q.getFilter().getClauses()) {
for (Object param : c.getParams()) {
query.setParameter(ord++, param);
}
}
return query.getSingleResult();
}
private int getLimit(RDBMSQuery<?> q) {
return q.getLimit() > 0 ? Math.min(q.getLimit(), defaultMax) : defaultLimit;
}
private int getOffset(RDBMSQuery<?> q) {
// allow for manual offsetting
int manualOffset = q.getManualOffset();
if (manualOffset > 0) {
return manualOffset;
}
// if first page, no offset
if (q.getPage() <= 1) {
return 0;
}
int limit = getLimit(q);
return (limit * q.getPage()) - limit;
}
@Override
public HealthCheckResponse call() {
HealthCheckResponseBuilder b = HealthCheckResponse.named("DB readiness");
if (maintenanceFlag) {
return b.down().withData("error", "Maintenance flag is set").build();
}
return b.up().build();
}
private static final Logger LOGGER = LoggerFactory.getLogger(DefaultHibernateDao.class);
@Inject
EntityManager em;
@ConfigProperty(name = "eclipse.db.default.limit")
int defaultLimit;
@ConfigProperty(name = "eclipse.db.default.limit.max")
int defaultMax;
@ConfigProperty(name = "eclipse.db.maintenance", defaultValue = "false")
boolean maintenanceFlag;
@Override
public <T extends BareNode> List<T> get(RDBMSQuery<T> q) {
if (maintenanceFlag) {
throw new MaintenanceException();
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Querying DB using the following query: {}", q.getFilter().getSelectSql());
}
// build base query
TypedQuery<T> query = em.createQuery(q.getFilter().getSelectSql(), q.getDocType());
// add ordinal parameters
int ord = 1;
for (Clause c : q.getFilter().getClauses()) {
for (Object param : c.getParams()) {
query.setParameter(ord++, param);
}
}
// check if result set should be limited
if (q.getDTOFilter().useLimit()) {
query = query.setFirstResult(getOffset(q)).setMaxResults(getLimit(q));
}
// run the query
return query.getResultList();
}
@Transactional
@Override
public <T extends BareNode> List<T> add(RDBMSQuery<T> q, List<T> documents) {
if (maintenanceFlag) {
throw new MaintenanceException();
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Adding {} documents to DB of type {}", documents.size(), q.getDocType().getSimpleName());
}
// for each doc, check if update or create
List<T> updatedDocs = new ArrayList<>(documents.size());
for (T doc : documents) {
T ref = doc;
if (doc.getId() != null) {
// ensure this object exists before merging on it
if (em.find(q.getDocType(), doc.getId()) != null) {
LOGGER.debug("Merging document with existing document with id '{}'", doc.getId());
ref = em.merge(doc);
} else {
LOGGER.debug("Persisting new document with id '{}'", doc.getId());
em.persist(doc);
}
} else {
LOGGER.debug("Persisting new document with generated UUID ID");
em.persist(doc);
}
// add the ref to the output list
updatedDocs.add(ref);
}
return updatedDocs;
}
@Transactional
@Override
public <T extends BareNode> void delete(RDBMSQuery<T> q) {
if (maintenanceFlag) {
throw new MaintenanceException();
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Removing documents from DB using the following query: {}", q);
}
// retrieve results for the given deletion query to delete using entity manager
List<T> results = get(q);
if (results.isEmpty()) {
throw new NoResultException("Could not find any documents with given filters");
}
// remove all matched documents
results.forEach(em::remove);
}
@Transactional
@Override
public Long count(RDBMSQuery<?> q) {
if (maintenanceFlag) {
throw new MaintenanceException();
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Counting documents in DB that match the following query: {}", q.getFilter().getCountSql());
}
// build base query
TypedQuery<Long> query = em.createQuery(q.getFilter().getCountSql(), Long.class);
// add ordinal parameters
int ord = 1;
for (Clause c : q.getFilter().getClauses()) {
for (Object param : c.getParams()) {
query.setParameter(ord++, param);
}
}
return query.getSingleResult();
}
@Override
public <T extends BareNode> T getReference(Object id, Class<T> type) {
if (maintenanceFlag) {
throw new MaintenanceException();
}
return em.getReference(type, id);
}
private int getLimit(RDBMSQuery<?> q) {
return q.getLimit() > 0 ? Math.min(q.getLimit(), defaultMax) : defaultLimit;
}
private int getOffset(RDBMSQuery<?> q) {
// allow for manual offsetting
int manualOffset = q.getManualOffset();
if (manualOffset > 0) {
return manualOffset;
}
// if first page, no offset
if (q.getPage() <= 1) {
return 0;
}
int limit = getLimit(q);
return (limit * q.getPage()) - limit;
}
@Override
public HealthCheckResponse call() {
HealthCheckResponseBuilder b = HealthCheckResponse.named("DB readiness");
if (maintenanceFlag) {
return b.down().withData("error", "Maintenance flag is set").build();
}
return b.up().build();
}
}
......@@ -42,4 +42,9 @@ public class PlaceholderPersistenceDao implements PersistenceDao {
throw new IllegalStateException("Placeholder DAO should not be used in running instances");
}
@Override
public <T extends BareNode> T getReference(Object id, Class<T> type) {
throw new IllegalStateException("Placeholder DAO should not be used in running instances");
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment