Rumah  >  Artikel  >  Java  >  Cara menggunakan kelas alat Java untuk menulis laporan dengan cekap

Cara menggunakan kelas alat Java untuk menulis laporan dengan cekap

WBOY
WBOYke hadapan
2023-04-14 19:16:101108semak imbas

Mengapa menggunakan kod java untuk menulis laporan

Untuk data laporan, dalam kebanyakan kes, menulis sql digunakan untuk menyediakan sumber data untuk skrin/laporan yang besar, tetapi dalam beberapa kes yang rumit, ia tidak boleh dicapai menggunakan sql sahaja, atau Apabila sukar untuk dilaksanakan, logik kompleks akan dilaksanakan melalui kod dan hasilnya akhirnya akan dikembalikan.

Masalah yang dihadapi

Untuk laporan yang agak kompleks, selalunya perlu melakukan sambungan data, iaitu, cantuman jadual ke jadual, pengumpulan, pengiraan dan operasi lain. SQL secara semula jadi menyokong operasi ini dan mudah untuk dilaksanakan. Tetapi apabila kami perlu menyambung data dalam kod java, sokongan asli tidak begitu mesra Kami sering melaksanakannya seperti ini

Kini terdapat dua koleksi

List<ContractDetail> contractDetails; // 合同明细集合,合同会重复
List<ContractInfo> contractInfos; // 合同主要信息,不会有重复合同

Struktur data yang sepadan

public class ContractDetail {
    /**
     * 合同编号
     */
    private String contractNo;
    /**
     * 总金额
     */
    private BigDecimal moneyTotal;
}
public class ContractInfo {
    /**
     * 合同编号
     */
    private String contractNo;
    /**
     * 状态
     */
    private String status;
}
Keperluan

contractDetails dikaitkan dengan contractInfos mengikut contractNo, tapis data dengan status = 'Ditandatangani’

, dan kemudian kumpulkan mengikut kontrakNo dalam contractDetails, dan dapatkannya masing-masing. Jumlah wangJumlah yang sepadan dengan setiap kontrakNo

Output akhir hendaklah peta

Map<String /* 合同编码 */, BigDecimal /* 对应moneyTotal之和 */> result;
Biasanya kami akan melaksanakannya seperti ini

//  setp 1 过滤出 已签订状态的合同编码
Set<String> stopContract = contractInfos.stream()
                .filter(it -> "已签订".equals(it.getStatus()))
                .map(ContractInfo::getContractNo).collect(Collectors.toSet());
//step2 根据 step1的合同编码集合过滤出状态正确的contractDetail
  contractDetails = contractDetails.stream()
                .filter(it -> stopContract.contains(it.getContractNo()))
                .collect(Collectors.toList());
//step3 根据contractNo分别累加对应的moneyTotal
 Map<String, BigDecimal> result = new HashMap<>();
 contractDetails.stream().forEach(it -> {
            BigDecimal moneyTotal = Optional.ofNullable(result.get(it.getContractNo()))
                    .orElse(BigDecimal.ZERO);
            moneyTotal = moneyTotal.add(it.getMoneyTotal() != null ? it.getMoneyTotal() : BigDecimal.ZERO);
            result.put(it.getContractNo(), moneyTotal);
        });
Jelas sekali pelaksanaan ini lebih rumit Ya, kerana menggunakan sql tidak lebih daripada menyertai dan menambah kumpulan dengan. Jumlah. Masalah ini boleh diselesaikan dengan mudah. Kemudian lihat kelas alat berikut dan fikirkan sama ada terdapat cara yang lebih mudah untuk melaksanakannya.

Kelas alat

CollectionDataStream

Fungsi aliran data koleksi CollectionDataStream adalah untuk mengaitkan koleksi melalui antara muka dan melaksanakan dua operasi yang serupa dengan sql join dan left join

Dan melaksanakan fungsi menukar ke dan dari Stream di java.

