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();
}

  至此,大功告成!

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM