001package springdao.support;
002
003import java.io.Serializable;
004import java.lang.reflect.InvocationTargetException;
005import java.lang.reflect.Method;
006import java.util.ArrayList;
007import java.util.Collection;
008import java.util.List;
009import java.util.Map;
010import java.util.concurrent.atomic.AtomicBoolean;
011import javax.persistence.EntityManager;
012import javax.persistence.EntityManagerFactory;
013import javax.persistence.LockModeType;
014import javax.persistence.PersistenceContext;
015import javax.persistence.PersistenceException;
016import javax.persistence.PersistenceUnit;
017import javax.persistence.PersistenceUnitUtil;
018import javax.persistence.Query;
019import org.apache.logging.log4j.Logger;
020import org.apache.logging.log4j.LogManager;
021import org.springframework.dao.support.DaoSupport;
022import org.springframework.orm.jpa.EntityManagerFactoryUtils;
023import org.springframework.util.ClassUtils;
024import org.springframework.util.ReflectionUtils;
025import springdao.DaoRepository;
026
027/**
028 *
029 * @author Kent Yeh
030 * @param <E>
031 */
032public abstract class AbstractSpringDao<E> extends DaoSupport implements DaoRepository<E> {
033
034    private static final Logger log = LogManager.getLogger(AbstractSpringDao.class);
035    private EntityManagerFactory emf;
036    private EntityManager em;
037
038    @Override
039    protected void checkDaoConfig() throws IllegalArgumentException {
040        if (this.emf == null) {
041            throw new IllegalArgumentException("'EntityManagerFactory' is required");
042        }
043        if (this.em == null) {
044            throw new IllegalArgumentException("'EntityManager' is required");
045        }
046    }
047
048    public EntityManagerFactory getEntityManagerFactory() {
049        return emf;
050    }
051
052    @PersistenceUnit
053    public void setEntityManagerFactory(EntityManagerFactory emf) {
054        this.emf = emf;
055    }
056
057    public EntityManager getEntityManager() {
058        return em;
059    }
060
061    @PersistenceContext
062    public void setEntityManager(EntityManager em) {
063        this.em = em;
064    }
065
066    LockModeType getLockMode(String lockMode) {
067        try {
068            return LockModeType.valueOf(lockMode);
069        } catch (RuntimeException ex) {
070            return LockModeType.NONE;
071        }
072    }
073
074    @Override
075    public E instanate() throws InstantiationException, IllegalAccessException {
076        try {
077            return getClazz().newInstance();
078        } catch (InstantiationException | IllegalAccessException ex) {
079            log.error("Instanate error", ex);
080            throw ex;
081        }
082    }
083
084    protected RuntimeException convertException(Exception e) {
085        if (e instanceof RuntimeException) {
086            RuntimeException res = EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible((RuntimeException) e);
087            return res == null ? (RuntimeException) e : res;
088        } else {
089            return new RuntimeException(e.getMessage(), e);
090        }
091    }
092
093    @Override
094    public void clear() {
095        try {
096            em.clear();
097        } catch (RuntimeException e) {
098            throw convertException(e);
099        }
100    }
101
102    @Override
103    public boolean contains(Object entity) {
104        try {
105            return em.contains(entity);
106        } catch (RuntimeException e) {
107            throw convertException(e);
108        }
109    }
110
111    @Override
112    public E findByPrimaryKey(Serializable primaryKey) {
113        try {
114            return em.find(getClazz(), primaryKey);
115        } catch (RuntimeException e) {
116            throw convertException(e);
117        }
118    }
119
120    @Override
121    public E findByPrimaryKey(Serializable primaryKey, String lockMode) {
122        try {
123            return em.find(getClazz(), primaryKey, getLockMode(lockMode));
124        } catch (RuntimeException e) {
125            throw convertException(e);
126        }
127    }
128
129    @Override
130    public E findByPrimaryKey(Serializable primaryKey, Map<String, Object> properties) {
131        try {
132            return em.find(getClazz(), primaryKey, properties);
133        } catch (RuntimeException e) {
134            throw convertException(e);
135        }
136    }
137
138    @Override
139    public E findByPrimaryKey(Serializable primaryKey, String lockMode, Map<String, Object> properties) {
140        try {
141            return em.find(getClazz(), primaryKey, getLockMode(lockMode), properties);
142        } catch (RuntimeException e) {
143            throw convertException(e);
144        }
145    }
146
147    @Override
148    public E save(E entity) {
149        return persist(entity);
150    }
151
152    @Override
153    public Collection<E> save(Collection<E> entities) {
154        try {
155            ArrayList<E> result = new ArrayList<>(entities.size());
156            for (E entity : entities) {
157                result.add(persist(entity));
158            }
159            em.flush();
160            return result;
161        } catch (RuntimeException e) {
162            throw convertException(e);
163        }
164    }
165
166    @Override
167    public E persist(E entity) {
168        try {
169            em.persist(entity);
170            em.flush();
171            return entity;
172        } catch (RuntimeException e) {
173            throw convertException(e);
174        }
175    }
176
177    @Override
178    public E update(E entity) {
179        return merge(entity);
180    }
181    
182    @Override
183    public Collection<E> update(Collection<E> entities) {
184        return merge(entities);
185    }
186
187    @Override
188    public E merge(E entity) {
189        try {
190            E result = em.merge(entity);
191            em.flush();
192            return result;
193        } catch (RuntimeException e) {
194            throw convertException(e);
195        }
196    }
197
198    @Override
199    public Collection<E> merge(Collection<E> entities) {
200        try {
201            ArrayList<E> result = new ArrayList<>(entities.size());
202            for (E entity : entities) {
203                result.add(em.merge(entity));
204            }
205            em.flush();
206            return result;
207        } catch (RuntimeException e) {
208            throw convertException(e);
209        }
210    }
211
212    /**
213     * Before using this method, look at
214     * <a
215     * href="http://blog.xebia.com/2009/03/23/jpa-implementation-patterns-saving-detached-entities/">saveOrUpdate
216     * vs. merge</a>.
217     *
218     * @param entity
219     * @return merged entity
220     */
221    @Override
222    public E saveOrUpdate(E entity) {
223        return contains(entity) ? merge(entity) : emf.getPersistenceUnitUtil().getIdentifier(entity) == null ? persist(entity) : merge(entity);
224    }
225
226    @Override
227    public Collection<E> saveOrUpdate(Collection<E> entities) {
228        try {
229            ArrayList<E> result = new ArrayList<>(entities.size());
230            for (E entity : entities) {
231                result.add(saveOrUpdate(entity));
232            }
233            em.flush();
234            return result;
235        } catch (RuntimeException e) {
236            throw convertException(e);
237        }
238    }
239
240    @Override
241    public void delete(Serializable primaryKey) {
242        try {
243            Object entity = em.find(getClazz(), primaryKey);
244            if (entity != null) {
245                em.remove(entity);
246                em.flush();
247            }
248        } catch (RuntimeException e) {
249            throw convertException(e);
250        }
251    }
252
253    @Override
254    public void delete(Serializable primaryKey, String lockMode) {
255        try {
256            Object entity = em.find(getClazz(), primaryKey,getLockMode(lockMode));
257            if (entity != null) {
258                em.remove(entity);
259                em.flush();
260            }
261        } catch (RuntimeException e) {
262            throw convertException(e);
263        }
264    }
265
266    @Override
267    public void delete(Collection<? extends Serializable> primaryKeys) {
268        try {
269            for (Serializable pk : primaryKeys) {
270                Object entity = em.find(getClazz(), pk);
271                if (entity != null) {
272                    em.remove(entity);
273                }
274            }
275            em.flush();
276        } catch (RuntimeException e) {
277            throw convertException(e);
278        }
279    }
280
281    @Override
282    public E remove(E entity) {
283        entity = merge(entity);
284        try {
285            em.remove(entity);
286            em.flush();
287            return entity;
288        } catch (RuntimeException e) {
289            throw convertException(e);
290        }
291    }
292
293    @Override
294    public E remove(E entity, String lockMode) {
295        Object pk = emf.getPersistenceUnitUtil().getIdentifier(entity);
296        if (pk == null) {
297            return null;
298        } else {
299            entity = em.find(getClazz(), pk, getLockMode(lockMode));
300            if (entity == null) {
301                return null;
302            } else {
303                em.remove(entity);
304                em.flush();
305                return entity;
306            }
307        }
308    }
309
310    @Override
311    public Collection<E> remove(Collection<E> entities) {
312        try {
313            ArrayList<E> result = new ArrayList<>(entities.size());
314            for (E entity : entities) {
315                entity = merge(entity);
316                em.remove(entity);
317                result.add(entity);
318            }
319            em.flush();
320            return result;
321        } catch (RuntimeException e) {
322            throw convertException(e);
323        }
324    }
325
326    @Override
327    public E lock(E entity, String lockMode) {
328        try {
329            em.lock(entity, getLockMode(lockMode));
330            return entity;
331        } catch (RuntimeException e) {
332            throw convertException(e);
333        }
334    }
335
336    @Override
337    public E refresh(E entity) {
338        try {
339            if(em.contains(entity))
340                em.refresh(entity);
341            else{
342                Object pk = emf.getPersistenceUnitUtil().getIdentifier(entity);
343                return em.find(getClazz(), pk);
344            }
345            return entity;
346        } catch (RuntimeException e) {
347            throw convertException(e);
348        }
349    }
350
351    @Override
352    public E refresh(E entity, String lockMode) {
353        try {
354            if (em.contains(entity)) {
355                em.refresh(entity, getLockMode(lockMode));
356            } else {
357                Object pk = emf.getPersistenceUnitUtil().getIdentifier(entity);
358                return em.find(getClazz(), pk, getLockMode(lockMode));
359            }
360            return entity;
361        } catch (RuntimeException e) {
362            throw convertException(e);
363        }
364    }
365    
366    @Override
367    public int sqlUpdate(String sql) {
368        try {
369            return em.createNativeQuery(sql).executeUpdate();
370        } catch (RuntimeException e) {
371            throw convertException(e);
372        }
373    }
374
375    @Override
376    public int sqlUpdate(String sql, Object... parameters) {
377        try {
378            int i = 0;
379            Query query = em.createNativeQuery(sql);
380            if (parameters != null && parameters.length > 0) {
381                for (Object paramter : parameters) {
382                    query.setParameter(++i, paramter);
383                }
384            }
385            return query.executeUpdate();
386        } catch (RuntimeException e) {
387            throw convertException(e);
388        }
389    }
390
391    @Override
392    public int sqlUpdate(String sql, Map<String, ?> parameters) {
393        try {
394            int i = 0;
395            Query query = em.createNativeQuery(sql);
396            if (parameters != null && !parameters.isEmpty()) {
397                for(Map.Entry<String,?> entry:parameters.entrySet()){
398                    query.setParameter(entry.getKey(), entry.getValue());
399                }
400            }
401            return query.executeUpdate();
402        } catch (RuntimeException e) {
403            throw convertException(e);
404        }
405    }
406    
407    @Override
408    public List<Integer> sqlUpdate(List<String> sqls) {
409        try {
410            List<Integer> res = new ArrayList<>(sqls.size());
411            for (String sql : sqls){
412                res.add(em.createNativeQuery(sql).executeUpdate());
413            }
414            return res;
415        } catch (RuntimeException e) {
416            throw convertException(e);
417        }
418    }
419
420    @Override
421    public int bulkUpdate(String QL) {
422        try {
423            return em.createQuery(QL).executeUpdate();
424        } catch (RuntimeException e) {
425            throw convertException(e);
426        }
427    }
428
429    @Override
430    public List<Integer> bulkUpdate(List<String> QLs) {
431        try {
432            List<Integer> result = new ArrayList<>();
433            for (String ql : QLs) {
434                result.add(em.createQuery(ql).executeUpdate());
435            }
436            return result;
437        } catch (RuntimeException e) {
438            throw convertException(e);
439        }
440    }
441
442    @Override
443    public int bulkUpdate(String QL, Object... parameters) {
444        try {
445            Query query = em.createQuery(QL);
446            int i = 0;
447            if (parameters != null && parameters.length > 0) {
448                for (Object paramter : parameters) {
449                    query.setParameter(++i, paramter);
450                }
451            }
452            return query.executeUpdate();
453        } catch (RuntimeException e) {
454            throw convertException(e);
455        }
456    }
457
458    @Override
459    public int bulkUpdate(String QL, Map<String, ?> parameters) {
460        try {
461            Query query = em.createQuery(QL);
462            int i = 0;
463            if (parameters != null && !parameters.isEmpty()) {
464                for(Map.Entry<String,?> entry:parameters.entrySet()){
465                    query.setParameter(entry.getKey(), entry.getValue());
466                }
467            }
468            return query.executeUpdate();
469        } catch (RuntimeException e) {
470            throw convertException(e);
471        }
472    }
473
474    @Override
475    public String $e() {
476        return getEntityName();
477    }
478
479    @Override
480    public String getEntityName() {
481        return getClazz().getName();
482    }
483
484    @Override
485    public String $a() {
486        return getAliasName();
487    }
488
489    @Override
490    public String $ea() {
491        return getEntityName() + " AS " + getAliasName();
492    }
493
494    @Override
495    public String getAliasName() {
496        return AliasHelper.$a(getClazz());
497    }
498
499    protected List<E> findList(Query query) {
500        try {
501            EntityManagerFactoryUtils.applyTransactionTimeout(query, getEntityManagerFactory());
502            return query.getResultList();
503        } catch (RuntimeException e) {
504            throw convertException(e);
505        }
506    }
507
508    protected List<E> findList(Query query, Object... parameters) {
509        try {
510            EntityManagerFactoryUtils.applyTransactionTimeout(query, getEntityManagerFactory());
511            if (parameters != null && parameters.length > 0) {
512                for (int i = 0; i < parameters.length; i++) {
513                    query.setParameter(i + 1, parameters[i]);
514                }
515            }
516            return query.getResultList();
517        } catch (RuntimeException e) {
518            throw convertException(e);
519        }
520    }
521
522    protected List<E> findList(Query query, Map<String, ?> parameters) {
523        try {
524            EntityManagerFactoryUtils.applyTransactionTimeout(query, getEntityManagerFactory());
525            if (parameters != null && !parameters.isEmpty()) {
526                for(Map.Entry<String,?> entry:parameters.entrySet()){
527                    query.setParameter(entry.getKey(), entry.getValue());
528                }
529            }
530            return query.getResultList();
531        } catch (RuntimeException e) {
532            throw convertException(e);
533        }
534    }
535
536    @Override
537    public List<E> findByCriteria(String qlCriteria) {
538        return findList(em.createQuery(JpqlHelper.get().select($a()).
539                from($ea()).$(qlCriteria).ql()));
540    }
541
542    @Override
543    public List<E> findByCriteria(String qlCriteria, Object... parameters) {
544        return findList(em.createQuery(JpqlHelper.get().select($a()).
545                from($ea()).$(qlCriteria).ql()), parameters);
546    }
547
548    @Override
549    public List<E> findByCriteria(String qlCriteria, Map<String, ?> parameters) {
550        return findList(em.createQuery(JpqlHelper.get().select($a()).from($ea())
551                .$(qlCriteria).ql()), parameters);
552    }
553
554    @Override
555    public List<E> findByCriteria(String qlCriteria, int startPageNo, int pageSize, Object... parameters) {
556        if ((startPageNo < 1) || (pageSize < 1)) {
557            return parameters == null || parameters.length == 0 ? findByCriteria(qlCriteria) : findByCriteria(qlCriteria, parameters);
558        } else if (parameters == null || parameters.length == 0) {
559            return findByCriteria(qlCriteria, startPageNo, pageSize);
560        } else {
561            return findList(em.createQuery(JpqlHelper.get().select($a()).from($ea()).$(qlCriteria).ql()).
562                    setFirstResult((startPageNo - 1) * pageSize).setMaxResults(pageSize), parameters);
563        }
564    }
565
566    @Override
567    public List<E> findByCriteria(String qlCriteria, int startPageNo, int pageSize, Map<String, ?> parameters) {
568        if ((startPageNo < 1) || (pageSize < 1)) {
569            return parameters == null || parameters.isEmpty() ? findByCriteria(qlCriteria) : findByCriteria(qlCriteria, parameters);
570        } else if (parameters == null || parameters.isEmpty()) {
571            return findByCriteria(qlCriteria, startPageNo, pageSize);
572        } else {
573            return findList(em.createQuery(JpqlHelper.get().select($a()).from($ea()).$(qlCriteria).ql())
574                    .setFirstResult((startPageNo - 1) * pageSize).setMaxResults(pageSize), parameters);
575        }
576    }
577
578    @Override
579    public List<E> findByCriteria(String qlCriteria, int startPageNo, int pageSize) {
580        if ((startPageNo < 1) || (pageSize < 1)) {
581            return findByCriteria(qlCriteria);
582        } else {
583            return findList(em.createQuery(JpqlHelper.get().select($a()).from($ea()).$(qlCriteria).ql()).setFirstResult((startPageNo - 1) * pageSize).setMaxResults(pageSize));
584        }
585    }
586
587    @Override
588    public List<E> findBySQLQuery(String sql) {
589        return findList(em.createNativeQuery(sql, getClazz()));
590    }
591
592    @Override
593    public List<E> findBySQLQuery(String sql, Object... parameters) {
594        return findList(em.createNativeQuery(sql, getClazz()), parameters);
595    }
596
597    @Override
598    public List<E> findBySQLQuery(String sql, Map<String, ?> parameters) {
599        return findList(em.createNativeQuery(sql, getClazz()), parameters);
600    }
601
602    @Override
603    public <T> T findUniqueByQL(String QL) {
604        try {
605            Query query = em.createQuery(QL);
606            EntityManagerFactoryUtils.applyTransactionTimeout(query, getEntityManagerFactory());
607            return (T) query.getSingleResult();
608        } catch (RuntimeException e) {
609            throw convertException(e);
610        }
611    }
612
613    @Override
614    public <T> T findUniqueByQL(String QL, Object... parameters) {
615        try {
616            Query query = em.createQuery(QL);
617            EntityManagerFactoryUtils.applyTransactionTimeout(query, getEntityManagerFactory());
618            if (parameters != null && parameters.length > 0) {
619                for (int i = 0; i < parameters.length; i++) {
620                    query.setParameter(i + 1, parameters[i]);
621                }
622            }
623            return (T) query.getSingleResult();
624        } catch (RuntimeException e) {
625            throw convertException(e);
626        }
627    }
628
629    @Override
630    public <T> T findUniqueByQL(String QL, Map<String, ?> parameters) {
631        try {
632            Query query = em.createQuery(QL);
633            EntityManagerFactoryUtils.applyTransactionTimeout(query, getEntityManagerFactory());
634            if (parameters != null && !parameters.isEmpty()) {
635                for(Map.Entry<String,?> entry:parameters.entrySet()){
636                    query.setParameter(entry.getKey(), entry.getValue());
637                }
638            }
639            return (T) query.getSingleResult();
640        } catch (RuntimeException e) {
641            throw convertException(e);
642        }
643    }
644
645    @Override
646    public <T> List<T> findListByQL(String QL) {
647        try {
648            Query query = em.createQuery(QL);
649            EntityManagerFactoryUtils.applyTransactionTimeout(query, getEntityManagerFactory());
650            return query.getResultList();
651        } catch (RuntimeException e) {
652            log.error(e.getMessage(), e);
653            throw convertException(e);
654        }
655    }
656
657    @Override
658    public <T> List<T> findListByQL(String QL, Object... parameters) {
659        try {
660            Query query = em.createQuery(QL);
661            EntityManagerFactoryUtils.applyTransactionTimeout(query, getEntityManagerFactory());
662            if (parameters != null && parameters.length > 0) {
663                for (int i = 0; i < parameters.length; i++) {
664                    query.setParameter(i + 1, parameters[i]);
665                }
666            }
667            return query.getResultList();
668        } catch (RuntimeException e) {
669            throw convertException(e);
670        }
671    }
672
673    @Override
674    public <T> List<T> findListByQL(String QL, Map<String, ?> parameters) {
675        try {
676            Query query = em.createQuery(QL);
677            EntityManagerFactoryUtils.applyTransactionTimeout(query, getEntityManagerFactory());
678            if (parameters != null && !parameters.isEmpty()) {
679                for(Map.Entry<String,?> entry:parameters.entrySet()){
680                    query.setParameter(entry.getKey(), entry.getValue());
681                }
682            }
683            return query.getResultList();
684        } catch (RuntimeException e) {
685            throw convertException(e);
686        }
687    }
688
689    @Override
690    public List<E> findByNamedQuery(String name) {
691        return findList(em.createNamedQuery(name, getClazz()));
692    }
693
694    @Override
695    public List<E> findByNamedQuery(String name, Object... parameters) {
696        return findList(em.createNamedQuery(name, getClazz()), parameters);
697    }
698
699    @Override
700    public List<E> findByNamedQuery(String name, Map<String, ?> parameters) {
701        return findList(em.createNamedQuery(name, getClazz()), parameters);
702    }
703
704    @Override
705    public <T> List<T> findListByNamedQuery(String name) {
706        try {
707            Query query = em.createNamedQuery(name);
708            EntityManagerFactoryUtils.applyTransactionTimeout(query, getEntityManagerFactory());
709            return query.getResultList();
710        } catch (RuntimeException e) {
711            throw convertException(e);
712        }
713    }
714
715    @Override
716    public <T> List<T> findListByNamedQuery(String name, Object... parameters) {
717        try {
718            Query query = em.createNamedQuery(name);
719            EntityManagerFactoryUtils.applyTransactionTimeout(query, getEntityManagerFactory());
720            if (parameters != null && parameters.length > 0) {
721                for (int i = 0; i < parameters.length; i++) {
722                    query.setParameter(i + 1, parameters[i]);
723                }
724            }
725            return query.getResultList();
726        } catch (RuntimeException e) {
727            throw convertException(e);
728        }
729    }
730
731    @Override
732    public <T> List<T> findListByNamedQuery(String name, Map<String, ?> parameters) {
733        try {
734            Query query = em.createNamedQuery(name);
735            EntityManagerFactoryUtils.applyTransactionTimeout(query, getEntityManagerFactory());
736            if (parameters != null && !parameters.isEmpty()) {
737                for(Map.Entry<String,?> entry:parameters.entrySet()){
738                    query.setParameter(entry.getKey(), entry.getValue());
739                }
740            }
741            return query.getResultList();
742        } catch (RuntimeException e) {
743            throw convertException(e);
744        }
745    }
746
747    @Override
748    public E initLazyCollection(E entity, final String collectionFieldName) {
749        final AtomicBoolean found = new AtomicBoolean(false);
750        final String methodName = collectionFieldName.matches("^[a-z][A-Z]") ? collectionFieldName : collectionFieldName.length() > 1
751                ? collectionFieldName.substring(0, 1).toUpperCase() + collectionFieldName.substring(1) : collectionFieldName.toUpperCase();
752        final Object obj = entity;
753        final EntityManager fem = em;
754        try {
755            ReflectionUtils.doWithMethods(getClazz(),
756                    new ReflectionUtils.MethodCallback() {
757                        @Override
758                        public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
759                            try {
760                                Method setter = obj.getClass().getMethod("s" + method.getName().substring(1), method.getReturnType());
761                                PersistenceUnitUtil puu = fem.getEntityManagerFactory().getPersistenceUnitUtil();
762                                if (!puu.isLoaded(obj, collectionFieldName)) {
763                                    E reattach = (E) fem.merge(obj);
764//                                    E reattach = (E) em.find(obj.getClass(), puu.getIdentifier(obj));
765                                    Object fieldObj = method.invoke(reattach, new Object[]{});
766                                    ((Collection) fieldObj).size();
767                                    setter.invoke(obj, fieldObj);
768                                }
769                            } catch (NoSuchMethodException ex) {
770                                throw new PersistenceException("Setter " + getClazz().getSimpleName() + ".set" + methodName + "(...) not found.", ex);
771                            } catch (InvocationTargetException ex) {
772                                throw new PersistenceException("Could not fetch Collection from " + getClazz().getSimpleName() + "." + method.getName(), ex);
773                            }
774                        }
775                    },
776                    new ReflectionUtils.MethodFilter() {
777                        @Override
778                        public boolean matches(Method method) {
779                            if (found.get()) {
780                                return false;
781                            } else {
782                                found.set(method.getName().equals("get" + methodName) && method.getParameterTypes().length == 0
783                                        && ClassUtils.isAssignable(Collection.class, method.getReturnType()));
784                                return found.get();
785                            }
786                        }
787                    });
788            return (E) obj;
789        } catch (IllegalArgumentException e) {
790            throw convertException(e);
791        } finally {
792            EntityManagerFactoryUtils.closeEntityManager(fem);
793        }
794    }
795}