Struktur data pengagregatan menukarkan koleksi kepada struktur data yang serupa dengan struktur jadual, termasuk nama jadual, data

public class AggregationData {
    Map<String, Map> aggregationMap;
    private AggregationData(){
        aggregationMap = new HashMap<>();
    }
    //key 为别名,value为对应对象
    public AggregationData(String tableName, Object data) {
        aggregationMap = new HashMap<>();
        aggregationMap.put(tableName, BeanUtil.beanToMap(data));
    }
    public Map<String, Map> getRowAllData() {
        return aggregationMap;
    }
    public Map getTableData(String tableName) {
        if (!aggregationMap.containsKey(tableName)) {
            throw new DataStreamException(tableName + ".not.exists");
        }
        return aggregationMap.get(tableName);
    }
    public void setTableData(String tableName, Object data) {
        if(aggregationMap.containsKey(tableName)){
            throw new DataStreamException(tableName+".has.been.exists!");
        }
        aggregationMap.put(tableName, BeanUtil.beanToMap(data));
    }
    private void setTableData(String tableName, Map<String, Object> data) {
        Map<String, Object> tableData =
                Optional.ofNullable(aggregationMap.get(tableName)).orElse(new HashMap<String, Object>());
        tableData.putAll(data);
        aggregationMap.put(tableName, tableData);
    }
    public AggregationData copyAggregationData() {
        AggregationData aggregationData = new AggregationData();
        for (String tableName : this.getRowAllData().keySet()) {
            aggregationData.setTableData(tableName, this.getRowAllData().get(tableName));
        }
        return aggregationData;
    }
}
AggregationData mewakili satu baris data, kunci aggregationMap ialah nama jadual dan nilainya ialah Data yang sepadan

Mari kita lihat secara terperinci antara muka ini

import java.util.Collection;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Stream;
public interface CollectionDataStream<T> {
    /**
     *将集合转化为数据流,并给一个别名
     * @param tableName
     * @param collection
     * @return
     */
    static CollectionDataStream<AggregationData> of(String tableName, Collection<?> collection) {
        return new CollectionDataStreamImpl(tableName, collection);
    }
    /**
     *将 Stream转化为数据流,并给一个别名
     * @param tableName
     * @param collection
     * @return
     */
    static CollectionDataStream<AggregationData> of(String tableName, Stream<?> collection) {
        return new CollectionDataStreamImpl(tableName, collection);
    }
    /**
     * 内连接,可自定义连接条件,使用双循环
     *
     * @param tableName
     * @param collection
     * @param predict
     * @param <T1>
     * @return
     */
    <T1> CollectionDataStream<T> join(String tableName, Collection<T1> collection, JoinPredicate<T, T1> predict);
    /**
     * 等值内连接,使用map优化
     *
     * @param collection
     * @param tableName
     * @param aggregationMapper
     * @param dataValueMapper
     * @param <T1>
     * @param <R>
     * @return
     */
    //等值条件推荐用法
    <T1, R> CollectionDataStream<T> joinUseHashOnEqualCondition(String tableName, Collection<T1> collection, Function<T, R> aggregationMapper, Function<T1, R> dataValueMapper);
    /**
     * 左连接,可自定义连接条件,使用双循环
     *
     * @param tableName
     * @param collection
     * @param predict
     * @param <T1>
     * @return
     */
    <T1> CollectionDataStream<T> leftJoin(String tableName, Collection<T1> collection, JoinPredicate<T, T1> predict);
    /**
     * 等值左连接,使用map优化
     *
     * @param collection
     * @param tableName
     * @param aggregationMapper
     * @param dataValueMapper
     * @param <T1>
     * @param <R>
     * @return
     */
    <T1, R> CollectionDataStream<T> leftJoinUseHashOnEqualCondition( String tableName, Collection<T1> collection,Function<T, R> aggregationMapper, Function<T1, R> dataValueMapper);
    Stream<T> toStream();
    Stream<Map> toStream(String tableName);
    <R> Stream<R> toStream(String tableName, Class<R> clzz);
    <R> Stream<R> toStream(Function<AggregationData, R> mapper);
}
Beri perhatian kepada perbezaan antara kaedah joinUseHashOnEqualCondition dan join.

