Mybatis foreach 填充 Merge into

合并操作与foreach遍历:Mybatis MERGEINTO与foreach实战
本文详细解读了Mybatis中的MERGEINTO语句在数据库操作中的应用,包括语法解析和实际案例。同时介绍了foreach标签在处理集合数据时的用法,通过实例展示了如何利用foreach遍历List并将数据插入或更新到目标表中。

一、merge into语句的语法以及解析

MERGE INTO schema.table alias
USING { schema. table | views | query} alias
ON {(condition) }
WHEN MATCHED THEN
  UPDATE SET {clause}
WHEN NOT MATCHED THEN
  INSERT VALUES {clause};

参数介绍:

INTO 子句
        用于指定你所update或者Insert目的表。
USING 子句
        用于指定你要update或者Insert的记录的来源,它可能是一个表,视图,子查询。
ON Clause
        用于目的表和源表(视图,子查询)的关联,如果匹配(或存在),则更新,否则插入。
merge_update_clause 
        用于写update语句
merge_insert_clause
        用于写insert语句

二、Mybatis foreach的使用

<foreach item="item" index="index" collection="list|array|map key" open="(" separator="," close=")">

参数值的使用

</foreach>

foreach 标签主要有以下属性,说明如下。

  1. item:表示集合中每一个元素进行迭代时的别名。
  2. index:指定一个名字,表示在迭代过程中每次迭代到的位置。
  3. open:表示该语句以什么开始(既然是 in 条件语句,所以必然以(开始)。
  4. separator:表示在每次进行迭代之间以什么符号作为分隔符(既然是 in 条件语句,所以必然以,作为分隔符)。
  5. close:表示该语句以什么结束(既然是 in 条件语句,所以必然以)开始)。


collection 属性,是必选的,主要有以下 3 种情况:

  1. 如果传入的是单参数且参数类型是一个 List,collection 属性值为 list。
  2. 如果传入的是单参数且参数类型是一个 array 数组,collection 的属性值为 array。
  3. 如果传入的参数是多个,需要把它们封装成一个 Map,当然单参数也可以封装成 Map。Map 的 key 是参数名,collection 属性值是传入的 List 或 array 对象在自己封装的 Map 中的 key。

三、demo

数据来源是一个List<Object>,先将所有数据加载到一个临时表dual中,再去与目标表进行主键比较;主键冲突则更新,否则插入;

<insert id="saveTableList" parameterType="com.example.test.beans">
	MERGE INTO TABLENAME A USING (
	<foreach collection="paramList" item="item" separator="union all"
		SELECT 
			#{item.paramName} AS PARAM_NAME,
			#{item.paramValue} AS PARAM_VALUE,
			#{item. paramType} AS PARAM_TYPE 
			FRON dual B
	</foreach>
	)C 
	ON (A.PARAM_NAME = C.PARAM_NAME AND A.PARAM_TYPE = C.PARAM_TYPE)
	WHEN MATCHED THEN
		UPDATE
			SET A.PARAM_VALUE= C.PARAM_VALUE
	WHEN NOT MATCHED THEN
		INSERT 
			(A.PARAM_NAME,A.PARAM_VALUE,A.PARAM_TYPE )
		VALUES
			(C.PARAM_NAME,C.PARAM_VALUE,C.PARAM_TYPE )
