net.groupbuy.dao.impl.ProductDaoImpl.java Source code

Java tutorial

Introduction

Here is the source code for net.groupbuy.dao.impl.ProductDaoImpl.java

Source

/*
 * Copyright 2005-2013 shopxx.net. All rights reserved.
 * Support: http://www.shopxx.net
 * License: http://www.shopxx.net/license
 */
package net.groupbuy.dao.impl;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.regex.Pattern;

import javax.annotation.Resource;
import javax.persistence.FlushModeType;
import javax.persistence.NoResultException;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.persistence.criteria.Subquery;

import net.groupbuy.Filter;
import net.groupbuy.Order;
import net.groupbuy.Page;
import net.groupbuy.Pageable;
import net.groupbuy.Setting;
import net.groupbuy.dao.GoodsDao;
import net.groupbuy.dao.ProductDao;
import net.groupbuy.dao.SnDao;
import net.groupbuy.entity.Attribute;
import net.groupbuy.entity.Brand;
import net.groupbuy.entity.Goods;
import net.groupbuy.entity.Member;
import net.groupbuy.entity.OrderItem;
import net.groupbuy.entity.Product;
import net.groupbuy.entity.ProductCategory;
import net.groupbuy.entity.Promotion;
import net.groupbuy.entity.SpecificationValue;
import net.groupbuy.entity.Tag;
import net.groupbuy.entity.Order.OrderStatus;
import net.groupbuy.entity.Order.PaymentStatus;
import net.groupbuy.entity.Product.OrderType;
import net.groupbuy.entity.Sn.Type;
import net.groupbuy.util.SettingUtils;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.builder.CompareToBuilder;
import org.springframework.stereotype.Repository;
import org.springframework.util.Assert;

/**
 * Dao - ?
 * 
 * @author SHOP++ Team
 * @version 3.0
 */
@Repository("productDaoImpl")
public class ProductDaoImpl extends BaseDaoImpl<Product, Long> implements ProductDao {

    private static final Pattern pattern = Pattern.compile("\\d*");

    @Resource(name = "goodsDaoImpl")
    private GoodsDao goodsDao;
    @Resource(name = "snDaoImpl")
    private SnDao snDao;

    public boolean snExists(String sn) {
        if (sn == null) {
            return false;
        }
        String jpql = "select count(*) from Product product where lower(product.sn) = lower(:sn)";
        Long count = entityManager.createQuery(jpql, Long.class).setFlushMode(FlushModeType.COMMIT)
                .setParameter("sn", sn).getSingleResult();
        return count > 0;
    }

    public Product findBySn(String sn) {
        if (sn == null) {
            return null;
        }
        String jpql = "select product from Product product where lower(product.sn) = lower(:sn)";
        try {
            return entityManager.createQuery(jpql, Product.class).setFlushMode(FlushModeType.COMMIT)
                    .setParameter("sn", sn).getSingleResult();
        } catch (NoResultException e) {
            return null;
        }
    }

    public List<Product> search(String keyword, Boolean isGift, Integer count) {
        if (StringUtils.isEmpty(keyword)) {
            return Collections.<Product>emptyList();
        }
        CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
        CriteriaQuery<Product> criteriaQuery = criteriaBuilder.createQuery(Product.class);
        Root<Product> root = criteriaQuery.from(Product.class);
        criteriaQuery.select(root);
        Predicate restrictions = criteriaBuilder.conjunction();
        if (pattern.matcher(keyword).matches()) {
            restrictions = criteriaBuilder.and(restrictions,
                    criteriaBuilder.or(criteriaBuilder.equal(root.get("id"), Long.valueOf(keyword)),
                            criteriaBuilder.like(root.<String>get("sn"), "%" + keyword + "%"),
                            criteriaBuilder.like(root.<String>get("fullName"), "%" + keyword + "%")));
        } else {
            restrictions = criteriaBuilder.and(restrictions,
                    criteriaBuilder.or(criteriaBuilder.like(root.<String>get("sn"), "%" + keyword + "%"),
                            criteriaBuilder.like(root.<String>get("fullName"), "%" + keyword + "%")));
        }
        if (isGift != null) {
            restrictions = criteriaBuilder.and(restrictions, criteriaBuilder.equal(root.get("isGift"), isGift));
        }
        criteriaQuery.where(restrictions);
        criteriaQuery.orderBy(criteriaBuilder.desc(root.get("isTop")));
        return super.findList(criteriaQuery, null, count, null, null);
    }

