1 /*
2 * EL4J, the Extension Library for the J2EE, adds incremental enhancements to
3 * the spring framework, http://el4j.sf.net
4 * Copyright (C) 2010 by ELCA Informatique SA, Av. de la Harpe 22-24,
5 * 1000 Lausanne, Switzerland, http://www.elca.ch
6 *
7 * EL4J is published under the GNU Lesser General Public License (LGPL)
8 * Version 2.1. See http://www.gnu.org/licenses/
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Lesser General Public License for more details.
14 *
15 * For alternative licensing, please contact info@elca.ch
16 */
17 package ch.elca.el4j.services.persistence.jpa.dao;
18
19 import java.io.Serializable;
20 import java.util.Collection;
21 import java.util.List;
22
23 import javax.persistence.EntityManager;
24 import javax.persistence.EntityManagerFactory;
25 import javax.persistence.PersistenceException;
26 import javax.persistence.TypedQuery;
27 import javax.persistence.criteria.CriteriaQuery;
28
29 import org.springframework.dao.DataAccessException;
30 import org.springframework.dao.DataRetrievalFailureException;
31 import org.springframework.dao.OptimisticLockingFailureException;
32 import org.springframework.orm.hibernate3.HibernateTemplate;
33 import org.springframework.orm.jpa.JpaCallback;
34 import org.springframework.orm.jpa.JpaOptimisticLockingFailureException;
35 import org.springframework.orm.jpa.JpaTemplate;
36 import org.springframework.util.Assert;
37
38 import ch.elca.el4j.services.monitoring.notification.PersistenceNotificationHelper;
39 import ch.elca.el4j.services.search.QueryObject;
40 import ch.elca.el4j.util.codingsupport.Reject;
41
42 /**
43 * This is a convenience class for the Jpa template.
44 * Features:
45 * <ul>
46 * <li> improved paging support: allows to specify id of 1st element of a
47 * query
48 * <li> methods that signal an error if no element is found
49 * (they use the <em>Strong</em> suffixes)
50 * </ul>
51 *
52 * @svnLink $Revision: 4253 $;$Date: 2010-12-21 11:08:04 +0100 (Di, 21. Dez 2010) $;$Author: swismer $;$URL: https://el4j.svn.sourceforge.net/svnroot/el4j/branches/el4j_3_1/el4j/framework/modules/hibernate/src/main/java/ch/elca/el4j/services/persistence/jpa/dao/ConvenienceJpaTemplate.java $
53 *
54 * @author Simon Stelling (SST)
55 */
56 public class ConvenienceJpaTemplate extends JpaTemplate {
57
58 /**
59 * Constructor.
60 * @param emf EntityManagerFactory used to create EntityManager
61 */
62 public ConvenienceJpaTemplate(EntityManagerFactory emf) {
63 super(emf);
64 }
65
66 /**
67 * Merges the given persistent instance in a strong way: does the
68 * same as the <code>saveOrUpdate(Object)</code> method, but throws a more
69 * specific <code>OptimisticLockingFailureException</code> in the case of
70 * an optimistic locking failure.
71 *
72 * @see HibernateTemplate#saveOrUpdate(Object)
73 * @param entity
74 * the persistent entity to save or update
75 * @param objectName
76 * Name of the persistent object type.
77 * @throws DataAccessException
78 * in case of Hibernate errors
79 * @throws OptimisticLockingFailureException
80 * in case optimistic locking fails
81 * @return the merged entity
82 */
83 public Object mergeStrong(Object entity, final String objectName)
84 throws DataAccessException, OptimisticLockingFailureException {
85
86 Reject.ifNull(entity);
87 Reject.ifEmpty(objectName, "The name of the persistent object type "
88 + "must not be empty.");
89 try {
90 return merge(entity);
91 } catch (JpaOptimisticLockingFailureException holfe) {
92 String message = "The current " + objectName + " was modified or"
93 + " deleted in the meantime.";
94 PersistenceNotificationHelper.notifyOptimisticLockingFailure(
95 message, objectName, holfe);
96 throw holfe;
97 }
98 }
99
100 //// paging support /////
101
102 /**
103 * for paging: what is the id of the first result to return?
104 * NO_CONSTRAINT means we do not constrain anything
105 */
106 int m_firstResult = QueryObject.NO_CONSTRAINT;
107
108 /**
109 * Gets the id of the first result to return.
110 * @return The id of the first result to return.
111 */
112 public int getFirstResult() {
113 return m_firstResult;
114 }
115
116 /**
117 * Sets the id of the first result to return.
118 * @param firstResult The id of the first result to return.
119 */
120 public void setFirstResult(int firstResult) {
121 m_firstResult = firstResult;
122 }
123
124 /**
125 * Finds entities matching the given criteria query.
126 * @param <T> the entity type
127 * @param criteria the criteria query to run against the database
128 * @return the list of found objects, which may be empty.
129 */
130 public <T> List<T> findByCriteria(final CriteriaQuery<T> criteria) {
131 Assert.notNull(criteria, "CriteriaQuery must not be null");
132 return execute(new JpaCallback<List<T>>() {
133
134 @Override
135 public List<T> doInJpa(EntityManager em) throws PersistenceException {
136 return em.createQuery(criteria).getResultList();
137 }
138 });
139 }
140
141 /**
142 * Finds entities matching the given criteria query, with paging support.
143 * @param <T> the entity type
144 * @param criteria the criteria query to run against the database
145 * @param firstResult the index of the first row to return
146 * @param maxResults the maximum number of rows to return
147 * @return the list of found objects, which may be empty.
148 */
149 public <T> List<T> findByCriteria(final CriteriaQuery<T> criteria, final int firstResult, final int maxResults) {
150 Assert.notNull(criteria, "CriteriaQuery must not be null");
151 return execute(new JpaCallback<List<T>>() {
152
153 @Override
154 public List<T> doInJpa(EntityManager em) throws PersistenceException {
155 TypedQuery<T> query = em.createQuery(criteria);
156 query.setFirstResult(firstResult);
157 query.setMaxResults(maxResults);
158 return query.getResultList();
159 }
160 });
161
162 }
163
164 /**
165 * Retrieves the persistent instance given by its identifier in a strong
166 * way: does the same as the <code>find(Class, java.io.Serializable)</code>
167 * method, but throws a <code>DataRetrievalException</code> instead of
168 * <code>null</code> if the persistent instance could not be found.
169 *
170 * @param <T> entity type
171 * @param entityClass
172 * The class of the object which should be returned.
173 * @param id
174 * An identifier of the persistent instance
175 * @param objectName
176 * Name of the persistent object type.
177 * @return the persistent instance
178 * @throws org.springframework.dao.DataAccessException
179 * in case of Jpa persistence exceptions
180 * @throws org.springframework.dao.DataRetrievalFailureException
181 * in case the persistent instance is null
182 */
183 public <T> T findByIdStrong(Class<T> entityClass, Serializable id, final String objectName)
184 throws DataAccessException, DataRetrievalFailureException {
185
186 Reject.ifNull(id, "The identifier must not be null.");
187 Reject.ifEmpty(objectName, "The name of the persistent object type "
188 + "must not be empty.");
189
190 T result = find(entityClass, id);
191
192 if (result == null || !(entityClass.isInstance(result))) {
193 PersistenceNotificationHelper.notifyObjectRetrievalFailure(entityClass, id, objectName);
194 }
195 return result;
196 }
197
198 /**
199 * Retrieves the persistent instance given by its identifier in a strong
200 * way: does the same as the <code>getReference(Class, java.io.Serializable)</code>
201 * method, but throws a <code>DataRetrievalException</code> instead of
202 * <code>null</code> if the persistent instance could not be found.
203 *
204 * @param <T> entity type
205 * @param entityClass
206 * The class of the object which should be returned.
207 * @param id
208 * An identifier of the persistent instance
209 * @param objectName
210 * Name of the persistent object type.
211 * @return the persistent instance
212 * @throws org.springframework.dao.DataAccessException
213 * in case of Jpa persistence exceptions
214 * @throws org.springframework.dao.DataRetrievalFailureException
215 * in case the persistent instance is null
216 */
217 public <T> T findByIdStrongLazy(Class<T> entityClass, Serializable id, final String objectName)
218 throws DataAccessException, DataRetrievalFailureException {
219
220 Reject.ifNull(id, "The identifier must not be null.");
221 Reject.ifEmpty(objectName, "The name of the persistent object type "
222 + "must not be empty.");
223
224 T result = getReference(entityClass, id);
225
226 if (result == null || !(entityClass.isInstance(result))) {
227 PersistenceNotificationHelper.notifyObjectRetrievalFailure(entityClass, id, objectName);
228 }
229 return result;
230 }
231
232 /**
233 * Removes the persistent instance given by its identifier in a strong way:
234 * first, the persistent instance is retrieved with the help of the
235 * identifier. If it exists, it will be deleted, otherwise a
236 * <code>DataRetrievalFailureException</code> will be thrown.
237 *
238 * @see JpaTemplate#remove(Object)
239 * @param entityClass
240 * The class of the object which should be deleted.
241 * @param id
242 * The identifier of the persistent instance to delete
243 * @param objectName
244 * Name of the persistent object type.
245 * @throws org.springframework.dao.DataRetrievalFailureException
246 * in case the persistent instance to delete is null
247 */
248 public void removeStrong(Class<?> entityClass, Serializable id, final String objectName)
249 throws DataRetrievalFailureException {
250
251 Reject.ifEmpty(objectName, "The name of the persistent object type "
252 + "must not be empty.");
253 Object toDelete = null;
254 try {
255 toDelete = findByIdStrong(entityClass, id, objectName);
256 } catch (DataRetrievalFailureException e) {
257 String message = "The current " + objectName + " was "
258 + "deleted already!";
259 PersistenceNotificationHelper.notifyOptimisticLockingFailure(
260 message, objectName, null);
261 }
262 remove(toDelete);
263 }
264
265 /**
266 * removes all entities in the given collection.
267 * @param entities the collection of all entities.
268 */
269 public void removeAll(final Collection<?> entities) {
270 execute(new JpaCallback<Void>() {
271
272 @Override
273 public Void doInJpa(EntityManager em) throws PersistenceException {
274 for (Object e : entities) {
275 em.remove(e);
276 }
277 return null;
278 }
279
280 });
281 }
282 }