Jika sambungan antara set ialah sambungan nilai yang sama medan, maka gunakan joinUseHashOnEqualCondition, yang secara dalaman menggunakan pengumpulan peta untuk menyertai. Jika anda menggunakan join terus, syarat sambungan boleh disesuaikan, tetapi keadaan dinilai melalui gelung berganda, yang kurang cekap. Oleh itu, dalam kes nilai yang sama, adalah lebih cekap untuk menggunakan joinUseHashOnEqualCondition.

Cara menggunakan

atau ambil keperluan di atas sebagai contoh

Mula-mula sambung dua koleksi

 CollectionDataStream.of("t1", contractDetails) .joinUseHashOnEqualCondition(
                        contractInfos.stream().filter(it -> "已签订".equals(it.getStatus())).collect(Collectors.toList()),
                        "t2",
                        agg -> agg.getTableData("t1").get("contractNo"),
                        ContractInfo::getContractNo
                );
Analisis kod

CollectionDataStream.of("t1", contractDetails)
adalah untuk menukarkan contractDetails ke dalam aliran data dengan nama jadual t1

 .joinUseHashOnEqualCondition(
                        contractInfos.stream().filter(
                          "t2",
                            it -> "已签订".equals(it.getStatus())).collect(Collectors.toList()),
                        agg -> agg.getTableData("t1").get("contractNo"),
                        ContractInfo::getContractNo
                );
disambungkan kepada contractInfos dan memberikan contractInfos alias t2 daripada t1. Selepas sambungan contractNol contractInfos, aliran data agregat baharu diperoleh

Sudah tentu, anda juga boleh menggunakan sambungan tersuai untuk mencapai

CollectionDataStream.of("t1", contractDetails)
                .join("t2",
                        contractInfos.stream().filter(it -> "已签订".equals(it.getStatus())).collect(Collectors.toList()),
                        (agg, data) -> agg.getTableData("t1").get("contractNo").equals(data.getContractNo())
                )
Di sini, melalui sambungan dalaman , ia juga memainkan peranan penapisan. Selepas sambungan selesai, kita masih perlu mengumpulkan untuk pengiraan, jadi kita perlu menggunakan kelas alat seterusnya

MyCollectors

ialah lanjutan daripada Pengumpul asli dalam stram, yang melaksanakan lebih biasa kumpulan untuk melaporkan Sesetengah operasi,

