001 /*
002 * ----------------------------------------------------------------------
003 * Copyright (C) 2009 Enrique Lara (k957@68k.org)
004 *
005 * TinLizard is free software; you can redistribute it and/or
006 * modify it under the terms of the GNU Lesser General Public License
007 * as published by the Free Software Foundation; either version 3.0
008 * of the License, or (at your option) any later version.
009 *
010 * TinLizard is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
013 * GNU Lesser General Public License for more details.
014 *
015 * You should have received a copy of the GNU Lesser General Public License
016 * along with TinLizard. If not, see http://www.gnu.org/licenses/.
017 * ----------------------------------------------------------------------
018 */
019 package tinlizard.dao.jpa;
020
021 import tinlizard.model.CurrentUser;
022 import tinlizard.model.TinLizardConfig;
023
024 import java.util.Collection;
025 import java.util.Date;
026 import java.util.Iterator;
027 import java.util.List;
028
029 import javax.persistence.EntityExistsException;
030 import javax.persistence.EntityManager;
031 import javax.persistence.EntityManagerFactory;
032 import javax.persistence.EntityNotFoundException;
033 import javax.persistence.EntityTransaction;
034 import javax.persistence.NoResultException;
035 import javax.persistence.NonUniqueResultException;
036 import javax.persistence.Persistence;
037 import javax.persistence.Query;
038
039 import org.apache.log4j.Logger;
040 import org.apache.lucene.analysis.standard.StandardAnalyzer;
041 import org.apache.lucene.queryParser.MultiFieldQueryParser;
042
043 import org.hibernate.search.jpa.FullTextEntityManager;
044 import org.hibernate.search.jpa.Search;
045
046 /**
047 * Dao to wrap/handle common JPA interactions.
048 */
049 public final class JpaDao {
050 private static JpaDao instance = null;
051 private static final ThreadLocal<EntityManager> THREAD_EM = new ThreadLocal<EntityManager>();
052 private static final Logger LOG = Logger.getLogger(JpaDao.class);
053 private final EntityManagerFactory emf;
054
055 private JpaDao(final TinLizardConfig initializer) {
056 emf = Persistence.createEntityManagerFactory("TinLizardModel", initializer.getJpaProperties());
057 }
058
059 public static synchronized void initialize(final TinLizardConfig initializer) {
060 if (instance == null) {
061 instance = new JpaDao(initializer);
062 }
063 }
064
065 public static JpaDao getInstance() {
066 return instance;
067 }
068
069 public EntityManager getEm() {
070 EntityManager em = THREAD_EM.get();
071
072 if (em == null) {
073 em = emf.createEntityManager();
074 THREAD_EM.set(em);
075 }
076
077 return em;
078 }
079
080 void closeEm() {
081 EntityManager em = THREAD_EM.get();
082
083 LOG.trace("closeEm");
084
085 if (em != null) {
086 THREAD_EM.remove();
087
088 if (em.isOpen()) {
089 em.close();
090 }
091 }
092 }
093
094 @SuppressWarnings("unchecked")
095 public void add(final Persistable obj) {
096 //XXX consider @EntityListeners http://www.javaworld.com/javaworld/jw-01-2008/jw-01-jpa1.html?page=4
097 obj.setCreated(new Date());
098 obj.setCreatedBy(CurrentUser.getUsername());
099
100 EntityManager em = getEm();
101 EntityTransaction tx = em.getTransaction();
102 tx.begin();
103
104 try {
105 em.persist(obj);
106
107 tx.commit();
108 } catch (EntityExistsException e) {
109 handleError(tx, "Add Error", e);
110 } catch (Exception e) {
111 handleError(tx, "Add Error", e);
112 }
113 }
114
115 @SuppressWarnings("unchecked")
116 public void update(final Persistable obj) {
117 obj.setLastModified(new Date());
118 obj.setLastModifiedBy(CurrentUser.getUsername());
119
120 EntityManager em = getEm();
121 EntityTransaction tx = em.getTransaction();
122 tx.begin();
123
124 try {
125 em.merge(obj);
126
127 tx.commit();
128 } catch (Exception e) {
129 handleError(tx, "Update Error", e);
130 }
131 }
132
133 public void delete(final Class<?> clazz, final Integer id) {
134 EntityManager em = getEm();
135 EntityTransaction tx = em.getTransaction();
136 tx.begin();
137
138 try {
139 if (LOG.isDebugEnabled()) {
140 LOG.debug("delete(" + clazz + ", " + id + ")");
141 }
142
143 Query query = em.createQuery("delete from " + clazz.getSimpleName() + " o where o.id = :id");
144 query.setParameter("id", id);
145
146 int deleted = query.executeUpdate();
147
148 if (LOG.isDebugEnabled()) {
149 LOG.debug("Deleted " + deleted + ".");
150 }
151
152 tx.commit();
153 } catch (EntityNotFoundException e) {
154 handleInfo(tx, "Object " + clazz + "#" + id + " did not exist", e);
155 } catch (Exception e) {
156 handleError(tx, "Delete Error", e);
157 }
158 }
159
160 @SuppressWarnings("unchecked")
161 public void index(final Persistable obj) {
162 EntityManager em = getEm();
163 FullTextEntityManager fullTextEntityManager = Search.getFullTextEntityManager(em);
164 EntityTransaction tx = em.getTransaction();
165 tx.begin();
166
167 try {
168 fullTextEntityManager.index(obj);
169
170 tx.commit();
171 } catch (Exception e) {
172 handleError(tx, "Index Error", e);
173 }
174 }
175
176 @SuppressWarnings("unchecked")
177 public void indexAll(final Class<?extends Persistable> clazz) {
178 EntityManager em = getEm();
179 FullTextEntityManager fullTextEntityManager = Search.getFullTextEntityManager(em);
180 EntityTransaction tx = em.getTransaction();
181 tx.begin();
182
183 try {
184 fullTextEntityManager.purgeAll(clazz);
185
186 List objects = em.createQuery("select o from " + clazz.getSimpleName() + " o order by o.name").getResultList();
187
188 for (Iterator iterator = objects.iterator(); iterator.hasNext();) {
189 Object object = iterator.next();
190
191 fullTextEntityManager.index(object);
192 }
193
194 tx.commit();
195 } catch (Exception e) {
196 handleError(tx, "IndexAll Error", e);
197 }
198 }
199
200 public void handleInfo(final EntityTransaction tx, final String msg, final Exception e) {
201 rollbackTransaction(tx);
202 LOG.info(msg, e);
203 }
204
205 public void handleError(final EntityTransaction tx, final String msg, final Exception e) {
206 rollbackTransaction(tx);
207
208 closeEm();
209 throw new RuntimeException(msg, e);
210 }
211
212 private void rollbackTransaction(final EntityTransaction tx) {
213 if (tx.isActive()) {
214 tx.rollback();
215 }
216 }
217
218 @SuppressWarnings("unchecked")
219 public <T> List<T> findAll(final Class<T> clazz) {
220 List<T> rval = null;
221
222 EntityManager em = getEm();
223 EntityTransaction tx = em.getTransaction();
224 tx.begin();
225
226 try {
227 rval = em.createQuery("select o from " + clazz.getSimpleName() + " o order by o.name").getResultList();
228
229 tx.commit();
230 } catch (Exception e) {
231 handleError(tx, "FindAll Error", e);
232 }
233
234 return rval;
235 }
236
237 public <T> T findByPrimaryKey(final Class<T> clazz, final Integer id) {
238 T rval = null;
239 EntityManager em = getEm();
240 EntityTransaction tx = em.getTransaction();
241 tx.begin();
242
243 try {
244 if (LOG.isDebugEnabled()) {
245 LOG.debug("findByPrimaryKey(" + clazz + ", " + id + ")");
246 }
247
248 rval = em.find(clazz, id);
249
250 tx.commit();
251 } catch (Exception e) {
252 handleError(tx, "FindByPrimaryKey Error", e);
253 }
254
255 return rval;
256 }
257
258 public void flush() {
259 EntityManager em = getEm();
260 EntityTransaction tx = em.getTransaction();
261 tx.begin();
262
263 try {
264 em.flush();
265
266 tx.commit();
267 } catch (Exception e) {
268 handleError(tx, "Flush Error", e);
269 }
270 }
271
272 @SuppressWarnings("unchecked")
273 public <T> Collection<T> findByNamedQuery(final Class<T> clazz, final String name, final Object... params) {
274 Collection<T> rval = null;
275
276 EntityManager em = getEm();
277 EntityTransaction tx = em.getTransaction();
278 tx.begin();
279
280 try {
281 Query q = getEm().createNamedQuery(name);
282
283 for (int i = 0; i < params.length; i++) {
284 q.setParameter(i + 1, params[i]);
285 }
286
287 rval = q.getResultList();
288 tx.commit();
289 } catch (Exception e) {
290 handleError(tx, "FindByNamedQuery Error", e);
291 }
292
293 return rval;
294 }
295
296 @SuppressWarnings("unchecked")
297 public <T> T findSingleByNamedQuery(final Class<T> clazz, final String name, final Object... params) {
298 T rval = null;
299
300 EntityManager em = getEm();
301 EntityTransaction tx = em.getTransaction();
302 tx.begin();
303
304 try {
305 Query q = getEm().createNamedQuery(name);
306
307 for (int i = 0; i < params.length; i++) {
308 q.setParameter(i + 1, params[i]);
309 }
310
311 rval = (T) q.getSingleResult();
312
313 tx.commit();
314 } catch (EntityNotFoundException e) {
315 rollbackTransaction(tx);
316 closeEm();
317 } catch (NoResultException e) {
318 rollbackTransaction(tx);
319 closeEm();
320 } catch (NonUniqueResultException e) {
321 handleError(tx, "FindSingleByNamedQuery Error", e);
322 } catch (Exception e) {
323 handleError(tx, "FindSingleByNamedQuery Error", e);
324 }
325
326 return rval;
327 }
328
329 @SuppressWarnings("unchecked")
330 public <T> Collection<T> findByTextSearch(final Class<T> clazz, final String query, final String... fields) {
331 Collection<T> rval = null;
332
333 EntityManager em = getEm();
334 FullTextEntityManager fullTextEntityManager = org.hibernate.search.jpa.Search.getFullTextEntityManager(em);
335 EntityTransaction tx = em.getTransaction();
336 tx.begin();
337
338 try {
339 // create native Lucene query
340 MultiFieldQueryParser parser = new MultiFieldQueryParser(fields, new StandardAnalyzer());
341 org.apache.lucene.search.Query luceneQuery = parser.parse(query);
342
343 // wrap Lucene query in a javax.persistence.Query
344 javax.persistence.Query persistenceQuery = fullTextEntityManager.createFullTextQuery(luceneQuery, clazz);
345
346 // execute search
347 rval = persistenceQuery.getResultList();
348
349 tx.commit();
350 } catch (Exception e) {
351 handleError(tx, "FindByTextSearch Error", e);
352 }
353
354 return rval;
355 }
356 }