org.azrul.langkuik.dao.HibernateGenericDAO.java Source code

Java tutorial

Introduction

Here is the source code for org.azrul.langkuik.dao.HibernateGenericDAO.java

Source

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package org.azrul.langkuik.dao;

import com.fasterxml.uuid.EthernetAddress;
import com.fasterxml.uuid.Generators;
import com.fasterxml.uuid.impl.TimeBasedGenerator;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.Order;
import javax.persistence.criteria.Root;
import org.hibernate.Session;
import org.hibernate.metadata.ClassMetadata;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import javax.persistence.Query;
import javax.persistence.metamodel.EntityType;
import javax.persistence.metamodel.Metamodel;
import javax.persistence.metamodel.SingularAttribute;
import org.apache.lucene.search.Sort;
import org.apache.lucene.search.SortField;
import org.azrul.langkuik.framework.relationship.RelationManager;
import org.hibernate.CacheMode;
import org.hibernate.search.annotations.Analyze;
import org.hibernate.search.annotations.Index;
import org.hibernate.search.annotations.Indexed;
import org.hibernate.search.jpa.FullTextEntityManager;
import org.hibernate.search.jpa.FullTextQuery;
import org.hibernate.search.jpa.Search;
import org.hibernate.search.query.dsl.BooleanJunction;
import org.hibernate.search.query.dsl.QueryBuilder;

/**
 *
 * @author azrulm
 * @param <T>
 */
public class HibernateGenericDAO<T> implements DataAccessObject<T>, Serializable {

    private EntityManagerFactory emf;
    private Class<T> classOfEntity;
    //    private static final ThreadLocal<EntityManager> threadLocal;
    //
    //    static {
    //        threadLocal = new ThreadLocal<EntityManager>();
    //    }

    public HibernateGenericDAO() {
    }

    public HibernateGenericDAO(EntityManagerFactory emf, Class<T> daoClass) {
        this.emf = emf;
        this.classOfEntity = daoClass;
    }

    @Override
    public Class<T> getType() {
        return classOfEntity;
    }

