Java tutorial
/* * Copyright (C) 2003-2013 eXo Platform SAS. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.exoplatform.common.dao.hibernate; import java.io.Serializable; import java.lang.reflect.Array; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.persistence.NonUniqueResultException; import org.exoplatform.common.dao.config.RepositorySessionFactory; import org.exoplatform.common.dao.search.SearchCriterion; import org.hibernate.Criteria; import org.hibernate.Query; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.criterion.Restrictions; import com.googlecode.genericdao.search.ISearch; import com.googlecode.genericdao.search.SearchResult; import com.googlecode.genericdao.search.hibernate.HibernateMetadataUtil; import com.googlecode.genericdao.search.hibernate.HibernateSearchProcessor; /** * Base class for DAOs that uses Hibernate SessionFactory and HQL for searches. * This is the heart of Hibernate Generic DAO. * * @author <a href="mailto:sondn@exoplatform.com">Ngoc Son Dang</a> * @version HibernateJpaRepository.java Nov 6, 2013 * */ public class HibernateJpaRepository implements Repository { private final HibernateSearchProcessor searchProcessor; private final HibernateMetadataUtil metadataUtil; private final RepositorySessionFactory repositoryFactory; public HibernateJpaRepository(RepositorySessionFactory repositoryFactory) { this.repositoryFactory = repositoryFactory; this.metadataUtil = HibernateMetadataUtil .getInstanceForSessionFactory(repositoryFactory.getSessionFactory()); this.searchProcessor = HibernateSearchProcessor .getInstanceForSessionFactory(repositoryFactory.getSessionFactory()); } public SessionFactory getSessionFactory() { return repositoryFactory.getSessionFactory(); } @Override public Session getSession() { return getSessionFactory().getCurrentSession(); } @Override public Session openSession() { return getSessionFactory().openSession(); } /** * <p> * Persist the given transient instance and add it to the datastore, first * assigning a generated identifier. (Or using the current value of the * identifier property if the assigned generator is used.) This operation * cascades to associated instances if the association is mapped with * cascade="save-update". * * <p> * This is different from <code>_persistEntities()</code> in that it does * guarantee that the object will be assigned an identifier immediately. * With <code>save()</code> a call is made to the datastore immediately if * the id is generated by the datastore so that the id can be determined. * With <code>persist</code> this call may not occur until flush time. * * @return The id of the newly saved entity. */ public Serializable _saveEntity(Object entity) { Session session = openSession(); Serializable serial = null; try { serial = session.save(entity); session.flush(); session.clear(); } finally { session.close(); } return serial; } /** * Persist the given transient instances and add them to the datastore, * first assigning a generated identifier. (Or using the current value of * the identifier property if the assigned generator is used.) This * operation cascades to associated instances if the association is mapped * with cascade="save-update". * * @param entities */ public void _saveEntities(Object... entities) { Session session = openSession(); try { for (Object entity : entities) { session.save(entity); } session.flush(); session.clear(); } finally { session.close(); } } /** * <p> * Calls Hibernate's <code>saveOrUpdate()</code>, which behaves as follows: * * <p> * Either <code>save()</code> or <code>update()</code> based on the * following rules * <ul> * <li>if the object is already persistent in this session, do nothing * <li> * if another object associated with the session has the same identifier, * throw an exception * <li>if the object has no identifier property, save() it * <li>if the object's identifier has the value assigned to a newly * instantiated object, save() it * <li>if the object is versioned (by a <version> or * <timestamp>), and the version property value is the same value * assigned to a newly instantiated object, save() it * <li>otherwise update() the object * </ul> * * @param entity */ public void _saveOrUpdateEntity(Object entity) { Session session = openSession(); try { session.saveOrUpdate(entity); session.flush(); session.clear(); } finally { session.close(); } } /** * <p> * If an entity already exists in the datastore with the same id, call * _update and return false (not new). If no such entity exists in the * datastore, call _save() and return true (new) * * @return <code>true</code> if _save(); <code>false</code> if _update(). */ public boolean _saveOrUpdateIsNewEntity(Object entity) { if (entity == null) { throw new IllegalArgumentException("attempt to saveOrUpdate with null entity"); } Serializable serializable = metadataUtil.getId(entity); if (openSession().contains(entity)) return false; if (serializable == null || (new Long(0)).equals(serializable) || !_isExists(entity)) { _saveEntity(entity); return true; } else { _updateEntity(entity); return false; } } /** * Either <code>save()</code> or <code>update()</code> each entity, * depending on whether or not an entity with the same id already exists in * the datastore. * * @return an boolean array corresponding to to the input list of entities. * Each element is <code>true</code> if the corresponding entity was * <code>_save()</code>d or <code>false</code> if it was * <code>_update()</code>d. * */ public boolean[] _saveOrUpdateIsNewEntities(Object... entities) { Boolean[] exists = new Boolean[entities.length]; // if an entity is contained in the session, it exists; if it has no id, // it does not exist for (int i = 0; i < entities.length; i++) { if (entities[i] == null) { throw new IllegalArgumentException("attempt to saveOrUpdate with null entity"); } if (openSession().contains(entities[i])) { exists[i] = true; } else { Serializable id = metadataUtil.getId(entities[i]); if (id == null || (new Long(0)).equals(id)) { exists[i] = false; } } } // if it has an id and is not contained in the session, it may exist Map<Class<?>, List<Integer>> mayExist = new HashMap<Class<?>, List<Integer>>(); for (int i = 0; i < entities.length; i++) { if (exists[i] == null) { Class<?> entityClass = metadataUtil.getUnproxiedClass(entities[i]); // Get the real entity class List<Integer> list = mayExist.get(entityClass); if (list == null) { list = new ArrayList<Integer>(); mayExist.put(entityClass, list); } list.add(i); } } // for each type of entity, do a batch call to the datastore to see // which of the entities of that class exist for (Map.Entry<Class<?>, List<Integer>> entry : mayExist.entrySet()) { Serializable[] ids = new Serializable[entry.getValue().size()]; for (int i = 0; i < ids.length; i++) { ids[i] = metadataUtil.getId(entities[entry.getValue().get(i)]); } boolean exists2[] = _isExists(entry.getKey(), ids); for (int i = 0; i < ids.length; i++) { exists[entry.getValue().get(i)] = exists2[i]; } } boolean[] isNew = new boolean[entities.length]; // now that we know which ones exist, save or update each. for (int i = 0; i < entities.length; i++) { if (entities[i] != null) { if (exists[i]) { _updateEntity(entities[i]); isNew[i] = false; } else { _saveEntity(entities[i]); isNew[i] = true; } } } return isNew; } /** * <p> * Make a transient instance persistent and add it to the datastore. This * operation cascades to associated instances if the association is mapped * with cascade="persist". Throws an error if the entity already exists. * * <p> * This is different from <code>save()</code> in that it does not guarantee * that the object will be assigned an identifier immediately. With * <code>save()</code> a call is made to the datastore immediately if the id * is generated by the datastore so that the id can be determined. With * <code>persist</code> this call may not occur until flush time. */ public void _persistEntities(Object... entities) { Session session = openSession(); try { for (Object entity : entities) { session.persist(entity); } session.flush(); session.clear(); } finally { session.close(); } } /** * Remove the entity of the specified class with the specified id from the * datastore. * * @return <code>true</code> if the object is found in the datastore and * deleted, <code>false</code> if the item is not found. */ public boolean _deleteEntityById(Class<?> type, Serializable id) { if (id != null) { type = metadataUtil.getUnproxiedClass(type); // Get the real entity // class Session session = openSession(); try { Object entity = session.get(type, id); if (entity != null) { session.delete(entity); session.flush(); session.clear(); return true; } } finally { session.close(); } } return false; } /** * Remove all the entities of the given type from the datastore that have * one of these ids. */ public void _deleteEntityById(Class<?> type, Serializable... ids) { type = metadataUtil.getUnproxiedClass(type); // Get the real entity // class Session session = openSession(); try { Criteria criteria = session.createCriteria(type); criteria.add(Restrictions.in("id", ids)); for (Object entity : criteria.list()) { session.delete(entity); } session.flush(); session.clear(); } finally { session.close(); } } /** * Remove the specified entity from the datastore. * * @return <code>true</code> if the object is found in the datastore and * removed, <code>false</code> if the item is not found. */ public boolean _deleteEntity(Object entity) { if (entity != null) { Serializable id = metadataUtil.getId(entity); if (id != null) { Session session = openSession(); try { entity = session.get(metadataUtil.getUnproxiedClass(entity), id); if (entity != null) { session.delete(entity); session.flush(); session.close(); return true; } } finally { session.close(); } } } return false; } /** * Remove the specified entities from the datastore. */ public void _deleteEntities(Object... entities) { Session session = openSession(); try { for (Object entity : entities) { if (entity != null) { session.delete(entity); } } session.flush(); session.clear(); } finally { session.close(); } } /** * Return the persistent instance of the given entity class with the given * identifier, or null if there is no such persistent instance. * <code>get()</code> always hits the database immediately. */ public <T> T _getEntity(Class<T> type, Serializable id) { type = metadataUtil.getUnproxiedClass(type); // Get the real entity // class return (T) openSession().get(type, id); } /** * <p> * Return the all the persistent instances of the given entity class with * the given identifiers. An array of entities is returned that matches the * same order of the ids listed in the call. For each entity that is not * found in the datastore, a null will be inserted in its place in the * return array. * * <p> * <code>get()</code> always hits the database immediately. */ public <T> T[] _getEntities(Class<T> type, Serializable... ids) { type = metadataUtil.getUnproxiedClass(type); // Get the real entity // class Criteria criteria = openSession().createCriteria(type); criteria.add(Restrictions.in("id", ids)); Object[] retVal = (Object[]) Array.newInstance(type, ids.length); for (Object entity : criteria.list()) { Serializable id = metadataUtil.getId(entity); for (int i = 0; i < ids.length; i++) { if (id.equals(ids[i])) { retVal[i] = entity; break; } } } return (T[]) retVal; } /** * <p> * Return the persistent instance of the given entity class with the given * identifier, assuming that the instance exists. Throw an unrecoverable * exception if there is no matching database row. * * <p> * If the class is mapped with a proxy, <code>load()</code> just returns an * uninitialized proxy and does not actually hit the database until you * invoke a method of the proxy. This behaviour is very useful if you wish * to create an association to an object without actually loading it from * the database. It also allows multiple instances to be loaded as a batch * if batch-size is defined for the class mapping. */ public <T> T _loadEntity(Class<T> type, Serializable id) { type = metadataUtil.getUnproxiedClass(type); // Get the real entity // class return (T) openSession().load(type, id); } /** * <p> * Return the persistent instance of the given entity class with the given * identifier, assuming that the instance exists. Throw an unrecoverable * exception if there is no matching database row. An array of entities is * returned that matches the same order of the ids listed in the call. For * each entity that is not found in the datastore, a null will be inserted * in its place in the return array. * * @see #_loadEntity(Class, Serializable) */ public <T> T[] _loadEntities(Class<T> type, Serializable... ids) { type = metadataUtil.getUnproxiedClass(type); // Get the real entity // class Object[] retVal = (Object[]) Array.newInstance(type, ids.length); for (int i = 0; i < ids.length; i++) { if (ids[i] != null) { retVal[i] = _loadEntity(type, ids[i]); } } return (T[]) retVal; } /** * Read the persistent state associated with the given identifier into the * given transient instance. Throw an unrecoverable exception if there is no * matching database row. */ public void _loadEntity(Object transientEntity, Serializable id) { openSession().load(transientEntity, id); } /** * Get a list of all the objects of the specified class. */ public <T> List<T> _allEnties(Class<T> type) { type = metadataUtil.getUnproxiedClass(type); // Get the real entityclass return openSession().createCriteria(type).setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY).list(); } /** * <p> * Update the persistent instance with the identifier of the given detached * instance. If there is a persistent instance with the same identifier, an * exception is thrown. This operation cascades to associated instances if * the association is mapped with cascade="save-update". * * <p> * The difference between <code>update()</code> and <code>merge()</code> is * significant: <code>update()</code> will make the given object persistent * and throw and error if another object with the same ID is already * persistent in the Session. <code>merge()</code> doesn't care if another * object is already persistent, but it also doesn't make the given object * persistent; it just copies over the values to the datastore. */ public void _updateEntity(Object... transientEntities) { Session session = openSession(); try { for (Object entity : transientEntities) { session.update(entity); } session.flush(); session.clear(); } finally { session.close(); } } /** * <p> * Copy the state of the given object onto the persistent object with the * same identifier. If there is no persistent instance currently associated * with the session, it will be loaded. Return the persistent instance. If * the given instance is unsaved, save a copy of and return it as a newly * persistent instance. The given instance does not become associated with * the session. This operation cascades to associated instances if the * association is mapped with cascade="merge". * * <p> * The difference between <code>update()</code> and <code>merge()</code> is * significant: <code>update()</code> will make the given object persistent * and throw and error if another object with the same ID is already * persistent in the Session. <code>merge()</code> doesn't care if another * object is already persistent, but it also doesn't make the given object * persistent; it just copies over the values to the datastore. */ public <T> T _mergeEntity(T entity) { Session session = openSession(); T t = null; try { t = (T) session.merge(entity); session.flush(); session.clear(); } finally { session.close(); } return t; } /** * Search for objects based on the search parameters in the specified * <code>SearchParameters</code> object. * * @see SearchCriterion */ public List _searchEntity(SearchCriterion parameters) { if (parameters == null) { throw new NullPointerException("Search is null."); } if (parameters.getSearchClass() == null) { throw new NullPointerException("Search class is null."); } return searchProcessor.search(openSession(), parameters); } /** * Same as <code>_searchEntity(SearchParameters)</code> except that it uses * the specified search class instead of getting it from the search object. * Also, if the search object has a different search class than what is * specified, an exception is thrown. */ public List _searchEntity(Class<?> searchClass, SearchCriterion parameters) { if (parameters == null) { throw new NullPointerException("Search is null."); } if (searchClass == null) { throw new NullPointerException("Search class is null."); } if (parameters.getSearchClass() != null && !parameters.getSearchClass().equals(searchClass)) { throw new IllegalArgumentException( "Search class does not match expected type: " + searchClass.getName()); } return searchProcessor.search(openSession(), searchClass, parameters); } /** * Returns the total number of results that would be returned using the * given <code>SearchParameters</code> if there were no paging or maxResult * limits. * * @see SearchCriterion */ public int _countEntity(SearchCriterion parameters) { if (parameters == null) { throw new NullPointerException("Search is null."); } if (parameters.getSearchClass() == null) { throw new NullPointerException("Search class is null."); } return searchProcessor.count(openSession(), parameters); } /** * Same as <code>_countEntity(SearchParameters)</code> except that it uses * the specified search class instead of getting it from the search object. * Also, if the search object has a different search class than what is * specified, an exception is thrown. */ public int _countEntity(Class<?> searchClass, SearchCriterion parameters) { if (parameters == null) { throw new NullPointerException("Search is null."); } if (searchClass == null) { throw new NullPointerException("Search class is null."); } if (parameters.getSearchClass() != null && !parameters.getSearchClass().equals(searchClass)) { throw new IllegalArgumentException( "Search class does not match expected type: " + searchClass.getName()); } return searchProcessor.count(openSession(), searchClass, parameters); } /** * Returns the number of instances of this class in the datastore. */ public int _countEntity(Class<?> type) { List counts = openSession() .createQuery("select count(_it_) from " + metadataUtil.get(type).getEntityName() + " _it_").list(); int sum = 0; for (Object count : counts) { sum += ((Long) count).intValue(); } return sum; } /** * Returns a <code>SearchResult</code> object that includes the list of * results like <code>search()</code> and the total length like * <code>searchLength</code>. * * @see ISearch */ public SearchResult _searchAndCountEntity(SearchCriterion parameters) { if (parameters == null) { throw new NullPointerException("Search is null."); } if (parameters.getSearchClass() == null) { throw new NullPointerException("Search class is null."); } return searchProcessor.searchAndCount(openSession(), parameters); } /** * Same as <code>_searchAndCountEntity(SearchParameters)</code> except that * it uses the specified search class instead of getting it from the search * object. Also, if the search object has a different search class than what * is specified, an exception is thrown. */ public SearchResult _searchAndCountEntity(Class<?> searchClass, SearchCriterion parameters) { if (parameters == null) { throw new NullPointerException("Search is null."); } if (searchClass == null) { throw new NullPointerException("Search class is null."); } if (parameters.getSearchClass() != null && !parameters.getSearchClass().equals(searchClass)) { throw new IllegalArgumentException( "Search class does not match expected type: " + searchClass.getName()); } return searchProcessor.searchAndCount(openSession(), searchClass, parameters); } /** * Search for a single result using the given parameters. */ public Object _searchUniqueEntity(SearchCriterion parameters) throws NonUniqueResultException { if (parameters == null) { throw new NullPointerException("Search is null."); } if (parameters.getSearchClass() == null) { throw new NullPointerException("Search class is null."); } return searchProcessor.searchUnique(openSession(), parameters); } /** * Same as <code>_searchUniqueEntity(SearchParameters)</code> except that it * uses the specified search class instead of getting it from the search * object. Also, if the search object has a different search class than what * is specified, an exception is thrown. */ public Object _searchUniqueEntity(Class<?> searchClass, SearchCriterion parameters) { if (parameters == null) { throw new NullPointerException("Search is null."); } if (searchClass == null) { throw new NullPointerException("Search class is null."); } if (parameters.getSearchClass() != null && !parameters.getSearchClass().equals(searchClass)) { throw new IllegalArgumentException( "Search class does not match expected type: " + searchClass.getName()); } return searchProcessor.searchUnique(openSession(), searchClass, parameters); } /** * Returns true if the object is connected to the current hibernate session. */ public boolean _sessionContains(Object o) { return openSession().contains(o); } /** * Flushes changes in the hibernate cache to the datastore. */ public void _flush() { openSession().flush(); } /** * Refresh the content of the given entity from the current datastore state. */ public void _refreshEntities(Object... entities) { for (Object entity : entities) { openSession().refresh(entity); } openSession().flush(); } public boolean _isExists(Object entity) { if (openSession().contains(entity)) { return true; } return _isExists(entity.getClass(), metadataUtil.getId(entity)); } public boolean _isExists(Class<?> type, Serializable id) { if (type == null) { throw new NullPointerException("Type is null."); } if (id == null) { return false; } type = metadataUtil.getUnproxiedClass(type); // Get the real entity // class Query query = openSession() .createQuery("select id from " + metadataUtil.get(type).getEntityName() + " where id = :id"); query.setParameter("id", id); return query.list().size() == 1; } public boolean[] _isExists(Class<?> type, Serializable... ids) { if (type == null) { throw new NullPointerException("Type is null."); } type = metadataUtil.getUnproxiedClass(type); // Get the real entity // class boolean[] ret = new boolean[ids.length]; // we can't use "id in (:ids)" because some databases do not support // this for compound ids. StringBuilder sb = new StringBuilder("select id from " + metadataUtil.get(type).getEntityName() + " where"); boolean first = true; for (int i = 0; i < ids.length; i++) { if (first) { first = false; sb.append(" id = :id"); } else { sb.append(" or id = :id"); } sb.append(i); } Query query = openSession().createQuery(sb.toString()); for (int i = 0; i < ids.length; i++) { query.setParameter("id" + i, ids[i]); } for (Serializable id : (List<Serializable>) query.list()) { for (int i = 0; i < ids.length; i++) { if (id.equals(ids[i])) { ret[i] = true; // don't break. the same id could be in the list twice. } } } return ret; } }