cn.guoyukun.spring.jpa.plugin.serivce.BaseMovableService.java Source code

Java tutorial

Introduction

Here is the source code for cn.guoyukun.spring.jpa.plugin.serivce.BaseMovableService.java

Source

/**
 * Copyright (c) 2005-2012 https://github.com/zhangkaitao
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 */
package cn.guoyukun.spring.jpa.plugin.serivce;

import cn.guoyukun.spring.jpa.entity.BaseEntity;
import cn.guoyukun.spring.jpa.entity.search.Searchable;
import cn.guoyukun.spring.jpa.plugin.entity.Movable;
import cn.guoyukun.spring.jpa.repository.RepositoryHelper;
import cn.guoyukun.spring.jpa.service.BaseService;

import com.google.common.collect.Maps;

import org.springframework.aop.framework.AopContext;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;

import java.io.Serializable;
import java.util.List;
import java.util.Map;

/**
 * <p>User: 
 * <p>Date: 13-2-22 ?2:34
 * <p>Version: 1.0
 */
public abstract class BaseMovableService<M extends BaseEntity & Movable, ID extends Serializable>
        extends BaseService<M, ID> {

    //??
    private final Integer stepLength;

    /**
     * 1000
     */
    protected BaseMovableService() {
        this(1000);

    }

    protected BaseMovableService(Integer stepLength) {
        this.stepLength = stepLength;
    }

    /**
     * ?? 1000
     *
     * @return
     */
    public Integer getStepLength() {
        return stepLength;
    }

    @Override
    public M save(M m) {
        if (m.getWeight() == null) {
            m.setWeight(findNextWeight());
        }
        return super.save(m);
    }

    /**
     * ??
     * {fromId}{}toId}?
     *  fromWeight 2000 toWeight 1000    500
     *
     * @param fromId
     * @param toId
     */
    public void down(ID fromId, ID toId) {
        M from = findOne(fromId);
        M to = findOne(toId);
        if (from == null || to == null || from.equals(to)) {
            return;
        }
        Integer fromWeight = from.getWeight();
        Integer toWeight = to.getWeight();

        M nextTo = findNextByWeight(toWeight);

        //toIdfromId ????
        if (from.equals(nextTo)) {
            from.setWeight(toWeight);
            to.setWeight(fromWeight);
            return;
        }

        int minWeight = Math.min(fromWeight, toWeight);
        int maxWeight = Math.max(fromWeight, toWeight);
        //??weight
        int count = countByBetween(minWeight, maxWeight).intValue();
        if (count > 0 && count < 20) {
            List<M> moves = findByBetweenAndAsc(minWeight, maxWeight);
            if (fromWeight < toWeight) {
                Integer swapInteger = moves.get(count - 2).getWeight();
                for (int i = count - 2; i >= 1; i--) {
                    //?weight = toWeight;
                    moves.get(i).setWeight(moves.get(i - 1).getWeight());
                }
                moves.get(0).setWeight(swapInteger);
            } else {
                for (int i = 0; i <= count - 2; i++) {
                    moves.get(i).setWeight(moves.get(i + 1).getWeight());
                }
                moves.get(count - 1).setWeight(minWeight);
            }
            return;
        }

        M preTo = findPreByWeight(toWeight);

        //??
        int newWeight = 0;
        if (preTo == null) {
            newWeight = toWeight / 2;
        } else {
            newWeight = toWeight - (toWeight - preTo.getWeight()) / 2;

        }

        if (Math.abs(newWeight - toWeight) <= 1) {
            throw new IllegalStateException(
                    String.format("up error, no enough weight :fromId:%d, toId:%d", fromId, toId));
        }
        from.setWeight(newWeight);

    }

    /**
     * ??
     * {fromId}toId
     *  fromWeight 1000 toWeight 2000  3000  2500
     *
     * @param fromId
     * @param toId
     */
    public void up(ID fromId, ID toId) {
        M from = findOne(fromId);
        M to = findOne(toId);
        if (from == null || to == null || from.equals(to)) {
            return;
        }
        Integer fromWeight = from.getWeight();
        Integer toWeight = to.getWeight();

        M preTo = findPreByWeight(toWeight);
        //toIdfromId ????
        if (from.equals(preTo)) {
            from.setWeight(toWeight);
            to.setWeight(fromWeight);
            return;
        }

        int minWeight = Math.min(fromWeight, toWeight);
        int maxWeight = Math.max(fromWeight, toWeight);
        //??weight
        int count = countByBetween(minWeight, maxWeight).intValue();
        if (count > 0 && count < 20) {
            List<M> moves = findByBetweenAndDesc(minWeight, maxWeight);
            //123/124
            //5000 4000 3000

            if (fromWeight > toWeight) {
                Integer swapInteger = moves.get(count - 2).getWeight();
                for (int i = count - 2; i >= 1; i--) {
                    //?weight = toWeight;
                    moves.get(i).setWeight(moves.get(i - 1).getWeight());
                }
                moves.get(0).setWeight(swapInteger);
            } else {
                for (int i = 0; i <= count - 2; i++) {
                    moves.get(i).setWeight(moves.get(i + 1).getWeight());
                }
                moves.get(count - 1).setWeight(maxWeight);
            }
            return;
        }

        //toIdfromId ????
        if (from.equals(preTo)) {
            from.setWeight(toWeight);
            to.setWeight(fromWeight);
            return;
        }
        M nextTo = findNextByWeight(toWeight);

        //??
        int newWeight = 0;
        if (nextTo == null) {
            newWeight = toWeight + stepLength;
        } else {
            newWeight = toWeight + (nextTo.getWeight() - toWeight) / 2;
        }

        if (Math.abs(newWeight - toWeight) <= 1) {
            throw new IllegalStateException(
                    String.format("down error, no enough weight :fromId:%d, toId:%d", fromId, toId));
        }
        from.setWeight(newWeight);
    }

    public void reweight() {
        int batchSize = 100;
        Sort sort = new Sort(Sort.Direction.DESC, "weight");
        Pageable pageable = new PageRequest(0, batchSize, sort);
        Page<M> page = findAll(pageable);
        do {
            //doReweight?requiresNew
            ((BaseMovableService) AopContext.currentProxy()).doReweight(page);

            RepositoryHelper.clear();

            if (page.isLast()) {
                break;
            }

            pageable = new PageRequest((pageable.getPageNumber() + 1) * batchSize, batchSize, sort);

            page = findAll(pageable);

        } while (true);
    }

    public void doReweight(Page<M> page) {
        int totalElements = (int) page.getTotalElements();
        List<M> moves = page.getContent();

        int firstElement = page.getNumber() * page.getSize();

        for (int i = 0; i < moves.size(); i++) {
            M move = moves.get(i);
            move.setWeight((totalElements - firstElement - i) * stepLength);
            update(move);
        }

    }

    private Integer findNextWeight() {
        Searchable searchable = Searchable.newSearchable().setPage(0, 1).addSort(Sort.Direction.DESC, "weight");
        Page<M> page = findAll(searchable);

        if (!page.hasContent()) {
            return stepLength;
        }

        return page.getContent().get(0).getWeight() + stepLength;
    }

    public M findPreByWeight(Integer weight) {
        Pageable pageable = new PageRequest(0, 1);
        Map<String, Object> searchParams = Maps.newHashMap();
        searchParams.put("weight_lt", weight);
        Sort sort = new Sort(Sort.Direction.DESC, "weight");
        Page<M> page = findAll(Searchable.newSearchable(searchParams).addSort(sort).setPage(pageable));

        if (page.hasContent()) {
            return page.getContent().get(0);
        }
        return null;
    }

    public M findNextByWeight(Integer weight) {
        Pageable pageable = new PageRequest(0, 1);

        Map<String, Object> searchParams = Maps.newHashMap();
        searchParams.put("weight_gt", weight);
        Sort sort = new Sort(Sort.Direction.ASC, "weight");
        Page<M> page = findAll(Searchable.newSearchable(searchParams).addSort(sort).setPage(pageable));

        if (page.hasContent()) {
            return page.getContent().get(0);
        }
        return null;
    }

    private Long countByBetween(Integer minWeight, Integer maxWeight) {
        Map<String, Object> searchParams = Maps.newHashMap();
        searchParams.put("weight_gte", minWeight);
        searchParams.put("weight_lte", maxWeight);
        return count(Searchable.newSearchable(searchParams));
    }

    //@Query(value = "from Move m where m.weight>=?1 and m.weight <= ?2 order by m.weight asc")
    List<M> findByBetweenAndAsc(Integer minWeight, Integer maxWeight) {
        Map<String, Object> searchParams = Maps.newHashMap();
        searchParams.put("weight_gte", minWeight);
        searchParams.put("weight_lte", maxWeight);

        return findAllWithSort(Searchable.newSearchable(searchParams).addSort(Sort.Direction.ASC, "weight"));
    }

    List<M> findByBetweenAndDesc(Integer minWeight, Integer maxWeight) {
        Map<String, Object> searchParams = Maps.newHashMap();
        searchParams.put("weight_gte", minWeight);
        searchParams.put("weight_lte", maxWeight);

        Sort sort = new Sort(Sort.Direction.DESC, "weight");
        return findAllWithSort(Searchable.newSearchable(searchParams).addSort(sort));
    }

}