MyCollectorspackage collector;
import utils.NumberUtil;
import java.math.BigDecimal;
import java.util.Comparator;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collector;
import java.util.stream.Collectors;
public class MyCollectors {
    /**
     * 返回一个Collector用于对集合进行分组并且,对于组内有多个元素,只返回最后一个,其他的忽略
     * 适用于明确分组key唯一的情况,value可为空
     * 谨慎使用,如果分组有多条,会丢失数据!!!
     * @param keyMapper
     * @param <T>
     * @param <K>
     * @param <U>
     * @param <M>
     * @return
     */
    public static <T, K, U, M extends Map<K, U>>
    Collector<T, ?, Map<K, U>> groupingByLast(Function<? super T, ? extends K> keyMapper,
                                               Function<? super T, ? extends U> valueMapper) {
        return Collectors.groupingBy(keyMapper, Collectors.reducing(null, valueMapper, (o1, o2) -> o2));
    }
    /**
     * 传入一个keyMaper和一个比较器
     * 根据key分组,组内使用比较器进行比较,最终得到一个最大结果
     * @param keyMapper
     * @param comparator
     * @param <T>
     * @param <K>
     * @param <U>
     * @param <M>
     * @return
     */
    public static <T, K, U, M extends Map<K, U>>
    Collector<T, ?, Map<K, T>> groupingByMaxComparator(Function<? super T, ? extends K> keyMapper,
                                                      Comparator<T> comparator) {
        return Collectors.groupingBy(keyMapper, Collectors.collectingAndThen(Collectors.maxBy(comparator), it -> it.orElse(null)));
    }
    /**
     * 传入一个keyMaper和一个比较器
     * 根据key分组,组内使用比较器进行比较,最终得到一个最小结果
     * @param keyMapper
     * @param comparator
     * @param <T>
     * @param <K>
     * @param <U>
     * @param <M>
     * @return
     */
    public static <T, K, U, M extends Map<K, U>>
    Collector<T, ?, Map<K, T>> groupingByMinComparator(Function<? super T, ? extends K> keyMapper,
                                                       Comparator<T> comparator) {
        return Collectors.groupingBy(keyMapper, Collectors.collectingAndThen(Collectors.maxBy(comparator), it -> it.orElse(null)));
    }
    /**
     * 分组后组内按照指定字段求和
     * @param keyMapper
     * @param <T>
     * @param <K>
     * @return
     */
    public static <T, K>
    Collector<T, ?, Map<K, BigDecimal>> groupingAndSum(Function<? super T, ? extends K> keyMapper,
                                                       Function<? super T, BigDecimal> valueMapper) {
        return Collectors.groupingBy(keyMapper, Collectors.reducing(BigDecimal.ZERO, valueMapper, NumberUtil::addNumbers));
    }
    /**
     * 根据对象某个字段进行求和
     * @param mapper
     * @param <T>
     * @return
     */
    public static <T>
    Collector<T, ?, BigDecimal> sumByField(Function<? super T, ? extends BigDecimal> mapper) {
        return Collectors.reducing(BigDecimal.ZERO, mapper, NumberUtil::addNumbers);
    }
    /**
     * 求和
     */
    public static Collector<BigDecimal, ?, BigDecimal> sum() {
        return Collectors.reducing(BigDecimal.ZERO, NumberUtil::addNumbers);
    }
}
dilaksanakan dalam kombinasi dengan

 Map result = CollectionDataStream.of("t1", contractDetails)
                .joinUseHashOnEqualCondition(
                        contractInfos.stream().filter(it -> "60".equals(it.getStatus())).collect(Collectors.toList()),
                        "t2",
                        agg -> agg.getTableData("t1").get("contractNo"),
                        ContractInfo::getContractNo
                ).toStream("s1", ContractDetail.class)//将数据流转换为 java原生Stream
                .collect(MyCollectors.groupingAndSum(ContractDetail::getContractNo, ContractDetail::getMoneyTotal));
Pelaksanaan ini jelas lebih mudah, mengurangkan kebarangkalian ralat, mengurangkan jumlah kod dan bertambah baik kecekapan.

Kelebihan

  • Ia merealisasikan operasi sambungan antara koleksi, dan ia merupakan operasi penstriman, yang boleh menyambungkan berbilang koleksi secara berterusan dalam satu masa.

  • melaksanakan penukaran ke dan dari Strim. Pelbagai operasi kompleks boleh dilaksanakan menggunakan fungsi aliran, seperti penapisan, penukaran, pengelompokan, dsb.

  • Kecekapan dijamin pada tahap tertentu Pengoptimuman peta digunakan untuk cantuman yang setara, dan apabila cantuman dalam dibuat, jadual kecil dianggap disambungkan ke jadual besar untuk pengoptimuman. sesetengah kes mengurangkan Bilangan gelung, gunakan BeanMap di bawah cglib untuk mengurangkan penggunaan memori dan penggunaan prestasi apabila menukar kacang kepada data agregat baris

Atas ialah kandungan terperinci Cara menggunakan kelas alat Java untuk menulis laporan dengan cekap. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Artikel ini dikembalikan pada:yisu.com. Jika ada pelanggaran, sila hubungi admin@php.cn Padam