• JPA 多表查询过程中,遇到大量数据的问题


    问题背景

      在JPA多表联合查询,执行JPA sql 查询语句的时候,会查询出多个对象所有的值。然后在内存中进行排序、重组。瞬间造成服务器内存使用量升高,影响查询性能。

    解决办法

      业务场景

        一对多查询,然后进行模糊搜索。

      解决办法

        PO类

          一类

          

    @Entity
    @Table(name = "transactionRecords")
    public class TransactionPO {
        
            @Id
    	@GeneratedValue(strategy = GenerationType.IDENTITY)
    	private Integer id;
    
            /**
    	 * 添加一对多关系 一条记录中可以有多条视频
    	 */
    	@OneToMany(mappedBy = "transactionPO", fetch = FetchType.LAZY)
    	@BatchSize(size = 15)  //使用@BatchSize(size = 15)可以指定一次最多查15条。不会造成一次查询大量数据
    	private List<VideoPO> list = new ArrayList<>();
    }
    

        多类

     

    @Entity
    @Table(name="VideInformation",indexes=@Index(columnList = "transactionId"))
    public class VideoPO {
    	@Id
    	@GeneratedValue(strategy = GenerationType.IDENTITY)
    	private Integer id;
    
            @ManyToOne
    	private TransactionPO transactionPO;
    
    }
    

        Service 实现方法

          

    package com.chinasofti.product.sc.application.jpa;
    
    import java.util.ArrayList;
    import java.util.Date;
    import java.util.Iterator;
    import java.util.List;
    import java.util.Map;
    
    import javax.persistence.EntityManager;
    import javax.persistence.PersistenceContext;
    import javax.persistence.TypedQuery;
    import javax.persistence.criteria.CriteriaBuilder;
    import javax.persistence.criteria.CriteriaQuery;
    import javax.persistence.criteria.Order;
    import javax.persistence.criteria.Predicate;
    import javax.persistence.criteria.Root;
    import javax.transaction.Transactional;
    
    import org.springframework.data.domain.PageImpl;
    import org.springframework.data.domain.Pageable;
    import org.springframework.data.domain.Sort;
    import org.springframework.data.domain.Sort.Direction;
    import org.springframework.data.jpa.repository.query.QueryUtils;
    
    import com.chinasofti.product.sc.application.jpa.entity.TransactionPO;
    
    public class TransactionRepositoryImpl implements TransactionCustomer {
    
    	@PersistenceContext
    	private EntityManager em;
    
    	@SuppressWarnings("deprecation")
    	@Transactional
    	@Override
    	public PageImpl<TransactionPO> searchVedioRecord(Map<String, Object> params, Pageable pageable) {
    
    		// Query Count
    		Long count = countByCondition(params);
    
    		// 数据装载
    		CriteriaBuilder cb = em.getCriteriaBuilder();
    		CriteriaQuery<TransactionPO> cq = cb.createQuery(TransactionPO.class);
    		Root<TransactionPO> root = cq.from(TransactionPO.class);
    
    		// root.fetch("list",JoinType.LEFT);  //如果使用这句就会造成多表全查询,造成性能降低
    		cq.select(root).distinct(true);
    		List<Predicate> predicates = buildQueryPredicate(params, root, cb);
    		if (!predicates.isEmpty()) {
    			cq.where(predicates.toArray(new Predicate[0]));
    		}
    		// Set order rule
    		List<Order> sortOrder = null;
    		if (pageable != null) {
    			sortOrder = QueryUtils.toOrders(pageable.getSort(), root, cb);
    		} else {
    			org.springframework.data.domain.Sort.Order order = new org.springframework.data.domain.Sort.Order(
    					Direction.DESC, "id");
    			List<org.springframework.data.domain.Sort.Order> orders = new ArrayList<>();
    			orders.add(order);
    			sortOrder = QueryUtils.toOrders(new Sort(orders), root, cb);
    		}
    		cq.orderBy(sortOrder);
    		TypedQuery<TransactionPO> query = this.em.createQuery(cq);
    		if (pageable != null) {
    			query.setMaxResults(pageable.getPageSize());
    			query.setFirstResult((int) pageable.getOffset());
    		}
    		List<TransactionPO> ts = query.getResultList();
    
    		for (TransactionPO t : ts) {
    			t.getList().size();   //优化点
    		}
    		return new PageImpl<TransactionPO>(ts, pageable, count);
    	}
    
    	@Override
    	public Long countByCondition(Map<String, Object> params) {
    		CriteriaBuilder cb = em.getCriteriaBuilder();
    		CriteriaQuery<Long> cq = cb.createQuery(Long.class);
    		Root<TransactionPO> root = cq.from(TransactionPO.class);
    		cq.select(cb.count(root));
    		List<Predicate> predicates = buildQueryPredicate(params, root, cb);
    		if (!predicates.isEmpty()) {
    			cq.where(predicates.toArray(new Predicate[0]));
    		}
    		Long clong = this.em.createQuery(cq).getSingleResult();
    		return clong;
    	}
    
    	/**
    	 * 
    	 * @param params
    	 *            params
    	 * @param root
    	 *            root
    	 * @param cb
    	 *            cb
    	 * @return list
    	 */
    	private List<Predicate> buildQueryPredicate(Map<String, Object> params, Root<TransactionPO> root,
    			CriteriaBuilder cb) {
    		List<Predicate> list = new ArrayList<Predicate>();
    		Iterator<Map.Entry<String, Object>> it = params.entrySet().iterator();
    		while (it.hasNext()) {
    			Map.Entry<String, Object> entry = it.next();
    			String key = entry.getKey();
    			if ("startTime".equals(key)) {
    				list.add(cb.greaterThan(root.<Date>get("createTime"), (Date) entry.getValue()));
    			}
    			if ("endTime".equals(key)) {
    				list.add(cb.lessThan(root.<Date>get("createTime"), (Date) entry.getValue()));
    			}
    			// 不支持模糊输入查询
    			if ("transactionId".equals(key)) {
    				list.add(cb.equal(root.get("transactionId").as(String.class), entry.getValue()));
    			}
    			// 支持模糊输入查询
    			if ("staffCode".equals(key)) {
    				list.add(cb.like(root.get("staffCode").as(String.class), "%" + entry.getValue().toString() + "%"));
    			}
    
    		}
    
    		return list;
    	}
    	
    
    }
    

        分析

            如果我们在代码中使用    root.fetch("list",JoinType.LEFT); 这样虽然说也可以查询出我们的结果集,但是由于Hibernate是以对象为查询单位,它会先查询整个对象,再去查另外一个对象。造成内存瞬间飙升。影响性能。这样运行之后,控制台报异常如下:

          

    firstResult/maxResults specified with collection fetch; applying in memory!
    

            所以我们需要进行优化。方法如下:

            1、在PO类中,一端的外键定义查询最大个数,用@BatchSize(size = 15)

                 2、在实现方法中,

    for (TransactionPO t : ts) {
       t.getList().size();
    }
    

      至此,大功告成!

  • 相关阅读:
    Windows 8.1 应用开发文章汇总
    演绎事件委托
    Azure China
    Orchard 刨析:导航篇
    为ASP.NET MVC应用程序使用高级功能
    兼容Mono的下一代云环境Web开发框架ASP.NET vNext
    商品导航菜单
    纳斯达克对经济泡沫的定义是:当交易价格远大于内在价值的时候,就称为泡沫(转)
    当一个低级问题,第一次解决时,你会感受到成就;第二次解决时,你感受到责任,第三次解决时,你可能更多的感受到无力(转)
    互联网组织的未来:剖析GitHub员工的任性之源(转)
  • 原文地址:https://www.cnblogs.com/Edward-Wang/p/10349704.html
Copyright © 2020-2023  润新知