    public List<Product> findList(ProductCategory productCategory, Brand brand, Promotion promotion, List<Tag> tags,
            Map<Attribute, String> attributeValue, BigDecimal startPrice, BigDecimal endPrice, Boolean isMarketable,
            Boolean isList, Boolean isTop, Boolean isGift, Boolean isOutOfStock, Boolean isStockAlert,
            OrderType orderType, Integer count, List<Filter> filters, List<Order> orders) {
        CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
        CriteriaQuery<Product> criteriaQuery = criteriaBuilder.createQuery(Product.class);
        Root<Product> root = criteriaQuery.from(Product.class);
        criteriaQuery.select(root);
        Predicate restrictions = criteriaBuilder.conjunction();
        if (productCategory != null) {
            restrictions = criteriaBuilder.and(restrictions,
                    criteriaBuilder.or(criteriaBuilder.equal(root.get("productCategory"), productCategory),
                            criteriaBuilder.like(root.get("productCategory").<String>get("treePath"),
                                    "%" + ProductCategory.TREE_PATH_SEPARATOR + productCategory.getId()
                                            + ProductCategory.TREE_PATH_SEPARATOR + "%")));
        }
        if (brand != null) {
            restrictions = criteriaBuilder.and(restrictions, criteriaBuilder.equal(root.get("brand"), brand));
        }
        if (promotion != null) {
            Subquery<Product> subquery1 = criteriaQuery.subquery(Product.class);
            Root<Product> subqueryRoot1 = subquery1.from(Product.class);
            subquery1.select(subqueryRoot1);
            subquery1.where(criteriaBuilder.equal(subqueryRoot1, root),
                    criteriaBuilder.equal(subqueryRoot1.join("promotions"), promotion));

            Subquery<Product> subquery2 = criteriaQuery.subquery(Product.class);
            Root<Product> subqueryRoot2 = subquery2.from(Product.class);
            subquery2.select(subqueryRoot2);
            subquery2.where(criteriaBuilder.equal(subqueryRoot2, root),
                    criteriaBuilder.equal(subqueryRoot2.join("productCategory").join("promotions"), promotion));

            Subquery<Product> subquery3 = criteriaQuery.subquery(Product.class);
            Root<Product> subqueryRoot3 = subquery3.from(Product.class);
            subquery3.select(subqueryRoot3);
            subquery3.where(criteriaBuilder.equal(subqueryRoot3, root),
                    criteriaBuilder.equal(subqueryRoot3.join("brand").join("promotions"), promotion));

            restrictions = criteriaBuilder.and(restrictions, criteriaBuilder.or(criteriaBuilder.exists(subquery1),
                    criteriaBuilder.exists(subquery2), criteriaBuilder.exists(subquery3)));
        }
        if (tags != null && !tags.isEmpty()) {
            Subquery<Product> subquery = criteriaQuery.subquery(Product.class);
            Root<Product> subqueryRoot = subquery.from(Product.class);
            subquery.select(subqueryRoot);
            subquery.where(criteriaBuilder.equal(subqueryRoot, root), subqueryRoot.join("tags").in(tags));
            restrictions = criteriaBuilder.and(restrictions, criteriaBuilder.exists(subquery));
        }
        if (attributeValue != null) {
            for (Entry<Attribute, String> entry : attributeValue.entrySet()) {
                String propertyName = Product.ATTRIBUTE_VALUE_PROPERTY_NAME_PREFIX
                        + entry.getKey().getPropertyIndex();
                restrictions = criteriaBuilder.and(restrictions,
                        criteriaBuilder.equal(root.get(propertyName), entry.getValue()));
            }
        }
        if (startPrice != null && endPrice != null && startPrice.compareTo(endPrice) > 0) {
            BigDecimal temp = startPrice;
            startPrice = endPrice;
            endPrice = temp;
        }
        if (startPrice != null && startPrice.compareTo(new BigDecimal(0)) >= 0) {
            restrictions = criteriaBuilder.and(restrictions,
                    criteriaBuilder.ge(root.<Number>get("price"), startPrice));
        }
        if (endPrice != null && endPrice.compareTo(new BigDecimal(0)) >= 0) {
            restrictions = criteriaBuilder.and(restrictions,
                    criteriaBuilder.le(root.<Number>get("price"), endPrice));
        }
        if (isMarketable != null) {
            restrictions = criteriaBuilder.and(restrictions,
                    criteriaBuilder.equal(root.get("isMarketable"), isMarketable));
        }
        if (isList != null) {
            restrictions = criteriaBuilder.and(restrictions, criteriaBuilder.equal(root.get("isList"), isList));
        }
        if (isTop != null) {
            restrictions = criteriaBuilder.and(restrictions, criteriaBuilder.equal(root.get("isTop"), isTop));
        }
        if (isGift != null) {
            restrictions = criteriaBuilder.and(restrictions, criteriaBuilder.equal(root.get("isGift"), isGift));
        }
        Path<Integer> stock = root.get("stock");
        Path<Integer> allocatedStock = root.get("allocatedStock");
        if (isOutOfStock != null) {
            if (isOutOfStock) {
                restrictions = criteriaBuilder.and(restrictions, criteriaBuilder.isNotNull(stock),
                        criteriaBuilder.lessThanOrEqualTo(stock, allocatedStock));
            } else {
                restrictions = criteriaBuilder.and(restrictions, criteriaBuilder.or(criteriaBuilder.isNull(stock),
                        criteriaBuilder.greaterThan(stock, allocatedStock)));
            }
        }
        if (isStockAlert != null) {
            Setting setting = SettingUtils.get();
            if (isStockAlert) {
                restrictions = criteriaBuilder.and(restrictions, criteriaBuilder.isNotNull(stock),
                        criteriaBuilder.lessThanOrEqualTo(stock,
                                criteriaBuilder.sum(allocatedStock, setting.getStockAlertCount())));
            } else {
                restrictions = criteriaBuilder.and(restrictions,
                        criteriaBuilder.or(criteriaBuilder.isNull(stock), criteriaBuilder.greaterThan(stock,
                                criteriaBuilder.sum(allocatedStock, setting.getStockAlertCount()))));
            }
        }
        criteriaQuery.where(restrictions);
        if (orderType == OrderType.priceAsc) {
            orders.add(Order.asc("price"));
            orders.add(Order.desc("createDate"));
        } else if (orderType == OrderType.priceDesc) {
            orders.add(Order.desc("price"));
            orders.add(Order.desc("createDate"));
        } else if (orderType == OrderType.salesDesc) {
            orders.add(Order.desc("sales"));
            orders.add(Order.desc("createDate"));
        } else if (orderType == OrderType.scoreDesc) {
            orders.add(Order.desc("score"));
            orders.add(Order.desc("createDate"));
        } else if (orderType == OrderType.dateDesc) {
            orders.add(Order.desc("createDate"));
        } else {
            orders.add(Order.desc("isTop"));
            orders.add(Order.desc("modifyDate"));
        }
        return super.findList(criteriaQuery, null, count, filters, orders);
    }