    public static void massIndexDatabaseForSearch(EntityManagerFactory emf) {
        try {
            EntityManager em = emf.createEntityManager();
            List<Class> classes = new ArrayList<>();
            for (EntityType e : emf.getMetamodel().getEntities()) {
                Class c = e.getJavaType();
                if (c.isAnnotationPresent(Index.class)) {
                    classes.add(c);
                }
            }

            FullTextEntityManager fullTextEntityManager = org.hibernate.search.jpa.Search
                    .getFullTextEntityManager(em);
            fullTextEntityManager.createIndexer(classes.toArray(new Class[] {})).batchSizeToLoadObjects(30)
                    .optimizeAfterPurge(true).optimizeOnFinish(true).threadsToLoadObjects(4)
                    .cacheMode(CacheMode.NORMAL) // defaults to CacheMode.IGNORE
                    .startAndWait();
        } catch (InterruptedException ex) {
            Logger.getLogger(HibernateGenericDAO.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public T find(Object id) {
        T bean = null;
        EntityManager em = emf.createEntityManager();
        bean = em.find(classOfEntity, id);
        em.close();
        return bean;
    }

    //no persistence
    private T createNew(Class<T> clazz) {
        try {
            T bean = clazz.getConstructor().newInstance(new Object[] {});
            return bean;
        } catch (NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException
                | IllegalArgumentException | InvocationTargetException ex) {
            Logger.getLogger(HibernateGenericDAO.class.getName()).log(Level.SEVERE, null, ex);
        }
        return null;
    }

    //no persistence
    public T createNew() {
        return createNew(classOfEntity);
    }

    //no persistence
    public T createNew(boolean giveId) {
        T bean = createNew(classOfEntity);
        if (giveId == true) {
            try {
                EthernetAddress addr = EthernetAddress.fromInterface();
                TimeBasedGenerator uuidGenerator = Generators.timeBasedGenerator(addr);
                UUID uuid = uuidGenerator.generate();
                Field field = bean.getClass().getDeclaredField(getIdentifierFieldName());
                field.setAccessible(true);
                field.set(bean, new Long(uuid.clockSequence()));
            } catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException
                    | SecurityException ex) {
                Logger.getLogger(HibernateGenericDAO.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        return bean;
    }

    private <P> Collection<T> getAllDependants(P parentObject, String parentField, String orderBy, boolean asc,
            int startIndex, int offset) {
        EntityManager em = emf.createEntityManager();
        Order order = null;

        CriteriaBuilder cb = em.getCriteriaBuilder();
        javax.persistence.criteria.CriteriaQuery<T> criteria = cb.createQuery(classOfEntity);
        Root parent = criteria.from(parentObject.getClass());
        Join join = parent.join(parentField);
        Collection<T> results = new ArrayList<>();

        //order by
        ClassMetadata classMetadata = ((Session) em.getDelegate()).getSessionFactory()
                .getClassMetadata(classOfEntity);
        if (orderBy == null && classMetadata != null) {
            orderBy = classMetadata.getIdentifierPropertyName();
        }
        if (orderBy != null) {
            if (asc) {
                order = cb.asc(join.get(orderBy));
            } else {
                order = cb.desc(join.get(orderBy));
            }
        }

        if (orderBy == null) { //orderBy is still null
            criteria.select(join).where(cb.equal(parent, parentObject));
        } else {
            criteria.select(join).where(cb.equal(parent, parentObject)).orderBy(order);
        }
        results = em.createQuery(criteria).setFirstResult(startIndex).setMaxResults(offset).getResultList();

        em.close();
        return results;
    }

    private <P> int countDependants(P parentObject, String parentField) {
        EntityManager em = emf.createEntityManager();
        int count = -1;
        try {
            //            em.getTransaction().begin();
            //            CriteriaBuilder builder = em.getCriteriaBuilder();
            //            javax.persistence.criteria.CriteriaQuery<T> criteria = builder.createQuery(daoClass);
            //            Root parent = criteria.from(parentObject.getClass());
            //            criteria.select(parent.get(parentField)).where(parent.in(parentObject));
            //            T result = em.createQuery(criteria).setFirstResult(1).setMaxResults(1).getSingleResult();
            //            if (result != null) {
            if (!(parentObject.getClass().getDeclaredField(parentField)
                    .getGenericType() instanceof ParameterizedType)) {
                return 1;
            }
            Class classOfField = (Class) ((ParameterizedType) parentObject.getClass().getDeclaredField(parentField)
                    .getGenericType()).getActualTypeArguments()[0];

            ClassMetadata classMetadata = ((Session) em.getDelegate()).getSessionFactory()
                    .getClassMetadata(classOfField);
            String childId = classMetadata.getIdentifierPropertyName();

            CriteriaBuilder countBuilder = em.getCriteriaBuilder();
            javax.persistence.criteria.CriteriaQuery<Long> countCriteria = countBuilder.createQuery(Long.class);
            Root parentForCounting = countCriteria.from(parentObject.getClass());
            countCriteria.select(countBuilder.count(parentForCounting.join(parentField).get(childId)))
                    .where(parentForCounting.in(parentObject));

            Long r = em.createQuery(countCriteria).getSingleResult();
            if (r != null) {
                count = r.intValue();
            } else {
                count = 0;
            }
            em.close();

        } catch (Exception ex) {
            em.close();
            Logger.getLogger(HibernateGenericDAO.class.getName()).log(Level.SEVERE, null, ex);
        }
        return count;
    }

    public <P> int count(P parentObject, String parentToCurrentField) {
        return countDependants(parentObject, parentToCurrentField);
    }

    public <P> Collection<T> find(P parentObject, String parentToCurrentField, String orderBy, boolean asc,
            int startIndex, int offset) {
        Collection<T> list = (Collection<T>) getAllDependants(parentObject, parentToCurrentField, orderBy, asc,
                startIndex, offset);
        return list;
    }

    @Override
    public void delete(T entity) {
        EntityManager em = emf.createEntityManager();
        FullTextEntityManager ftem = Search.getFullTextEntityManager(em);
        try {
            em.getTransaction().begin();
            boolean isIndexed = classOfEntity.isAnnotationPresent(Indexed.class);

            em.remove(refresh(em, entity));
            if (isIndexed) {
                ftem.purge(classOfEntity, (Serializable) getIdentifierValue(entity));
            }

            em.flush();
            em.getTransaction().commit();
        } catch (Exception e) {
            em.getTransaction().rollback();
            Logger.getLogger(HibernateGenericDAO.class.getName()).log(Level.SEVERE, null, e);
        }
        em.close();
    }

    @Override
    public void delete(Collection<T> entities) {
        EntityManager em = emf.createEntityManager();
        FullTextEntityManager ftem = Search.getFullTextEntityManager(em);
        try {
            em.getTransaction().begin();
            boolean isIndexed = classOfEntity.isAnnotationPresent(Indexed.class);
            for (T entity : entities) {
                em.remove(em.merge(entity));
                if (isIndexed) {
                    ftem.purge(classOfEntity, (Serializable) getIdentifierValue(entity));
                }
            }
            em.flush();
            em.getTransaction().commit();
        } catch (Exception e) {
            em.getTransaction().rollback();
            Logger.getLogger(HibernateGenericDAO.class.getName()).log(Level.SEVERE, null, e);
        }
        em.close();
    }

    @Override
    public T save(T newObject) {
        EntityManager em = emf.createEntityManager();
        FullTextEntityManager ftem = Search.getFullTextEntityManager(em);
        T savedObject = null;
        try {
            em.getTransaction().begin();
            savedObject = em.merge(newObject);
            em.flush();
            if (savedObject.getClass().isAnnotationPresent(Indexed.class)) {
                ftem.index(savedObject);
            }
            em.getTransaction().commit();
            return savedObject;
        } catch (Exception e) {
            Logger.getLogger(HibernateGenericDAO.class.getName()).log(Level.SEVERE, null, e);
            em.getTransaction().rollback();
        } finally {

            em.close();
        }
        return null;
    }

    @Override
    public <P> T createAndSave(DaoParameter<P, T> parameter) {
        if (parameter instanceof FindEntityCollectionParameter) {
            FindEntityCollectionParameter<P, T> feParam = (FindEntityCollectionParameter<P, T>) parameter;
            return createAndSave(feParam.getParentObject(), feParam.getParentToCurrentField(),
                    feParam.getRelationManager());
        } else {
            return createAndSave();
        }
    }

    @Override
    public <P> T createAndSave(P parentBean, String parentToCurrentField, RelationManager<P, T> relationManager) {
        T newBean = createNew();
        EntityManager em = emf.createEntityManager();
        FullTextEntityManager ftem = Search.getFullTextEntityManager(em);
        try {

            em.getTransaction().begin();
            ;
            T newBeanFromDB = em.merge(newBean);
            P parentBeanFromDB = refresh(em, parentBean);

            if (relationManager != null) {
                relationManager.link(parentBeanFromDB, newBeanFromDB, parentToCurrentField, em);
            }
            em.merge(parentBeanFromDB);
            em.merge(newBeanFromDB);
            em.flush(); // must flush before index
            if (newBeanFromDB.getClass().isAnnotationPresent(Indexed.class)) {
                ftem.index(newBeanFromDB);
            }
            if (parentBeanFromDB.getClass().isAnnotationPresent(Indexed.class)) {
                if (relationManager != null) {
                    ftem.index(parentBeanFromDB);
                }
            }
            em.getTransaction().commit();
            return newBeanFromDB;
        } catch (Exception e) {
            Logger.getLogger(HibernateGenericDAO.class.getName()).log(Level.SEVERE, null, e);
            em.getTransaction().rollback();
        } finally {
            em.close();
        }
        return null;
    }

    @Override
    public Object createAndSave(Class c) {
        EntityManager em = emf.createEntityManager();
        try {
            Object bean = createNew(c);//call default constructor
            em.getTransaction().begin();
            em.persist(bean);
            em.flush();
            if (bean.getClass().isAnnotationPresent(Indexed.class)) {
                FullTextEntityManager ftem = Search.getFullTextEntityManager(em);
                ftem.index(bean);
            }
            em.getTransaction().commit();
            return bean;
        } catch (Exception e) {
            Logger.getLogger(HibernateGenericDAO.class.getName()).log(Level.SEVERE, null, e);
            em.getTransaction().rollback();
        } finally {
            em.close();
        }
        return null;
    }

    @Override
    public T createAndSave() {
        T newObject = createNew();
        EntityManager em = emf.createEntityManager();
        FullTextEntityManager ftem = Search.getFullTextEntityManager(em);
        try {
            em.getTransaction().begin();
            em.persist(newObject);
            em.flush();
            if (newObject.getClass().isAnnotationPresent(Indexed.class)) {
                ftem.index(newObject);
            }
            em.getTransaction().commit();
        } catch (Exception e) {
            Logger.getLogger(HibernateGenericDAO.class.getName()).log(Level.SEVERE, null, e);
            em.getTransaction().rollback();
        } finally {
            em.close();
        }
        return newObject;
    }

    private Collection<T> getAll(Class<T> daoClass, String orderBy, boolean asc, int startIndex, int offset) {
        EntityManager em = emf.createEntityManager();

        //        ClassMetadata classMetadata = ((Session) em.getDelegate()).getSessionFactory().getClassMetadata(daoClass);
        //        String childId = classMetadata.getIdentifierPropertyName();
        String childId = this.getIdentifierFieldName();

        CriteriaBuilder cb = em.getCriteriaBuilder();
        javax.persistence.criteria.CriteriaQuery<T> criteria = cb.createQuery(daoClass);

        Root<T> root = criteria.from(daoClass);

        if (orderBy == null) {
            criteria.select(root).orderBy(cb.asc(root.get(childId)));
        } else {
            if (asc == true) {
                criteria.select(root).orderBy(cb.asc(root.get(orderBy)));
            } else {
                criteria.select(root).orderBy(cb.desc(root.get(orderBy)));
            }
        }
        Collection<T> results = em.createQuery(criteria).setFirstResult(startIndex).setMaxResults(offset)
                .getResultList();

        em.close();
        return results;
    }

    private int countAll(Class<T> daoClass) {
        EntityManager em = emf.createEntityManager();

        ClassMetadata classMetadata = ((Session) em.getDelegate()).getSessionFactory().getClassMetadata(daoClass);
        String childId = classMetadata.getIdentifierPropertyName();

        CriteriaBuilder cb = em.getCriteriaBuilder();
        javax.persistence.criteria.CriteriaQuery<Long> criteria = cb.createQuery(Long.TYPE);
        Root<T> root = criteria.from(daoClass);

        criteria.select(cb.count(root.get(childId)));
        Long size = em.createQuery(criteria).getSingleResult();

        em.close();
        return size.intValue();
    }

    @Override
    public int countSearch(Collection<SearchTerm> searchTerms, Class<T> daoClass) {
        if (searchTerms == null || searchTerms.isEmpty()) {
            return countAll(daoClass);
        }
        EntityManager em = emf.createEntityManager();

        FullTextEntityManager fullTextEntityManager = org.hibernate.search.jpa.Search.getFullTextEntityManager(em);

        org.apache.lucene.search.Query luceneQuery = buildSearchQuery(fullTextEntityManager, daoClass, searchTerms);
        FullTextQuery ftQuery = fullTextEntityManager.createFullTextQuery(luceneQuery, daoClass);
        return ftQuery.getResultSize();
    }

    @Override
    public <P> Collection<T> runQuery(DaoParameter<P, T> query, String orderBy, boolean asc, int startIndex,
            int offset) {
        if (query instanceof FindAnyEntityParameter) {
            FindAnyEntityParameter searchQuery = (FindAnyEntityParameter) query;
            return search(searchQuery.getSearchTerms(), searchQuery.getSearchClass(), orderBy, asc, startIndex,
                    offset);
        } else {
            FindEntityCollectionParameter entityQuery = (FindEntityCollectionParameter) query;
            return find(entityQuery.getParentObject(), entityQuery.getParentToCurrentField(), orderBy, asc,
                    startIndex, offset);
        }
    }

    @Override
    public <P> int countQueryResult(DaoParameter<P, T> query) {
        if (query instanceof FindAnyEntityParameter) {
            FindAnyEntityParameter searchQuery = (FindAnyEntityParameter) query;
            return countSearch(searchQuery.getSearchTerms(), searchQuery.getSearchClass());
        } else {
            FindEntityCollectionParameter entityQuery = (FindEntityCollectionParameter) query;
            return count(entityQuery.getParentObject(), entityQuery.getParentToCurrentField());
        }
    }

    public Collection<T> search(Collection<SearchTerm> searchTerms, Class<T> daoClass, int startIndex, int offset) {
        return search(searchTerms, daoClass, null, true, startIndex, offset);
    }

    public Collection<T> search(Collection<SearchTerm> searchTerms, Class<T> daoClass, String orderBy, boolean asc,
            int startIndex, int offset) {
        if (searchTerms == null || searchTerms.isEmpty()) {
            return getAll(daoClass, orderBy, asc, startIndex, offset);
        }
        try {
            EntityManager em = emf.createEntityManager();

            FullTextEntityManager fullTextEntityManager = org.hibernate.search.jpa.Search
                    .getFullTextEntityManager(em);

            //Session session = (Session) em.getDelegate();
            //String currentIdField = session.getSessionFactory().getClassMetadata(daoClass).getIdentifierPropertyName();
            String currentIdField = this.getIdentifierFieldName();
            org.apache.lucene.search.Query luceneQuery = buildSearchQuery(fullTextEntityManager, daoClass,
                    searchTerms);
            Sort sort = Sort.INDEXORDER;
            if (orderBy == null) {
                if (Long.TYPE.equals(daoClass.getDeclaredField(currentIdField).getType())) {
                    sort = new Sort(new SortField(currentIdField, SortField.LONG, asc));
                } else if (Integer.TYPE.equals(daoClass.getDeclaredField(currentIdField).getType())) {
                    sort = new Sort(new SortField(currentIdField, SortField.INT, asc));
                } else if (String.class.equals(daoClass.getDeclaredField(currentIdField).getType())) {
                    sort = new Sort(new SortField(currentIdField, SortField.STRING, asc));
                }
            } else {
                if (Long.TYPE.equals(daoClass.getDeclaredField(orderBy).getType())) {
                    sort = new Sort(new SortField(orderBy, SortField.LONG, asc));
                } else if (Integer.TYPE.equals(daoClass.getDeclaredField(orderBy).getType())) {
                    sort = new Sort(new SortField(orderBy, SortField.INT, asc));
                } else { //all else fail, sort as string
                    sort = new Sort(new SortField(orderBy, SortField.STRING, asc));
                }
            }

            Query jpaQuery = fullTextEntityManager.createFullTextQuery(luceneQuery, daoClass).setSort(sort)
                    .setFirstResult(startIndex).setMaxResults(offset);

            List<T> results = jpaQuery.getResultList();
            fullTextEntityManager.close();

            return results;
        } catch (NoSuchFieldException ex) {
            Logger.getLogger(HibernateGenericDAO.class.getName()).log(Level.SEVERE, null, ex);
        }
        return new ArrayList<T>();
    }

    private org.apache.lucene.search.Query buildSearchQuery(FullTextEntityManager fullTextEntityManager,
            Class<T> daoClass, Collection<SearchTerm> searchTerms) {
        QueryBuilder qb = fullTextEntityManager.getSearchFactory().buildQueryBuilder().forEntity(daoClass).get();
        BooleanJunction bj = qb.bool();
        for (SearchTerm entry : searchTerms) {
            if (entry.getValue() instanceof Date) {
                bj.must(qb.range().onField(entry.getFieldName()).ignoreAnalyzer().below(entry.getValue())
                        .createQuery());
                bj.must(qb.range().onField(entry.getFieldName()).ignoreAnalyzer().above(entry.getValue())
                        .createQuery());
            } else {
                org.hibernate.search.annotations.Field searchFieldAnno = entry.getField()
                        .getAnnotation(org.hibernate.search.annotations.Field.class);
                if (searchFieldAnno.analyze() == Analyze.NO) {
                    bj.must(qb.keyword().onField(entry.getFieldName()).ignoreAnalyzer().matching(entry.getValue())
                            .createQuery());
                } else {
                    bj.must(qb.keyword().fuzzy().onField(entry.getFieldName()).matching(entry.getValue())
                            .createQuery());
                }
            }
        }
        org.apache.lucene.search.Query luceneQuery = bj.createQuery();
        return luceneQuery;
    }

    public <P> Collection<T> searchResultAlreadyInParent(Collection<T> searchResult, Class<T> daoClass,
            P parentObject, String parentToChildrenField) {
        try {
            EntityManager em = emf.createEntityManager();

            Session session = (Session) em.getDelegate();
            String currentIdField = session.getSessionFactory().getClassMetadata(daoClass)
                    .getIdentifierPropertyName();

            Collection searchResultIds = new ArrayList();
            for (T a : searchResult) {
                Field idField = a.getClass().getDeclaredField(currentIdField);
                idField.setAccessible(true);
                searchResultIds.add(idField.get(a));
            }

            CriteriaBuilder cb = em.getCriteriaBuilder();
            javax.persistence.criteria.CriteriaQuery<T> criteria = cb.createQuery(daoClass);
            Root parent = criteria.from(parentObject.getClass());
            Join join = parent.join(parentToChildrenField);

            criteria.select(join)
                    .where(cb.and(cb.equal(parent, parentObject), join.get(currentIdField).in(searchResultIds)));
            List<T> filteredSearchResult = em.createQuery(criteria).getResultList();
            session.close();

            return filteredSearchResult;
        } catch (IllegalArgumentException ex) {
            Logger.getLogger(HibernateGenericDAO.class.getName()).log(Level.SEVERE, null, ex);
        } catch (SecurityException ex) {
            Logger.getLogger(HibernateGenericDAO.class.getName()).log(Level.SEVERE, null, ex);
        } catch (NoSuchFieldException ex) {
            Logger.getLogger(HibernateGenericDAO.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IllegalAccessException ex) {
            Logger.getLogger(HibernateGenericDAO.class.getName()).log(Level.SEVERE, null, ex);
        }
        return new ArrayList<T>();
    }

    @Override
    public <P> P associate(DaoParameter<P, T> parameter, Collection<T> newBeans) {
        if (parameter instanceof FindEntityCollectionParameter) {
            FindEntityCollectionParameter<P, T> feParam = (FindEntityCollectionParameter<P, T>) parameter;
            return associate(newBeans, feParam.getParentObject(), feParam.getParentToCurrentField(),
                    feParam.getRelationManager());
        } else {
            return null;
        }
    }

    @Override
    public <P> T saveAndAssociate(T newBean, P parentBean, String parentToNewBeanField,
            RelationManager<P, T> relationManager) {
        EntityManager em = emf.createEntityManager();
        FullTextEntityManager ftem = Search.getFullTextEntityManager(em);
        try {
            em.getTransaction().begin();
            T newBeanFromDB = em.merge(newBean);

            //P parentBeanFromDB = em.merge(parentBean);
            P parentBeanFromDB = refresh(em, parentBean);

            relationManager.link(parentBeanFromDB, newBeanFromDB, parentToNewBeanField, em);
            em.merge(newBeanFromDB);

            em.flush();
            if (parentBeanFromDB != null && newBeanFromDB != null) {
                ftem.index(parentBeanFromDB);
                ftem.index(newBeanFromDB);
            }

            em.getTransaction().commit();
            return newBeanFromDB;
        } catch (Exception e) {
            Logger.getLogger(HibernateGenericDAO.class.getName()).log(Level.SEVERE, null, e);
            em.getTransaction().rollback();
        } finally {
            em.close();
        }
        return null;
    }

    protected <P> P associate(Collection<T> newBeans, P parentBean, String parentToNewBeanField,
            RelationManager<P, T> relationManager) {
        EntityManager em = emf.createEntityManager();
        FullTextEntityManager ftem = Search.getFullTextEntityManager(em);
        try {
            em.getTransaction().begin();
            List<T> newBeansFromDB = new ArrayList<T>();
            //P parentBeanFromDB = em.merge(parentBean);
            P parentBeanFromDB = refresh(em, parentBean);

            for (T newBean : newBeans) {
                T newBeanFromDB = refresh(em, newBean);
                relationManager.link(parentBeanFromDB, newBeanFromDB, parentToNewBeanField, em);
                em.merge(newBeanFromDB);
                newBeansFromDB.add(newBeanFromDB);
            }
            em.flush();
            ftem.index(parentBeanFromDB);
            for (T newBean : newBeansFromDB) {
                ftem.index(newBean);
            }
            em.getTransaction().commit();
            return parentBeanFromDB;
        } catch (Exception e) {
            Logger.getLogger(HibernateGenericDAO.class.getName()).log(Level.SEVERE, null, e);
            em.getTransaction().rollback();
        } finally {
            em.close();
        }
        return null;
    }

    private <E> E refresh(EntityManager em, E entity) {
        Object idValue = this.getIdentifierValue(entity);
        if (idValue == null) {
            return null; //cannot refresh something that does exist not in DB
        }
        E parentBeanFromDB = em.find((Class<E>) entity.getClass(), idValue);
        return parentBeanFromDB;
    }

    @Override
    public T refresh(T entity) {
        EntityManager em = emf.createEntityManager();
        try {
            T entityFromDB = refresh(em, entity);
            return entityFromDB;
        } finally {
            em.close();
        }
    }

    @Override
    public <P> P associate(DaoParameter<P, T> parameter, T newBean, T oldBean) {
        if (parameter instanceof FindEntityCollectionParameter) {
            FindEntityCollectionParameter<P, T> feParam = (FindEntityCollectionParameter<P, T>) parameter;
            return associate(newBean, oldBean, feParam.getParentObject(), feParam.getParentToCurrentField(),
                    feParam.getRelationManager());
        } else {
            return null;
        }
    }

    @Override
    public Object saveWithRelation(Object newBean, Object parentBean, String parentToNewBeanField,
            RelationManager relationManager) {
        EntityManager em = emf.createEntityManager();
        FullTextEntityManager ftem = Search.getFullTextEntityManager(em);
        try {
            em.getTransaction().begin();
            Object newBeanFromDB = em.merge(newBean);
            //Object newBeanFromDB = refresh(em, newBean);
            Object parentBeanFromDB = refresh(em, parentBean);

            relationManager.link(parentBeanFromDB, newBeanFromDB, parentToNewBeanField, em);
            em.merge(newBeanFromDB);
            em.merge(parentBeanFromDB);
            em.flush();
            if (newBeanFromDB.getClass().isAnnotationPresent(Indexed.class)) {
                ftem.index(newBeanFromDB);
            }
            if (parentBeanFromDB.getClass().isAnnotationPresent(Indexed.class)) {
                ftem.index(parentBeanFromDB);
            }
            em.getTransaction().commit();
            return parentBeanFromDB;
        } catch (Exception e) {
            Logger.getLogger(HibernateGenericDAO.class.getName()).log(Level.SEVERE, null, e);
            em.getTransaction().rollback();
        } finally {
            em.close();
        }
        return null;
    }

    protected <P> P associate(T newBean, T oldBean, P parentBean, String parentToBeanField,
            RelationManager<P, T> relationManager) {
        EntityManager em = emf.createEntityManager();
        FullTextEntityManager ftem = Search.getFullTextEntityManager(em);
        try {
            em.getTransaction().begin();
            T oldBeanFromDB = refresh(em, oldBean);
            T newBeanFromDB = refresh(em, newBean);

            P parentBeanFromDB = refresh(em, parentBean);

            if (oldBeanFromDB != null) {
                relationManager.unlink(parentBeanFromDB, oldBeanFromDB, parentToBeanField, em);
            }
            relationManager.link(parentBeanFromDB, newBeanFromDB, parentToBeanField, em);
            em.merge(newBeanFromDB);
            em.merge(parentBeanFromDB);
            if (oldBeanFromDB != null) {
                em.merge(oldBeanFromDB);
            }
            em.flush();
            if (newBeanFromDB.getClass().isAnnotationPresent(Indexed.class)) {
                ftem.index(newBeanFromDB);
            }

            if (parentBeanFromDB.getClass().isAnnotationPresent(Indexed.class)) {
                ftem.index(parentBeanFromDB);
            }
            if (oldBeanFromDB != null) {
                if (oldBeanFromDB.getClass().isAnnotationPresent(Indexed.class)) {
                    ftem.index(oldBeanFromDB);
                }
            }
            em.getTransaction().commit();
            return parentBeanFromDB;
        } catch (Exception e) {
            Logger.getLogger(HibernateGenericDAO.class.getName()).log(Level.SEVERE, null, e);
            em.getTransaction().rollback();
        } finally {
            em.close();
        }
        return null;
    }

    @Override
    public <P> P unlink(DaoParameter<P, T> parameter, Collection<T> oldBeans) {
        if (parameter instanceof FindEntityCollectionParameter) {
            FindEntityCollectionParameter<P, T> feParam = (FindEntityCollectionParameter<P, T>) parameter;
            return unlink(oldBeans, feParam.getParentObject(), feParam.getParentToCurrentField(),
                    feParam.getRelationManager());
        } else {
            return null;
        }
    }

    //    private <P> P unlinkAndDelete(T oldBean, P parentBean, RelationManager<P, T> relationManager) {
    //        EntityManager em = emf.createEntityManager();
    //        FullTextEntityManager ftem = Search.getFullTextEntityManager(em);
    //
    //        try {
    //            em.getTransaction().begin();
    //
    //            P parentBeanFromDB = refresh(em, parentBean);
    //            T oldBeanFromDB = refresh(em, oldBean);
    //            relationManager.unlink(parentBeanFromDB, oldBeanFromDB, em);
    //            em.remove(oldBeanFromDB);
    //
    //            em.merge(parentBeanFromDB);
    //
    //            em.flush();
    //            if (parentBeanFromDB.getClass().isAnnotationPresent(Indexed.class)) {
    //                ftem.index(parentBeanFromDB);
    //            }
    //
    //            if (oldBeanFromDB.getClass().isAnnotationPresent(Indexed.class)) {
    //                ftem.purge(classOfEntity, (Serializable) getIdentifierValue((T) oldBeanFromDB));
    //            }
    //
    //            em.getTransaction().commit();
    //
    //            return parentBeanFromDB;
    //        } catch (Exception e) {
    //            Logger.getLogger(HibernateGenericDAO.class.getName()).log(Level.SEVERE, null, e);
    //            em.getTransaction().rollback();
    //        } finally {
    //            em.close();
    //        }
    //        return null;
    //    }

    @Override
    public Object unlinkAndDelete(Collection oldBeans, Object parentBean, String parentToNewBeanField,
            RelationManager relationManager) {
        EntityManager em = emf.createEntityManager();
        FullTextEntityManager ftem = Search.getFullTextEntityManager(em);

        try {
            em.getTransaction().begin();
            List oldBeansFromDB = new ArrayList();
            Object parentBeanFromDB = refresh(em, parentBean);
            for (Object oldBean : oldBeans) {
                Object oldBeanFromDB = refresh(em, oldBean);
                relationManager.unlink(parentBeanFromDB, oldBeanFromDB, parentToNewBeanField, em);
                em.remove(oldBeanFromDB);
                oldBeansFromDB.add(oldBeanFromDB);
            }
            em.merge(parentBeanFromDB);

            em.flush();
            if (parentBeanFromDB.getClass().isAnnotationPresent(Indexed.class)) {
                ftem.index(parentBeanFromDB);
            }

            for (Object oldBeanFromDB : oldBeansFromDB) {
                if (oldBeanFromDB.getClass().isAnnotationPresent(Indexed.class)) {
                    ftem.purge(classOfEntity, (Serializable) getIdentifierValue((T) oldBeanFromDB));
                }
            }
            em.getTransaction().commit();

            return parentBeanFromDB;
        } catch (Exception e) {
            Logger.getLogger(HibernateGenericDAO.class.getName()).log(Level.SEVERE, null, e);
            em.getTransaction().rollback();
        } finally {
            em.close();
        }
        return null;
    }

    private <P> P unlink(Collection<T> oldBeans, P parentBean, String parentToCurrentField,
            RelationManager<P, T> relationManager) {
        EntityManager em = emf.createEntityManager();
        FullTextEntityManager ftem = Search.getFullTextEntityManager(em);

        try {
            em.getTransaction().begin();
            List<T> oldBeansFromDB = new ArrayList<T>();
            P parentBeanFromDB = refresh(em, parentBean);
            for (T oldBean : oldBeans) {
                T oldBeanFromDB = refresh(em, oldBean);
                relationManager.unlink(parentBeanFromDB, oldBeanFromDB, parentToCurrentField, em);
                em.merge(oldBeanFromDB);
                oldBeansFromDB.add(oldBeanFromDB);
            }
            em.merge(parentBeanFromDB);

            em.flush();
            if (parentBeanFromDB.getClass().isAnnotationPresent(Indexed.class)) {
                ftem.index(parentBeanFromDB);
            }

            for (T oldBeanFromDB : oldBeansFromDB) {
                if (oldBeanFromDB.getClass().isAnnotationPresent(Indexed.class)) {
                    ftem.index(oldBeanFromDB);
                }
            }

            em.getTransaction().commit();

            return parentBeanFromDB;
        } catch (Exception e) {
            Logger.getLogger(HibernateGenericDAO.class.getName()).log(Level.SEVERE, null, e);
            em.getTransaction().rollback();
        } finally {
            em.close();
        }
        return null;
    }

    public <C> Object getIdentifierValue(C entity) {
        Object id = emf.getPersistenceUnitUtil().getIdentifier(entity);
        return id;
    }

    @Override
    public String getIdentifierFieldName() {
        EntityManager em = emf.createEntityManager();
        try {
            Metamodel metamodel = em.getMetamodel();
            EntityType entity = metamodel.entity(classOfEntity);
            Set<SingularAttribute> singularAttributes = entity.getSingularAttributes();
            for (SingularAttribute singularAttribute : singularAttributes) {
                if (singularAttribute.isId()) {
                    return singularAttribute.getName();
                }
            }
        } finally {
            em.close();
        }
        return null;
    }

}