/**
*
*/
package org.activejpa.entity;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
import javax.persistence.Id;
import javax.persistence.NoResultException;
import javax.persistence.NonUniqueResultException;
import javax.persistence.Query;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaDelete;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
import javax.persistence.metamodel.ManagedType;
import javax.persistence.metamodel.PluralAttribute;
import org.activejpa.ActiveJpaException;
import org.activejpa.jpa.JPA;
import org.activejpa.jpa.JPAContext;
import org.activejpa.util.BeanUtil;
/**
* <p> Base class for all entities. Embeds entity manager in it and provides a bunch of DAL abstractions to make data access a lot simpler.
* The static methods are to be implemented by the entity classes and will be done at runtime through activejpa instrumentation.
*
* <p> This allows activerecord style of usage,
*
* <pre>
* Person.findById(1L);
* Person.where("firstName", "Ganesh", "lastName", "Subramanian");
* Person.count();
* Person.collection("accounts")
*
* @author ganeshs
*
*/
public abstract class Model extends BaseObject {
private static final String NIE = "Your models are not instrumented. Make sure you run with -javaagent:activejpa-core.jar or you load the agent using ActiveJpaAgentLoader.instance().loadAgent()";
public Model() {
}
/**
* Loads the given attributes to this model
*
* @param attributes
*/
public void updateAttributes(Map<String, Object> attributes) {
try {
BeanUtil.load(this, attributes);
persist();
} catch (Exception e) {
throw new ActiveJpaException("Failed while updating the attributes", e);
}
}
/**
* The model identifier. Override and annotate with {@link Id}
*
* @return
*/
public abstract Serializable getId();
/**
* Returns the entity identified by the id
*
* @param id
* @return
*/
public static <T extends Model> T findById(Serializable id) {
throw new UnsupportedOperationException(NIE);
}
/**
* Returns the total count of rows in the table
*
* @return
*/
public static long count() {
throw new UnsupportedOperationException(NIE);
}
/**
* Returns the count of rows matching the given filter
*
* @param filter
* @return
*/
public static long count(Filter filter) {
throw new UnsupportedOperationException(NIE);
}
/**
* Returns all the rows in the table
*
* @return
*/
public static <T extends Model> List<T> all() {
throw new UnsupportedOperationException(NIE);
}
/**
* Deletes all the rows from the table
*/
public static void deleteAll() {
throw new UnsupportedOperationException(NIE);
}
/**
* Deletes the rows matching the given filter
*
* @param filter
*/
public static void deleteAll(Filter filter) {
throw new UnsupportedOperationException(NIE);
}
/**
* Checks if an entity exists with the given id
*
* @param id
* @return
*/
public static boolean exists(Serializable id) {
throw new UnsupportedOperationException(NIE);
}
/**
* Returns a list of entities matching the given key value pairs. The key value pairs are supplied as arguments like (key1, value1, key2, value2)
*
* @param paramValues
* @return
*/
public static <T extends Model> List<T> where(Object... paramValues) {
throw new UnsupportedOperationException(NIE);
}
/**
* Returns a list of entities matching the given filter
*
* @param filter
* @return
*/
public static <T extends Model> List<T> where(Filter filter) {
throw new UnsupportedOperationException(NIE);
}
/**
* Returns a single row matching the given key value pairs. The key value pairs are supplied as arguments like (key1, value1, key2, value2)
*
* <p> If more than one row matches or no row matches, throws an exception
*
* @param paramValues
* @return
* @throws NoResultException
* @throws NonUniqueResultException
*/
public static <T extends Model> T one(Object... paramValues) {
throw new UnsupportedOperationException(NIE);
}
/**
* Returns the first row matching the given key value pairs. The key value pairs are supplied as arguments like (key1, value1, key2, value2)
*
* @param paramValues
* @return
*/
public static <T extends Model> T first(Object... paramValues) {
throw new UnsupportedOperationException(NIE);
}
/**
* Starts the transaction if its not active already. Returns back the new transaction or existing active one.
*
* @return
*/
public static EntityTransaction beginTxn() {
JPA.instance.getDefaultConfig().getContext().beginTxn();
return getEntityManager().getTransaction();
}
protected static <T extends Model> T findById(Class<T> clazz, Serializable id) {
return getEntityManager().find(clazz, id);
}
protected static <T extends Model> T one(Class<T> clazz, Object... paramValues) {
return createQuery(clazz, paramValues).getSingleResult();
}
protected static <T extends Model> T first(Class<T> clazz, Object... paramValues) {
List<T> list = where(clazz, paramValues);
if (list != null && ! list.isEmpty()) {
return list.get(0);
}
return null;
}
protected static <T extends Model> List<T> where(Class<T> clazz, Object... paramValues) {
return createQuery(clazz, paramValues).getResultList();
}
protected static <T extends Model> List<T> where(Class<T> clazz, Filter filter) {
return createQuery(clazz, filter).getResultList();
}
protected static <T extends Model> long count(final Class<T> clazz) {
return count(clazz, new Filter());
}
protected static <T extends Model> long count(final Class<T> clazz, Filter filter) {
CriteriaBuilder builder = getEntityManager().getCriteriaBuilder();
CriteriaQuery<Long> cQuery = builder.createQuery(Long.class);
Root<T> root = cQuery.from(clazz);
cQuery.select(builder.count(root));
filter.constructQuery(builder, cQuery, root);
TypedQuery<Long> query = createQuery(cQuery, filter);
return query.getSingleResult();
}
protected static <T extends Model> List<T> all(Class<T> clazz) {
return getEntityManager().createQuery("from " + clazz.getSimpleName(), clazz).getResultList();
}
protected static <T extends Model> void deleteAll(Class<T> clazz) {
deleteAll(clazz, new Filter());
}
protected static <T extends Model> void deleteAll(final Class<T> clazz, final Filter filter) {
execute(manager -> {
CriteriaBuilder builder = manager.getCriteriaBuilder();
CriteriaDelete<T> deleteQuery = builder.createCriteriaDelete(clazz);
Root<T> root = deleteQuery.from(clazz);
filter.constructQuery(builder, deleteQuery, root);
Query query = createQuery(deleteQuery, filter);
return query.executeUpdate();
}, false);
}
protected static <T extends Model> boolean exists(Class<T> clazz, Serializable id) {
return findById(clazz, id) != null;
}
/**
* Save this entity to the persistence context
*/
public void persist() {
execute(manager -> {
manager.persist(Model.this);
return null;
}, false);
}
/**
* Delete this entity from the persistence context
*/
public void delete() {
execute(manager -> {
manager.remove(Model.this);
return null;
}, false);
}
/**
* Merge this entity with the one from the persistence context
*/
public void merge() {
execute(manager -> {
return manager.merge(Model.this);
}, false);
}
/**
* Reload this entity from the persistence context
*/
public void refresh() {
getEntityManager().refresh(this);
}
/**
* Returns the collection object identified by the given name
*
* @param name
* @return
*/
public <T extends Model> EntityCollection<T> collection(String name) {
ManagedType<? extends Model> type = getEntityManager().getMetamodel().managedType(getClass());
EntityCollection<T> collection = null;
if (type.getAttribute(name).isCollection()) {
Class<T> elementType = ((PluralAttribute)type.getAttribute(name)).getElementType().getJavaType();
collection = new EntityCollection<T>(this, name, elementType);
}
return collection;
}
protected static <T> T execute(Executor<T> executor, boolean readOnly) {
JPAContext context = JPA.instance.getDefaultConfig().getContext();
boolean beganTxn = false;
if (! context.isTxnOpen()) {
context.beginTxn();
beganTxn = true;
}
try {
return executor.execute(getEntityManager());
} finally {
if (beganTxn) {
context.closeTxn(readOnly);
}
}
}
public static interface Executor<T> {
T execute(EntityManager manager);
}
}