    public List<Product> findList(ProductCategory productCategory, Date beginDate, Date endDate, Integer first,
            Integer count) {
        CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
        CriteriaQuery<Product> criteriaQuery = criteriaBuilder.createQuery(Product.class);
        Root<Product> root = criteriaQuery.from(Product.class);
        criteriaQuery.select(root);
        Predicate restrictions = criteriaBuilder.conjunction();
        restrictions = criteriaBuilder.and(restrictions, criteriaBuilder.equal(root.get("isMarketable"), true));
        if (productCategory != null) {
            restrictions = criteriaBuilder.and(restrictions,
                    criteriaBuilder.or(criteriaBuilder.equal(root.get("productCategory"), productCategory),
                            criteriaBuilder.like(root.get("productCategory").<String>get("treePath"),
                                    "%" + ProductCategory.TREE_PATH_SEPARATOR + productCategory.getId()
                                            + ProductCategory.TREE_PATH_SEPARATOR + "%")));
        }
        if (beginDate != null) {
            restrictions = criteriaBuilder.and(restrictions,
                    criteriaBuilder.greaterThanOrEqualTo(root.<Date>get("createDate"), beginDate));
        }
        if (endDate != null) {
            restrictions = criteriaBuilder.and(restrictions,
                    criteriaBuilder.lessThanOrEqualTo(root.<Date>get("createDate"), endDate));
        }
        criteriaQuery.where(restrictions);
        criteriaQuery.orderBy(criteriaBuilder.desc(root.get("isTop")));
        return super.findList(criteriaQuery, first, count, null, null);
    }