</insert>
package com.cmb.lul.lulm.service.data.sink; import com.cmb.dtpframework.exception.AppException; import com.cmb.dtpframework.utils.SpringContextHolder; import com.cmb.dtpframework.utils.StringUtil; import com.cmb.lp.za20.util.BaseLayerFacade; import com.cmb.lul.lulm.service.data.cache.metadata.ColumnMetadata; import com.cmb.lul.lulm.service.data.cache.metadata.JdbcMetadataManager; import com.cmb.lul.lulm.service.data.cache.metadata.TableMetadata; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import java.math.BigDecimal; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.Types; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; public abstract class AbstractTableSink implements TableSink { protected final String sinkDbName; protected final String sinkSchemaName; protected final String sinkTableName; protected final TableMetadata sinkDsTableMetadata; protected final String indexName; protected final List<Map<String, String>> datas; protected final List<String> dataColumnNames; protected final List<String> primaryKeys; protected final List<String> updateColumnNames; protected long connectCostMs = 0L; protected long sinkCostMs = 0L; public AbstractTableSink(String sinkSchemaName, String sinkTableName, String indexName, List<String> primaryKeys, List<Map<String, String>> datas) { this.sinkDbName = sinkSchemaName; this.sinkSchemaName = sinkSchemaName; this.sinkTableName = sinkTableName; this.indexName = indexName; this.primaryKeys = primaryKeys; this.sinkDsTableMetadata = BaseLayerFacade.getSingleton(JdbcMetadataManager.class).getTableMetadata(sinkSchemaName, sinkTableName); this.datas = datas; this.dataColumnNames = getAndSortDataColumnNames(sinkDsTableMetadata, datas); this.updateColumnNames = dataColumnNames.stream().filter(column -> !primaryKeys.contains(column)).collect(Collectors.toList()); } public AbstractTableSink(String sinkSchemaName, String sinkTableName, List<String> primaryKeys, List<Map<String, String>> datas) { this(sinkSchemaName, sinkTableName, null, primaryKeys, datas); } protected List<String> getAndSortDataColumnNames(TableMetadata sinkDsTableMetadata, List<Map<String, String>> datas) { // 用源表data数据确定要写哪些字段 Set<String> tempDataColumnNamesSet = new HashSet<>(16); datas.forEach(data -> tempDataColumnNamesSet.addAll(data.keySet())); // 用目标表metadata重排列字段顺序 List<String> tempDataColumnNamesList = new ArrayList<>(tempDataColumnNamesSet.size()); sinkDsTableMetadata.getColumnMetadatas().keySet().forEach(metadataColumnName -> { if (tempDataColumnNamesSet.contains(metadataColumnName)) { tempDataColumnNamesList.add(metadataColumnName); tempDataColumnNamesSet.remove(metadataColumnName); } }); // 源表data中存在,但目标表metadata中不存在的字段放到最后(这部分写的时候会报错,表明目标表字段比源表少) tempDataColumnNamesList.addAll(tempDataColumnNamesSet); return tempDataColumnNamesList; } @Override public void doSink() { if (!datas.isEmpty()) { /* 构建sql语句 */ String sql = buildSql(); SqlSession session = null; try { long beforeConnectTime = System.currentTimeMillis(); session = SpringContextHolder.getBean(SqlSessionFactory.class).openSession(); Connection connection = session.getConnection(); long afterConnectTime = System.currentTimeMillis(); try (PreparedStatement preparedStatement = connection.prepareStatement(sql)) { /* 设置参数 */ for (Map<String, String> data : datas) { // 设置参数 setPreparedStatementParameter(preparedStatement, data); preparedStatement.addBatch(); } int[] results = preparedStatement.executeBatch(); } /* 事务提交 */ session.commit(true); long afterSinkTime = System.currentTimeMillis(); connectCostMs = afterConnectTime - beforeConnectTime; sinkCostMs = afterSinkTime - afterConnectTime; } catch (Exception e) { if (null != session) { /* 事务回滚 */ session.rollback(true); } throw new AppException(e, "LULM605", sinkDbName, sinkTableName, sql, e.getMessage()); } finally { if (null != session) { session.close(); } } } } protected String buildSql() { StringBuilder sql = new StringBuilder(); sql.append("MERGE "); if (indexName != null) { sql.append("/*+INDEX(").append(sinkTableName).append(" ").append(indexName).append(") */ "); } sql.append("INTO ").append(buildTableName()).append(" USING DUAL ON \n") .append("(").append(buildWhereCondition()).append(") \n") .append(" WHEN MATCHED THEN UPDATE SET \n") .append(buildOnDuplicateKeyUpdateStatement()).append(" \n") .append(" WHEN NOT MATCHED THEN INSERT \n") .append("(").append(buildColumnNamesSpiltByComma()).append(") \n") .append("values (").append(buildPlaceHoldersSpiltByComma()).append(")"); return sql.toString(); } protected String buildWhereCondition() { StringBuilder result = new StringBuilder(); for (int i = 0; i < primaryKeys.size(); i++) { String primaryKey = primaryKeys.get(i); result.append(primaryKeys.get(i)); result.append((i < primaryKeys.size() - 1) ? " = ? AND " : " = ?"); } return result.toString(); } protected String buildTableName() { StringBuilder tableName = new StringBuilder(); tableName.append(sinkSchemaName).append(".") .append(sinkTableName); return tableName.toString(); } protected String buildColumnNamesSpiltByComma() { StringBuilder columnNamesSpiltByComma = new StringBuilder(); for (String columnName : dataColumnNames) { columnNamesSpiltByComma.append(columnName).append(","); } if (columnNamesSpiltByComma.length() >= 1) { columnNamesSpiltByComma.deleteCharAt(columnNamesSpiltByComma.length() - 1); } return columnNamesSpiltByComma.toString(); } protected String buildPlaceHoldersSpiltByComma() { StringBuilder placeHolders = new StringBuilder(); for (int i = 0; i < dataColumnNames.size(); i++) { placeHolders.append("?,"); } if (placeHolders.length() >= 1) { placeHolders.deleteCharAt(placeHolders.length() - 1); } return placeHolders.toString(); } protected String buildOnDuplicateKeyUpdateStatement() { StringBuilder updateStatement = new StringBuilder(); for (String columnName : updateColumnNames) { updateStatement.append(columnName).append(" = ?,"); } if (updateStatement.length() >= 1) { updateStatement.deleteCharAt(updateStatement.length() - 1); } return updateStatement.toString(); } protected void setPreparedStatementParameter(PreparedStatement preparedStatement, Map<String, String> data) { // 设置参数 // 通过binlog字段找metadata,找不到时getDsTableColumnMetadata(columnName)会报错,以此确保所有binlog数据字段都被写入目标表 int i = 1; for (String primaryKey : primaryKeys) { setOneColumn(preparedStatement, i, primaryKey, data, true); i++; } for (String columnName : updateColumnNames) { setOneColumn(preparedStatement, i, columnName, data, false); i++; } for (String columnName : dataColumnNames) { setOneColumn(preparedStatement, i, columnName, data, false); i++; } } protected void setOneColumn(PreparedStatement preparedStatement, int index, String columnName, Map<String, String> data, boolean format) { String columnValue = data.get(columnName); int columnDataType = -1; int columnSize = 0; try { ColumnMetadata metadata = sinkDsTableMetadata.getColumnMetadata(columnName); if (metadata == null) { throw new IllegalArgumentException("目标表不存在该字段!"); } columnDataType = metadata.getDataType(); columnSize = metadata.getColumnSize(); preparedStatement.setObject(index, transferData(columnName, columnDataType, columnValue, columnSize, format), columnDataType); } catch (Exception e) { throw new AppException(e, "LULM606", sinkDbName, sinkTableName, columnName, columnDataType, columnValue, columnValue.getClass().getName(), e.getMessage()); } } private Object transferData(String columnName, int columnType, String columnValue, int columnSize, boolean format) { switch (columnType) { case Types.CHAR: if (format) { if (columnValue == null) { columnValue = ""; } return columnValue.length() < columnSize ? String.format("%-" + columnSize + "s", columnValue) : columnValue; } return checkEmpty(columnValue) ? " " : columnValue; case Types.VARCHAR: case Types.LONGVARCHAR: return checkEmpty(columnValue) ? " " : columnValue; case Types.SMALLINT: return checkEmpty(columnValue) ? 0 : Short.parseShort(columnValue); case Types.INTEGER: return checkEmpty(columnValue) ? 0 : Integer.parseInt(columnValue); case Types.BIGINT: return checkEmpty(columnValue) ? 0 : Long.parseLong(columnValue); case Types.FLOAT: case Types.DOUBLE: return checkEmpty(columnValue) ? 0 : Double.parseDouble(columnValue); case Types.NUMERIC: case Types.DECIMAL: return checkEmpty(columnValue) ? BigDecimal.ZERO : new BigDecimal(columnValue); case Types.DATE: case Types.TIMESTAMP: case Types.TIME: return checkEmpty(columnValue) ? 0 : columnValue; default: throw new AppException("LULM603", sinkDbName, sinkSchemaName, sinkTableName, columnName, columnType); } } private boolean checkEmpty(Object value) { return value == null || (value instanceof String && StringUtil.isBlank((String)value)); } } 请你给我详细分析解释一下这段代码
07-22
package com.hvlink.service.impl; import cn.hutool.core.collection.CollUtil; import com.alibaba.excel.EasyExcel; import com.alibaba.excel.write.merge.LoopMergeStrategy; import com.alibaba.excel.write.metadata.style.WriteCellStyle; import com.alibaba.excel.write.style.HorizontalCellStyleStrategy; import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.hvlink.common.Result; import com.hvlink.entity.dto.order.CompositeKeyDTO; import com.hvlink.entity.dto.order.PurchaseOrderDetailsDTO; import com.hvlink.entity.dto.order.PurchaseOrderMainDTO; import com.hvlink.entity.param.order.PurchaseOrderParam; import com.hvlink.entity.po.master.CompanyPO; import com.hvlink.entity.po.master.FactoryPO; import com.hvlink.entity.po.master.PartPO; import com.hvlink.entity.po.order.TbOrderMainPO; import com.hvlink.entity.vo.order.PurchaseOrderDetailsVO; import com.hvlink.entity.vo.order.PurchaseOrderExportVO; import com.hvlink.entity.vo.order.PurchaseOrderMainVO; import com.hvlink.enums.PurchaseOrderPlanTypeEnum; import com.hvlink.enums.PurchaseOrderTypeEnum; import com.hvlink.mapper.master.CompanyMapper; import com.hvlink.mapper.master.FactoryMapper; import com.hvlink.mapper.master.PartMapper; import com.hvlink.mapper.order.TbOrderDetailMapper; import com.hvlink.mapper.order.TbOrderMainMapper; import com.hvlink.pagination.PageResult; import com.hvlink.service.IPurchaseOrderService; import com.hvlink.utils.BeanCopyUtils; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.poi.ss.usermodel.HorizontalAlignment; import org.springframework.stereotype.Service; import javax.servlet.http.HttpServletResponse; import java.net.URLEncoder; import java.text.SimpleDateFormat; import java.util.*; import java.util.function.BinaryOperator; import java.util.function.Function; import java.util.stream.Collectors; @Service @RequiredArgsConstructor @Slf4j public class PurchaseOrderServiceImpl extends ServiceImpl<TbOrderMainMapper, TbOrderMainPO> implements IPurchaseOrderService { private final TbOrderMainMapper orderMainMapper; private final TbOrderDetailMapper orderDetailMapper; private final PartMapper partMapper; private final CompanyMapper companyMapper; private final FactoryMapper factoryMapper; @Override public PageResult<PurchaseOrderMainVO> queryPage(PurchaseOrderParam purchaseOrderParam) { // 查询主表数据 Page<PurchaseOrderMainDTO> page = new Page<>(purchaseOrderParam.getPageIndex(), purchaseOrderParam.getPageSize()); Page<PurchaseOrderMainDTO> mainPage = orderMainMapper.queryPurchaseOrderPage(page, purchaseOrderParam); if (CollectionUtils.isEmpty(mainPage.getRecords())) { return null; } // 构建复合键列表用于查询明细 List<CompositeKeyDTO> compositeKeys = mainPage.getRecords().stream() .map(main -> new CompositeKeyDTO( main.getPurchaseOrderNo(), main.getCompanyCode(), main.getSerialVersionNum() )) .collect(Collectors.toList()); // 查询明细数据 List<PurchaseOrderDetailsDTO> detailList = orderDetailMapper.queryDetailsByCompositeKeys(compositeKeys); // 按复合键分组明细数据 Map<String, List<PurchaseOrderDetailsDTO>> detailMap = detailList.stream() .collect(Collectors.groupingBy(detail -> detail.getPurchaseOrderNo() + "#" + detail.getCompanyCode() + "#" + detail.getSerialVersionNum() )); // 转换主表数据到VO List<PurchaseOrderMainVO> mainVOList = mainPage.getRecords().stream().map(mainDTO -> { PurchaseOrderMainVO mainVO = BeanCopyUtils.copyBean(mainDTO, PurchaseOrderMainVO.class); mainVO.setSpliceSupplierName(mainDTO.getSupplierCode() + " - " + mainDTO.getSupplierName()); mainVO.setSpliceFactoryName(mainDTO.getFactoryCode() + " - " + mainDTO.getFactoryName()); mainVO.setSpliceWarehouseName(mainDTO.getWarehouseCode() + " - " + mainDTO.getWarehouseName()); // 设置订单类型描述 if (mainDTO.getOrderType() != null) { PurchaseOrderTypeEnum typeEnum = Arrays.stream(PurchaseOrderTypeEnum.values()) .filter(e -> e.getTypeCode().equals(mainDTO.getOrderType())) .findFirst() .orElse(null); if (typeEnum != null) { mainVO.setType(typeEnum.getTypeCode()); } } // 设置明细数据 - 使用复合键 String compositeKey = mainDTO.getPurchaseOrderNo() + "#" + mainDTO.getCompanyCode() + "#" + mainDTO.getSerialVersionNum(); List<PurchaseOrderDetailsVO> detailVOList = new ArrayList<>(); if (detailMap.containsKey(compositeKey)) { detailVOList = detailMap.get(compositeKey).stream().map(detail -> { PurchaseOrderDetailsVO detailVO = BeanCopyUtils.copyBean(detail, PurchaseOrderDetailsVO.class); // 设置类型名称 if (detail.getType() != null) { PurchaseOrderPlanTypeEnum planTypeEnum = Arrays.stream(PurchaseOrderPlanTypeEnum.values()) .filter(e -> e.getTypeCode().equals(detail.getType())) .findFirst() .orElse(null); if (planTypeEnum != null) { detailVO.setTypeName(planTypeEnum.getTypeDesc()); } } return detailVO; }).collect(Collectors.toList()); } mainVO.setDetailsList(detailVOList); return mainVO; }).collect(Collectors.toList()); // 构建分页结果 PageResult<PurchaseOrderMainVO> result = new PageResult<>(); result.setRecords(mainVOList); result.setTotal(mainPage.getTotal()); result.setPageIndex(purchaseOrderParam.getPageIndex()); result.setPageSize(purchaseOrderParam.getPageSize()); return result; } @Override public void exportPurchaseOrderData(PurchaseOrderParam exportQuery, HttpServletResponse response) { // 获取所有零件代码 Map<String, PartPO> partMap = partMapper.selectList(Wrappers.emptyWrapper()).stream().collect(Collectors.toMap(PartPO::getPartCode, Function.identity(), BinaryOperator.maxBy(Comparator.comparing(PartPO::getCreateTime)))); // 获取所有公司 Map<String, CompanyPO> companyMap = companyMapper.selectList(Wrappers.emptyWrapper()).stream().collect(Collectors.toMap(CompanyPO::getCompanyCode, Function.identity(), BinaryOperator.maxBy(Comparator.comparing(CompanyPO::getCreateTime)))); // 获取所有工厂 Map<String, FactoryPO> factoryMap = factoryMapper.selectList(Wrappers.emptyWrapper()).stream().collect(Collectors.toMap(FactoryPO::getFactoryCode, Function.identity(),BinaryOperator.maxBy(Comparator.comparing(FactoryPO::getCreateTime)))); try { // 查询所有符合条件的数据(不分页) Page<PurchaseOrderMainDTO> page = new Page<>(1, 9999999, true); Page<PurchaseOrderMainDTO> mainPage = orderMainMapper.queryPurchaseOrderPage(page, exportQuery); if (CollectionUtils.isEmpty(mainPage.getRecords())) { log.warn("没有找到符合条件的采购订单数据"); return; } // 构建复合键列表用于查询明细 List<CompositeKeyDTO> compositeKeys = mainPage.getRecords().stream() .map(main -> new CompositeKeyDTO( main.getPurchaseOrderNo(), main.getCompanyCode(), main.getSerialVersionNum() )) .collect(Collectors.toList()); // 查询明细数据 List<PurchaseOrderDetailsDTO> detailList = orderDetailMapper.queryDetailsByCompositeKeys(compositeKeys); // 按复合键分组明细数据 Map<String, List<PurchaseOrderDetailsDTO>> detailMap = detailList.stream() .collect(Collectors.groupingBy(detail -> detail.getPurchaseOrderNo() + "#" + detail.getCompanyCode() + "#" + detail.getSerialVersionNum() )); // 准备导出数据 List<PurchaseOrderExportVO> exportList = new ArrayList<>(); // 用于记录合并信息:key为采购订单号+版本号,value为明细行数 Map<String, Integer> mergeInfoMap = new HashMap<>(); for (PurchaseOrderMainDTO mainDTO : mainPage.getRecords()) { String compositeKey = mainDTO.getPurchaseOrderNo() + "#" + mainDTO.getCompanyCode() + "#" + mainDTO.getSerialVersionNum(); List<PurchaseOrderDetailsDTO> details = detailMap.getOrDefault(compositeKey, new ArrayList<>()); // 记录合并信息 String mergeKey = mainDTO.getPurchaseOrderNo() + "#" + mainDTO.getSerialVersionNum(); mergeInfoMap.put(mergeKey, details.size()); // 设置类型描述 String orderTypeDesc = ""; if (mainDTO.getOrderType() != null) { PurchaseOrderTypeEnum typeEnum = Arrays.stream(PurchaseOrderTypeEnum.values()) .filter(e -> e.getTypeCode().equals(mainDTO.getOrderType())) .findFirst() .orElse(null); if (typeEnum != null) { orderTypeDesc = typeEnum.getTypeDesc(); } } for (PurchaseOrderDetailsDTO detail : details) { PurchaseOrderExportVO exportVO = new PurchaseOrderExportVO(); // 主表信息 exportVO.setPublishDate(mainDTO.getPublishDate()); exportVO.setPurchaseOrderNo(mainDTO.getPurchaseOrderNo()); exportVO.setSupplier(mainDTO.getSupplierCode() + " - " + mainDTO.getSupplierName()); exportVO.setFactory(mainDTO.getFactoryCode() + " - " + mainDTO.getFactoryName()); exportVO.setWarehouse(mainDTO.getWarehouseCode() + " - " + mainDTO.getWarehouseName()); exportVO.setSerialVersionNum(mainDTO.getSerialVersionNum()); // 明细信息 exportVO.setLineNo(detail.getLineNo()); exportVO.setDeliveryDate(detail.getDeliveryDate()); exportVO.setPartCode(detail.getPartCode()); exportVO.setPartDesc(detail.getPartDesc()); exportVO.setSupplierPartCode(detail.getSupplierPartCode()); exportVO.setUnit(detail.getUnit()); exportVO.setTotalQty(detail.getTotalQty()); exportVO.setRequiredQty(detail.getRequiredQty()); exportVO.setAvailableQty(detail.getAvailableQty()); exportVO.setShippedQty(detail.getShippedQty()); exportVO.setType(detail.getType()); exportVO.setFrequency(detail.getFrequency()); // 设置类型名称 if (detail.getType() != null) { PurchaseOrderPlanTypeEnum planTypeEnum = Arrays.stream(PurchaseOrderPlanTypeEnum.values()) .filter(e -> e.getTypeCode().equals(detail.getType())) .findFirst() .orElse(null); if (planTypeEnum != null) { exportVO.setType(planTypeEnum.getTypeDesc()); } } exportList.add(exportVO); } } // 设置响应头 SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss"); String fileName = "采购订单数据_" + sdf.format(new Date()) + ".xlsx"; response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8")); // 创建单元格样式 WriteCellStyle headWriteCellStyle = new WriteCellStyle(); headWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER); WriteCellStyle contentWriteCellStyle = new WriteCellStyle(); contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.LEFT); // 创建合并策略 HorizontalCellStyleStrategy styleStrategy = new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle); // 自定义合并策略 - 合并相同订单号+版本号的主表信息 LoopMergeStrategy mergeStrategy = new LoopMergeStrategy(1, 0) { private final Map<String, Integer> mergeMap = mergeInfoMap; private int currentIndex = 0; protected boolean needMerge(int rowIndex, int columnIndex) { if (columnIndex < 6) { // 只合并前6列(主表信息列) // 这里需要根据实际数据计算合并范围 // 简化实现:根据mergeInfoMap来确定合并范围 return true; } return false; } protected int getMergeRange(int rowIndex, int columnIndex) { // 实际实现需要根据数据来计算合并行数 // 这里返回简化实现 return 1; } }; // 导出Excel EasyExcel.write(response.getOutputStream()) .registerWriteHandler(styleStrategy) .registerWriteHandler(mergeStrategy) .head(PurchaseOrderExportVO.class) .sheet("采购订单数据") .doWrite(exportList); } catch (Exception e) { String errorMessage = "导出采购订单数据出现异常,具体信息是:" + e.getMessage(); log.error(errorMessage, e); throw new RuntimeException(errorMessage); } } } <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.hvlink.mapper.order.TbOrderMainMapper"> <!-- 其他 insert/update 不变 --> <insert id="batchInsert"> INSERT INTO tb_order_main( company_code, purchase_order_no, publish_date, supplier_code, factory_code, warehouse_code, create_time, serial_version_num, order_type ) VALUES <foreach collection="list" item="item" separator=","> ( #{item.companyCode}, #{item.purchaseOrderNo}, #{item.publishDate}, #{item.supplierCode}, #{item.factoryCode}, #{item.warehouseCode}, GETDATE(), #{item.serialVersionNum}, #{item.orderType} ) </foreach> </insert> <update id="batchUpdate"> UPDATE tb_order_main SET update_time = GETDATE(), publish_date = <foreach collection="list" item="item" index="index" separator=" " open="CASE ID" close="END"> WHEN #{item.id} THEN #{item.publishDate} </foreach>, serial_version_num = <foreach collection="list" item="item" index="index" separator=" " open="CASE ID" close="END"> WHEN #{item.id} THEN #{item.serialVersionNum} </foreach> WHERE id IN <foreach collection="list" item="item" open="(" close=")" separator=","> #{item.id} </foreach> </update> <!-- 主查询:采购订单分页,去除 purchaseOrderNo 重复数据,只保留最新版本 --> <select id="queryPurchaseOrderPage" resultType="com.hvlink.entity.dto.order.PurchaseOrderMainDTO"> SELECT m.id, m.company_code, m.purchase_order_no, m.publish_date, s.supplier_code, s.supplier_name, f.factory_code, f.factory_name, w.warehouse_code, w.warehouse_name, m.serial_version_num, m.order_type FROM ( SELECT *, ROW_NUMBER() OVER (PARTITION BY purchase_order_no, company_code ORDER BY serial_version_num DESC) as rn FROM tb_order_main WHERE is_deleted = 0 ) m LEFT JOIN tm_supplier s ON m.supplier_code = s.supplier_code AND m.company_code = s.company_code LEFT JOIN tm_factory f ON m.factory_code = f.factory_code AND m.company_code = f.company_code LEFT JOIN tm_warehouse w ON m.warehouse_code = w.warehouse_code AND m.company_code = w.company_code <where> m.rn = 1 <!-- 只取每个订单的最新版本 --> <!-- 优化后的模糊匹配组 --> <if test="param.supplierName != null and param.supplierName != '' or param.supplierCode != null and param.supplierCode != '' or param.factoryName != null and param.factoryName != '' or param.factoryCode != null and param.factoryCode != '' or param.warehouseName != null and param.warehouseName != '' or param.warehouseCode != null and param.warehouseCode != ''"> AND ( <trim prefixOverrides="OR"> <!-- 供应商条件 --> <if test="param.supplierName != null and param.supplierName != ''"> OR s.supplier_name LIKE '%' + #{param.supplierName} + '%' </if> <if test="param.supplierCode != null and param.supplierCode != ''"> OR s.supplier_code LIKE '%' + #{param.supplierCode} + '%' </if> <!-- 工厂条件 --> <if test="param.factoryName != null and param.factoryName != ''"> OR f.factory_name LIKE '%' + #{param.factoryName} + '%' </if> <if test="param.factoryCode != null and param.factoryCode != ''"> OR f.factory_code LIKE '%' + #{param.factoryCode} + '%' </if> <!-- 仓库条件 --> <if test="param.warehouseName != null and param.warehouseName != ''"> OR w.warehouse_name LIKE '%' + #{param.warehouseName} + '%' </if> <if test="param.warehouseCode != null and param.warehouseCode != ''"> OR w.warehouse_code LIKE '%' + #{param.warehouseCode} + '%' </if> </trim> ) </if> <!-- 订单号模糊查询 --> <if test="param.purchaseOrderNo != null and param.purchaseOrderNo != ''"> AND m.purchase_order_no LIKE '%' + #{param.purchaseOrderNo} + '%' </if> <if test="param.startDate != null"> AND m.publish_date >= #{param.startDate} </if> <if test="param.endDate != null"> AND m.publish_date <= #{param.endDate} </if> <!-- 订单类型 --> <if test="param.orderType != null"> AND m.order_type = #{param.orderType} </if> <!-- 明细字段关联查询 - 使用EXISTS优化性能 --> <if test="param.partCode != null and param.partCode != ''"> AND EXISTS ( SELECT 1 FROM tb_order_detail d WHERE d.purchase_order_no = m.purchase_order_no AND d.company_code = m.company_code AND d.serial_version_num = m.serial_version_num AND d.part_code LIKE '%' + #{param.partCode} + '%' ) </if> <if test="param.partDesc != null and param.partDesc != ''"> AND EXISTS ( SELECT 1 FROM tb_order_detail d WHERE d.purchase_order_no = m.purchase_order_no AND d.company_code = m.company_code AND d.serial_version_num = m.serial_version_num AND d.part_desc LIKE '%' + #{param.partDesc} + '%' ) </if> <if test="param.frequency != null and param.frequency != ''"> AND EXISTS ( SELECT 1 FROM tb_order_detail d WHERE d.purchase_order_no = m.purchase_order_no AND d.company_code = m.company_code AND d.serial_version_num = m.serial_version_num AND d.frequency = #{param.frequency} ) </if> </where> ORDER BY m.create_time DESC </select> </mapper> 我现在需要根据前端选择哪些数据就导出哪些数据,比如前端展示的有10行数据,我选中了3,6,8行数据,那么再点击导出,就只导出选中的这三条,请帮我修改代码,参数里已经添加了private List<String> selectedOrderIds;这个ID集合
最新发布
09-23
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值