package org.webpieces.plugins.hibernate;
import java.lang.annotation.Annotation;
import java.util.function.Function;
import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.persistence.metamodel.ManagedType;
import javax.persistence.metamodel.Metamodel;
import javax.persistence.metamodel.SingularAttribute;
import org.hibernate.metamodel.internal.EntityTypeImpl;
import org.webpieces.router.api.EntityLookup;
import org.webpieces.router.api.ObjectStringConverter;
import org.webpieces.router.impl.params.Meta;
import org.webpieces.router.impl.params.ObjectTranslator;
import org.webpieces.router.impl.params.ParamMeta;
import org.webpieces.router.impl.params.ParamNode;
import org.webpieces.router.impl.params.ParamTreeNode;
import org.webpieces.router.impl.params.ValueNode;
import org.webpieces.util.logging.Logger;
import org.webpieces.util.logging.LoggerFactory;
public class HibernateLookup implements EntityLookup {
private static final Logger log = LoggerFactory.getLogger(HibernateLookup.class);
private ObjectTranslator translator;
@Inject
public HibernateLookup(ObjectTranslator translator) {
this.translator = translator;
}
@Override
public <T> boolean isManaged(Class<T> paramTypeToCreate) {
EntityManager entityManager = Em.get();
try {
ManagedType<T> managedType = entityManager.getMetamodel().managedType(paramTypeToCreate);
EntityTypeImpl<T> entityType = (EntityTypeImpl<T>) managedType;
if(!entityType.hasSingleIdAttribute()) {
log.warn("You generally should be using beans with hibernate ids since this is a hibernate class");
return false; //if no single id attribute, let the default creator create the bean
}
} catch(IllegalArgumentException e) {
return false;
}
return true;
}
@SuppressWarnings("unchecked")
@Override
public <T> T find(Meta paramMeta, ParamTreeNode tree,
Function<Class<T>, T> beanCreate) {
if(!(paramMeta instanceof ParamMeta))
throw new UnsupportedOperationException("this plugin does not support type="+paramMeta.getClass());
ParamMeta m = (ParamMeta) paramMeta;
Class<T> paramTypeToCreate = (Class<T>) m.getFieldClass();
EntityManager entityManager = Em.get();
Metamodel metamodel = entityManager.getMetamodel();
ManagedType<T> managedType = metamodel.managedType(paramTypeToCreate);
EntityTypeImpl<T> entityType = (EntityTypeImpl<T>) managedType;
Class<?> idClazz = entityType.getIdType().getJavaType();
SingularAttribute<? super T, ?> idAttribute = entityType.getId(idClazz);
String name = idAttribute.getName();
ParamNode paramNode = tree.get(name);
String value = null;
if(paramNode != null) {
if(!(paramNode instanceof ValueNode))
throw new IllegalStateException("The id field in the hibernate entity should have matched to a "
+ "ValueNode on incoming data and did not. node="+paramNode+". bad multipart form? (Please "
+ "let us know so we can pair with you on this and I can add better error messaging)");
ValueNode node = (ValueNode) paramNode;
value = node.getValue();
}
if(value == null)
return beanCreate.apply(paramTypeToCreate);
@SuppressWarnings("rawtypes")
ObjectStringConverter unmarshaller = translator.getConverter(idClazz);
Object id = unmarshaller.stringToObject(value);
UseQuery namedQuery = fetchUseQuery(m.getAnnotations());
if(namedQuery == null)
return entityManager.find(paramTypeToCreate, id);
Query query = entityManager.createNamedQuery(namedQuery.value());
query.setParameter(namedQuery.id(), id);
return (T) query.getSingleResult();
}
private UseQuery fetchUseQuery(Annotation[] annotations) {
for(Annotation anno : annotations) {
if(anno instanceof UseQuery)
return (UseQuery)anno;
}
return null;
}
}