    public List<Product> findList(Goods goods, Set<Product> excludes) {
        CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
        CriteriaQuery<Product> criteriaQuery = criteriaBuilder.createQuery(Product.class);
        Root<Product> root = criteriaQuery.from(Product.class);
        criteriaQuery.select(root);
        Predicate restrictions = criteriaBuilder.conjunction();
        if (goods != null) {
            restrictions = criteriaBuilder.and(restrictions, criteriaBuilder.equal(root.get("goods"), goods));
        }
        if (excludes != null && !excludes.isEmpty()) {
            restrictions = criteriaBuilder.and(restrictions, criteriaBuilder.not(root.in(excludes)));
        }
        criteriaQuery.where(restrictions);
        return entityManager.createQuery(criteriaQuery).setFlushMode(FlushModeType.COMMIT).getResultList();
    }

    public List<Object[]> findSalesList(Date beginDate, Date endDate, Integer count) {
        CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
        CriteriaQuery<Object[]> criteriaQuery = criteriaBuilder.createQuery(Object[].class);
        Root<Product> product = criteriaQuery.from(Product.class);
        Join<Product, OrderItem> orderItems = product.join("orderItems");
        Join<Product, net.groupbuy.entity.Order> order = orderItems.join("order");
        criteriaQuery.multiselect(product.get("id"), product.get("sn"), product.get("name"),
                product.get("fullName"), product.get("price"),
                criteriaBuilder.sum(orderItems.<Integer>get("quantity")), criteriaBuilder.sum(criteriaBuilder
                        .prod(orderItems.<Integer>get("quantity"), orderItems.<BigDecimal>get("price"))));
        Predicate restrictions = criteriaBuilder.conjunction();
        if (beginDate != null) {
            restrictions = criteriaBuilder.and(restrictions,
                    criteriaBuilder.greaterThanOrEqualTo(order.<Date>get("createDate"), beginDate));
        }
        if (endDate != null) {
            restrictions = criteriaBuilder.and(restrictions,
                    criteriaBuilder.lessThanOrEqualTo(order.<Date>get("createDate"), endDate));
        }
        restrictions = criteriaBuilder.and(restrictions,
                criteriaBuilder.equal(order.get("orderStatus"), OrderStatus.completed),
                criteriaBuilder.equal(order.get("paymentStatus"), PaymentStatus.paid));
        criteriaQuery.where(restrictions);
        criteriaQuery.groupBy(product.get("id"), product.get("sn"), product.get("name"), product.get("fullName"),
                product.get("price"));
        criteriaQuery.orderBy(criteriaBuilder.desc(criteriaBuilder.sum(
                criteriaBuilder.prod(orderItems.<Integer>get("quantity"), orderItems.<BigDecimal>get("price")))));
        TypedQuery<Object[]> query = entityManager.createQuery(criteriaQuery).setFlushMode(FlushModeType.COMMIT);
        if (count != null && count >= 0) {
            query.setMaxResults(count);
        }
        return query.getResultList();
    }

