info.unterrainer.commons.httpserver.daos.JpqlCoreDao Maven / Gradle / Ivy
package info.unterrainer.commons.httpserver.daos;
import java.lang.reflect.InvocationTargetException;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.LockModeType;
import javax.persistence.NoResultException;
import javax.persistence.Query;
import javax.persistence.TypedQuery;
import org.mapstruct.ap.internal.util.Strings;
import info.unterrainer.commons.httpserver.exceptions.ForbiddenException;
import info.unterrainer.commons.httpserver.exceptions.InternalServerErrorException;
import info.unterrainer.commons.httpserver.jpas.BasicPermissionJpa;
import info.unterrainer.commons.httpserver.jsons.ListJson;
import info.unterrainer.commons.jreutils.DateUtils;
import info.unterrainer.commons.rdbutils.entities.BasicJpa;
import info.unterrainer.commons.rdbutils.enums.AsyncState;
import io.javalin.http.Context;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class JpqlCoreDao implements CoreDao
{
protected final Class
type;
@Getter
protected JpqlTransactionManager transactionManager;
@Getter
protected TenantData tenantData;
public JpqlCoreDao(final EntityManagerFactory emf, final Class
type) {
super();
this.type = type;
transactionManager = new JpqlTransactionManager(emf, null);
}
public JpqlCoreDao(final Function entityManagerFactorySupplier,
final Class type) {
super();
this.type = type;
transactionManager = new JpqlTransactionManager(null, entityManagerFactorySupplier);
}
@Override
public P create(final EntityManager em, final P entity, final Set tenantIds) {
LocalDateTime time = DateUtils.nowUtc();
entity.setCreatedOn(time);
entity.setEditedOn(time);
em.persist(entity);
if (hasTenantData())
try {
for (Long tenantId : tenantIds) {
BasicPermissionJpa tenantJpa = tenantData.getPermissionJpaType().getConstructor().newInstance();
tenantData.getReferenceSetMethod().invoke(tenantJpa, entity.getId());
tenantData.getTenantIdSetMethod().invoke(tenantJpa, tenantId);
tenantJpa.setCreatedOn(time);
tenantJpa.setEditedOn(time);
em.persist(tenantJpa);
}
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException
| InvocationTargetException | NoSuchMethodException | SecurityException e) {
throw new InternalServerErrorException(String.format("Error creating permission-entry in [%s]",
tenantData.getPermissionJpaType().getSimpleName()));
}
return entity;
}
@Override
public void delete(final EntityManager em, final Long id, final Set tenantIds) {
if (!isAllowed(em, id, tenantIds))
return;
em.createQuery(String.format("DELETE FROM %s AS o WHERE o.id = :id", type.getSimpleName()))
.setParameter("id", id)
.executeUpdate();
}
@Override
public P getById(final EntityManager em, final Long id, final Set tenantIds) {
try {
return getQuery(em, "o", null, "o.id = :id", Map.of("id", id), type, null, false, null, tenantIds)
.getSingleResult();
} catch (NoResultException e) {
return null;
}
}
@Override
public ListJson getList(final EntityManager em, final Long offset, final Long size, final String selectClause,
final String joinClause, final String whereClause, final ParamMap params, final String orderByClause,
final Set tenantIds) {
ListJson r = new ListJson<>();
r.setEntries(getList(em, getQuery(em, selectClause, joinClause, whereClause,
params == null ? null : params.getParameters(), type, orderByClause, false, null, tenantIds), offset,
size));
r.setCount((Long) getCountQuery(em, selectClause, joinClause, whereClause,
params == null ? null : params.getParameters(), null, tenantIds).getSingleResult());
return r;
}
@Override
public P update(final EntityManager em, final P entity, final Set tenantIds) {
if (!isAllowed(em, entity.getId(), tenantIds))
throw new ForbiddenException();
LocalDateTime time = DateUtils.nowUtc();
entity.setEditedOn(time);
return em.merge(entity);
}
TypedQuery getQuery(final EntityManager em, final String selectClause, String joinClause, String whereClause,
Map params, final Class type, final String orderBy, final boolean lockPessimistic,
final Set asyncStates, final Set tenantIds) {
String query = "SELECT ";
if (selectClause == null || selectClause.isBlank())
query += "o";
else
query += selectClause;
query += " FROM %s AS o";
joinClause = addTenantJoin(joinClause);
if (joinClause != null && !joinClause.isBlank())
query += " " + joinClause;
whereClause = addTenantWhere(whereClause, tenantIds);
query += buildWhereClause(whereClause, asyncStates);
if (orderBy == null)
query += " ORDER BY o.id ASC";
else if (!orderBy.isBlank())
query += " ORDER BY " + orderBy;
log.debug("Query is: [{}]", query);
query = String.format(query, this.type.getSimpleName());
@SuppressWarnings("unchecked")
Class t = (Class) this.type;
if (type != null)
t = type;
TypedQuery q = em.createQuery(query, t);
if (lockPessimistic)
q.setLockMode(LockModeType.PESSIMISTIC_WRITE);
log.debug(" with lockmode: [{}]", q.getLockMode());
if (asyncStates != null)
log.debug(" with asynchronous_state: [{}]",
Strings.join(asyncStates.stream().map(AsyncState::toString).collect(Collectors.toList()), ", "));
addAsyncStatesParamsToQuery(asyncStates, q);
params = addTenantParams(params, tenantIds);
if (params != null) {
log.debug(" with parameters: [{}]",
Strings.join(params.entrySet()
.stream()
.map(e -> e.getKey() + ": " + e.getValue().toString())
.collect(Collectors.toList()), ", "));
for (Entry e : params.entrySet())
q.setParameter(e.getKey(), e.getValue());
}
return q;
}
Query getCountQuery(final EntityManager em, final String selectClause, String joinClause, String whereClause,
Map params, final Set asyncStates, final Set tenantIds) {
String query = "SELECT COUNT(";
if (selectClause == null || selectClause.isBlank())
query += "o.id";
else
query += selectClause;
query += ") FROM %s AS o";
joinClause = addTenantJoin(joinClause);
if (joinClause != null && !joinClause.isBlank())
query += " " + joinClause;
whereClause = addTenantWhere(whereClause, tenantIds);
query += buildWhereClause(whereClause, asyncStates);
params = addTenantParams(params, tenantIds);
Query q = em.createQuery(String.format(query, this.type.getSimpleName()));
addAsyncStatesParamsToQuery(asyncStates, q);
if (params != null)
for (Entry e : params.entrySet())
q.setParameter(e.getKey(), e.getValue());
return q;
}
List getList(final EntityManager em, final TypedQuery query, final long offset, final long size) {
int s = Integer.MAX_VALUE;
if (size < s)
s = (int) size;
int o = Integer.MAX_VALUE;
if (offset < o)
o = (int) offset;
query.setFirstResult(o);
query.setMaxResults(s);
return query.getResultList();
}
private boolean isAllowed(final EntityManager em, final Long id, final Set tenantIds) {
if (!hasTenantData())
return true;
if (tenantIds == null)
return false;
if (tenantIds.contains(null))
return true;
TypedQuery query = getQuery(em, "o.id", null, "o.id = :id", Map.of("id", id), Long.class, null, false,
null, tenantIds);
query.setMaxResults(1);
List list = query.getResultList();
return list != null && list.size() > 0;
}
private String addTenantJoin(final String joinClause) {
if (!hasTenantData())
return joinClause;
String r = joinClause;
if (joinClause == null || joinClause.isBlank())
r = "";
r += String.format(" LEFT JOIN %s tenantTable on o.id = tenantTable.%s",
tenantData.getPermissionJpaType().getSimpleName(), tenantData.getMainTableIdReferenceField());
return r;
}
private String addTenantWhere(final String whereClause, final Set tenantIds) {
if (!hasTenantData())
return whereClause;
String r = "";
if (whereClause != null && !whereClause.isBlank())
r = "( " + whereClause + " ) AND ";
r += String.format("( tenantTable.%1$s IS NULL", tenantData.getTenantIdField());
if (tenantIds != null && !tenantIds.isEmpty())
r += String.format(" OR tenantTable.%1$s IN (:tenantIds)", tenantData.getTenantIdField());
r += " )";
return r;
}
private Map addTenantParams(final Map map, final Set tenantIds) {
Map m;
if (map == null)
m = new HashMap<>();
else
m = new HashMap<>(map);
if (!hasTenantData() || tenantIds == null || tenantIds.isEmpty())
return m;
m.put("tenantIds", tenantIds);
return m;
}
private boolean hasTenantData() {
return tenantData != null && tenantData.getPermissionJpaType() != null
&& tenantData.getMainTableIdReferenceField() != null && tenantData.getTenantIdField() != null;
}
private boolean isSet(final String str) {
return str != null && !str.isBlank();
}
private boolean isSet(final Set> set) {
return set != null && !set.isEmpty();
}
private String buildWhereClause(final String whereClause, final Set asyncStates) {
String r = "";
if (!isSet(whereClause) && !isSet(asyncStates))
return r;
r = " WHERE ";
if (isSet(whereClause) && !isSet(asyncStates))
r += whereClause;
if (isSet(asyncStates)) {
if (isSet(whereClause))
r += "( " + whereClause + " ) AND ";
r += "( o.state IN :asynchronous_state )";
}
return r;
}
private void addAsyncStatesParamsToQuery(final Set asyncStates, final Query query) {
if (!isSet(asyncStates))
return;
query.setParameter("asynchronous_state", asyncStates);
}
}