    public Page<Product> findPage(ProductCategory productCategory, Brand brand, Promotion promotion, List<Tag> tags,
            Map<Attribute, String> attributeValue, BigDecimal startPrice, BigDecimal endPrice, Boolean isMarketable,
            Boolean isList, Boolean isTop, Boolean isGift, Boolean isOutOfStock, Boolean isStockAlert,
            OrderType orderType, Pageable pageable) {
        CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
        CriteriaQuery<Product> criteriaQuery = criteriaBuilder.createQuery(Product.class);
        Root<Product> root = criteriaQuery.from(Product.class);
        criteriaQuery.select(root);
        Predicate restrictions = criteriaBuilder.conjunction();
        if (productCategory != null) {
            restrictions = criteriaBuilder.and(restrictions,
                    criteriaBuilder.or(criteriaBuilder.equal(root.get("productCategory"), productCategory),
                            criteriaBuilder.like(root.get("productCategory").<String>get("treePath"),
                                    "%" + ProductCategory.TREE_PATH_SEPARATOR + productCategory.getId()
                                            + ProductCategory.TREE_PATH_SEPARATOR + "%")));
        }
        if (brand != null) {
            restrictions = criteriaBuilder.and(restrictions, criteriaBuilder.equal(root.get("brand"), brand));
        }
        if (promotion != null) {
            Subquery<Product> subquery1 = criteriaQuery.subquery(Product.class);
            Root<Product> subqueryRoot1 = subquery1.from(Product.class);
            subquery1.select(subqueryRoot1);
            subquery1.where(criteriaBuilder.equal(subqueryRoot1, root),
                    criteriaBuilder.equal(subqueryRoot1.join("promotions"), promotion));

            Subquery<Product> subquery2 = criteriaQuery.subquery(Product.class);
            Root<Product> subqueryRoot2 = subquery2.from(Product.class);
            subquery2.select(subqueryRoot2);
            subquery2.where(criteriaBuilder.equal(subqueryRoot2, root),
                    criteriaBuilder.equal(subqueryRoot2.join("productCategory").join("promotions"), promotion));

            Subquery<Product> subquery3 = criteriaQuery.subquery(Product.class);
            Root<Product> subqueryRoot3 = subquery3.from(Product.class);
            subquery3.select(subqueryRoot3);
            subquery3.where(criteriaBuilder.equal(subqueryRoot3, root),
                    criteriaBuilder.equal(subqueryRoot3.join("brand").join("promotions"), promotion));

            restrictions = criteriaBuilder.and(restrictions, criteriaBuilder.or(criteriaBuilder.exists(subquery1),
                    criteriaBuilder.exists(subquery2), criteriaBuilder.exists(subquery3)));
        }
        if (tags != null && !tags.isEmpty()) {
            Subquery<Product> subquery = criteriaQuery.subquery(Product.class);
            Root<Product> subqueryRoot = subquery.from(Product.class);
            subquery.select(subqueryRoot);
            subquery.where(criteriaBuilder.equal(subqueryRoot, root), subqueryRoot.join("tags").in(tags));
            restrictions = criteriaBuilder.and(restrictions, criteriaBuilder.exists(subquery));
        }
        if (attributeValue != null) {
            for (Entry<Attribute, String> entry : attributeValue.entrySet()) {
                String propertyName = Product.ATTRIBUTE_VALUE_PROPERTY_NAME_PREFIX
                        + entry.getKey().getPropertyIndex();
                restrictions = criteriaBuilder.and(restrictions,
                        criteriaBuilder.equal(root.get(propertyName), entry.getValue()));
            }
        }
        if (startPrice != null && endPrice != null && startPrice.compareTo(endPrice) > 0) {
            BigDecimal temp = startPrice;
            startPrice = endPrice;
            endPrice = temp;
        }
        if (startPrice != null && startPrice.compareTo(new BigDecimal(0)) >= 0) {
            restrictions = criteriaBuilder.and(restrictions,
                    criteriaBuilder.ge(root.<Number>get("price"), startPrice));
        }
        if (endPrice != null && endPrice.compareTo(new BigDecimal(0)) >= 0) {
            restrictions = criteriaBuilder.and(restrictions,
                    criteriaBuilder.le(root.<Number>get("price"), endPrice));
        }
        if (isMarketable != null) {
            restrictions = criteriaBuilder.and(restrictions,
                    criteriaBuilder.equal(root.get("isMarketable"), isMarketable));
        }
        if (isList != null) {
            restrictions = criteriaBuilder.and(restrictions, criteriaBuilder.equal(root.get("isList"), isList));
        }
        if (isTop != null) {
            restrictions = criteriaBuilder.and(restrictions, criteriaBuilder.equal(root.get("isTop"), isTop));
        }
        if (isGift != null) {
            restrictions = criteriaBuilder.and(restrictions, criteriaBuilder.equal(root.get("isGift"), isGift));
        }
        Path<Integer> stock = root.get("stock");
        Path<Integer> allocatedStock = root.get("allocatedStock");
        if (isOutOfStock != null) {
            if (isOutOfStock) {
                restrictions = criteriaBuilder.and(restrictions, criteriaBuilder.isNotNull(stock),
                        criteriaBuilder.lessThanOrEqualTo(stock, allocatedStock));
            } else {
                restrictions = criteriaBuilder.and(restrictions, criteriaBuilder.or(criteriaBuilder.isNull(stock),
                        criteriaBuilder.greaterThan(stock, allocatedStock)));
            }
        }
        if (isStockAlert != null) {
            Setting setting = SettingUtils.get();
            if (isStockAlert) {
                restrictions = criteriaBuilder.and(restrictions, criteriaBuilder.isNotNull(stock),
                        criteriaBuilder.lessThanOrEqualTo(stock,
                                criteriaBuilder.sum(allocatedStock, setting.getStockAlertCount())));
            } else {
                restrictions = criteriaBuilder.and(restrictions,
                        criteriaBuilder.or(criteriaBuilder.isNull(stock), criteriaBuilder.greaterThan(stock,
                                criteriaBuilder.sum(allocatedStock, setting.getStockAlertCount()))));
            }
        }
        criteriaQuery.where(restrictions);
        List<Order> orders = pageable.getOrders();
        if (orderType == OrderType.priceAsc) {
            orders.add(Order.asc("price"));
            orders.add(Order.desc("createDate"));
        } else if (orderType == OrderType.priceDesc) {
            orders.add(Order.desc("price"));
            orders.add(Order.desc("createDate"));
        } else if (orderType == OrderType.salesDesc) {
            orders.add(Order.desc("sales"));
            orders.add(Order.desc("createDate"));
        } else if (orderType == OrderType.scoreDesc) {
            orders.add(Order.desc("score"));
            orders.add(Order.desc("createDate"));
        } else if (orderType == OrderType.dateDesc) {
            orders.add(Order.desc("createDate"));
        } else {
            orders.add(Order.desc("isTop"));
            orders.add(Order.desc("modifyDate"));
        }
        return super.findPage(criteriaQuery, pageable);
    }

    public Page<Product> findPage(Member member, Pageable pageable) {
        if (member == null) {
            return new Page<Product>(Collections.<Product>emptyList(), 0, pageable);
        }
        CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
        CriteriaQuery<Product> criteriaQuery = criteriaBuilder.createQuery(Product.class);
        Root<Product> root = criteriaQuery.from(Product.class);
        criteriaQuery.select(root);
        criteriaQuery.where(criteriaBuilder.equal(root.join("favoriteMembers"), member));
        return super.findPage(criteriaQuery, pageable);
    }

    public Long count(Member favoriteMember, Boolean isMarketable, Boolean isList, Boolean isTop, Boolean isGift,
            Boolean isOutOfStock, Boolean isStockAlert) {
        CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
        CriteriaQuery<Product> criteriaQuery = criteriaBuilder.createQuery(Product.class);
        Root<Product> root = criteriaQuery.from(Product.class);
        criteriaQuery.select(root);
        Predicate restrictions = criteriaBuilder.conjunction();
        if (favoriteMember != null) {
            restrictions = criteriaBuilder.and(restrictions,
                    criteriaBuilder.equal(root.join("favoriteMembers"), favoriteMember));
        }
        if (isMarketable != null) {
            restrictions = criteriaBuilder.and(restrictions,
                    criteriaBuilder.equal(root.get("isMarketable"), isMarketable));
        }
        if (isList != null) {
            restrictions = criteriaBuilder.and(restrictions, criteriaBuilder.equal(root.get("isList"), isList));
        }
        if (isTop != null) {
            restrictions = criteriaBuilder.and(restrictions, criteriaBuilder.equal(root.get("isTop"), isTop));
        }
        if (isGift != null) {
            restrictions = criteriaBuilder.and(restrictions, criteriaBuilder.equal(root.get("isGift"), isGift));
        }
        Path<Integer> stock = root.get("stock");
        Path<Integer> allocatedStock = root.get("allocatedStock");
        if (isOutOfStock != null) {
            if (isOutOfStock) {
                restrictions = criteriaBuilder.and(restrictions, criteriaBuilder.isNotNull(stock),
                        criteriaBuilder.lessThanOrEqualTo(stock, allocatedStock));
            } else {
                restrictions = criteriaBuilder.and(restrictions, criteriaBuilder.or(criteriaBuilder.isNull(stock),
                        criteriaBuilder.greaterThan(stock, allocatedStock)));
            }
        }
        if (isStockAlert != null) {
            Setting setting = SettingUtils.get();
            if (isStockAlert) {
                restrictions = criteriaBuilder.and(restrictions, criteriaBuilder.isNotNull(stock),
                        criteriaBuilder.lessThanOrEqualTo(stock,
                                criteriaBuilder.sum(allocatedStock, setting.getStockAlertCount())));
            } else {
                restrictions = criteriaBuilder.and(restrictions,
                        criteriaBuilder.or(criteriaBuilder.isNull(stock), criteriaBuilder.greaterThan(stock,
                                criteriaBuilder.sum(allocatedStock, setting.getStockAlertCount()))));
            }
        }
        criteriaQuery.where(restrictions);
        return super.count(criteriaQuery, null);
    }

    public boolean isPurchased(Member member, Product product) {
        if (member == null || product == null) {
            return false;
        }
        String jqpl = "select count(*) from OrderItem orderItem where orderItem.product = :product and orderItem.order.member = :member and orderItem.order.orderStatus = :orderStatus";
        Long count = entityManager.createQuery(jqpl, Long.class).setFlushMode(FlushModeType.COMMIT)
                .setParameter("product", product).setParameter("member", member)
                .setParameter("orderStatus", OrderStatus.completed).getSingleResult();
        return count > 0;
    }

    /**
     * ?
     * 
     * @param product
     *            ?
     */
    @Override
    public void persist(Product product) {
        Assert.notNull(product);

        setValue(product);
        super.persist(product);
    }

    /**
     * 
     * 
     * @param product
     *            ?
     * @return ?
     */
    @Override
    public Product merge(Product product) {
        Assert.notNull(product);

        if (!product.getIsGift()) {
            String jpql = "delete from GiftItem giftItem where giftItem.gift = :product";
            entityManager.createQuery(jpql).setFlushMode(FlushModeType.COMMIT).setParameter("product", product)
                    .executeUpdate();
        }
        if (!product.getIsMarketable() || product.getIsGift()) {
            String jpql = "delete from CartItem cartItem where cartItem.product = :product";
            entityManager.createQuery(jpql).setFlushMode(FlushModeType.COMMIT).setParameter("product", product)
                    .executeUpdate();
        }
        setValue(product);
        return super.merge(product);
    }

    @Override
    public void remove(Product product) {
        if (product != null) {
            Goods goods = product.getGoods();
            if (goods != null && goods.getProducts() != null) {
                goods.getProducts().remove(product);
                if (goods.getProducts().isEmpty()) {
                    goodsDao.remove(goods);
                }
            }
        }
        super.remove(product);
    }

    /**
     * 
     * 
     * @param product
     *            ?
     */
    private void setValue(Product product) {
        if (product == null) {
            return;
        }
        if (StringUtils.isEmpty(product.getSn())) {
            String sn;
            do {
                sn = snDao.generate(Type.product);
            } while (snExists(sn));
            product.setSn(sn);
        }
        StringBuffer fullName = new StringBuffer(product.getName());
        if (product.getSpecificationValues() != null && !product.getSpecificationValues().isEmpty()) {
            List<SpecificationValue> specificationValues = new ArrayList<SpecificationValue>(
                    product.getSpecificationValues());
            Collections.sort(specificationValues, new Comparator<SpecificationValue>() {
                public int compare(SpecificationValue a1, SpecificationValue a2) {
                    return new CompareToBuilder().append(a1.getSpecification(), a2.getSpecification())
                            .toComparison();
                }
            });
            fullName.append(Product.FULL_NAME_SPECIFICATION_PREFIX);
            int i = 0;
            for (Iterator<SpecificationValue> iterator = specificationValues.iterator(); iterator.hasNext(); i++) {
                if (i != 0) {
                    fullName.append(Product.FULL_NAME_SPECIFICATION_SEPARATOR);
                }
                fullName.append(iterator.next().getName());
            }
            fullName.append(Product.FULL_NAME_SPECIFICATION_SUFFIX);
        }
        product.setFullName(fullName.toString());
    }

}