深浅模式
queryDsl 框架特性
1. 概述
QueryDsl 是一个基于 Java 的领域特定语言(DSL)框架,用于构建类型安全的 SQL 查询。
- QueryDsl 通过代码生成器生成查询实体,通过实体类的方法链式调用来构建查询条件,最终生成 SQL 语句。
- QueryDsl 满足了 Java 开发者对 SQL 查询的需求,提供了更加灵活、类型安全的查询方式,避免了 SQL 注入等问题。
- QueryDsl 生成的 SQL 语句满足多表关联、分页、排序、分组等复杂查询需求。
- QueryDsl 生成的 SQL 语句满足多类型数据库的查询需求,如 MySQL、Oracle、SQL Server 等。无需关注数据库方言之间的差异。
2. 查询的使用
java
// 引用 QueryDsl 类
import static com.galaxis.wms.query.QBasLoginAgent.basLoginAgent;
import static com.galaxis.wms.query.QBasStaff.basStaff;
import static com.galaxis.wms.query.QBasUser.basUser;
// 获取 master 数据源的 QueryDsl 对象
QueryDSL queryDsl = DaoFactory.getQueryDSL();
// 获取其他数据源的 QueryDsl 对象
QueryDSL queryDsl2 = DaoFactory.getQueryDSL("history");
// 单表单实体记录查询
BasLoginAgent loginAgent = queryDSL.select(basLoginAgent)
.from(basLoginAgent)
.where(basLoginAgent.authType.eq(CSysAuthType.PASSWORD))
.where(basLoginAgent.userId.eq(user.getUserId()))
.fetchFirst();
获取单个值
java
// 获取单个值
Long waveId = queryDSL.select(docAllocItems.waveId)
.from(docAllocItems)
.innerJoin(docAllocItemsRel)
.on(docAllocItemsRel.allocId.eq(docAllocItems.allocId))
.where(docAllocItems.isActive.eq(CSysYesNo.YES)
.and(docAllocItems.outStatus.eq(COutStatus.OUT_WAVEALLOC_OVER))
.and(docAllocItems.isRing.eq(CSysYesNo.YES))
.and(docAllocItemsRel.virtualSoId.eq(0L).or(docAllocItemsRel.ringId.eq(0L)))
)
.orderBy(docAllocItems.priority.asc())
.fetchFirst();
获取下划线命名风格的字段记录 Map
java
LinkedHashMap<String, ?> hashMap = queryDSL
.select(QueryDslUtils.linkedMap(
basLocation.locCode, basLocation.locId, basLocation.devicePos,
basLocation.floors, basWhArea.whAreaType, basWhArea.whAreaName,
basLocation.whId
))
.from(basLocation)
.innerJoin(basWhArea).on(basWhArea.whAreaId.eq(basLocation.whAreaId))
.where(basLocation.locCode.eq(locCode).or(basLocation.devicePos.eq(locCode)))
.fetchFirst();
String currWhAreaType = DbConv.asString(hashMap.get("wh_area_type"));
String currFloor = DbConv.asString(hashMap.get("floors"));
String currDevicePos = DbConv.asString(hashMap.get("device_pos"));
Long whId = DbConv.asLong(hashMap.get("wh_id"));
String currWhAreaName = DbConv.asString(hashMap.get("wh_area_name"));
获取单行实体
java
BasItem basItem = queryDsl.selectFrom(QBasItem.basItem)
.where(QBasItem.basItem.itemCode.eq(item.getItem()))
.where(QBasItem.basItem.ownerId.eq(ownerId))
.fetchFirst();
获取实体集合
java
List<BasPackage> basPackageList = queryDsl
.selectFrom(QBasPackage.basPackage)
.where(QBasPackage.basPackage.itemId.eq(basItem.getItemId()))
.fetch();
处理链式条件拼接
java
public static boolean function1() {
SQLQuery<LinkedHashMap<String, ?>> query = queryDSL.select(QueryDslUtils.linkedMap(docTaskItems, basPackage.isShuttle, basPackage.isConveyor,
docTask.containerCode, basPackageItems.maxSupplyNum, basItemRecall.itemRecallId,
docTaskItems.qty.subtract(docTaskItems.pickQty).subtract(docTaskItems.cancelQty).subtract(docTaskItems.shortQty).as("now_pick_qty")))
.from(docTaskItems)
.leftJoin(docTask).on(docTask.taskId.eq(docTaskItems.taskId))
.innerJoin(basPackage).on(basPackage.packId.eq(docTaskItems.packId))
.innerJoin(basPackageItems).on(basPackageItems.packId.eq(docTaskItems.packId).and(basPackageItems.packLevel.eq(docTaskItems.targetPackLevel)))
.innerJoin(invLotAtt).on(invLotAtt.lotId.eq(docTaskItems.lotId))
.leftJoin(basItemRecall).on(basItemRecall.itemId.eq(docTaskItems.itemId).and(basItemRecall.whId.eq(docTaskItems.whId))
.and(basItemRecall.ownerId.eq(docTaskItems.ownerId)).and(basItemRecall.lot06.eq(invLotAtt.lot06)))
.where(docTaskItems.outStatus.lt(COutStatus.OUT_PACK_OVER).and(docTaskItems.isActive.eq(CSysYesNo.YES)));
if (StringUtils.isNotBlankAndNA(barcode)) {
query.where(docTaskItems.lpn.eq(barcode));
}
if (null != taskItemsIdList && !taskItemsIdList.isEmpty()) {
query.where(docTaskItems.taskItemsId.in(taskItemsIdList));
}
}
更复杂的查询
java
public static boolean function1() {
queryDsl.select(QueryDslUtils.linkedMap(
docPutItems.putId,
docPutItems.qty.subtract(docPutItems.cancelQty).as("item_qty"),
docPutItems.qty.subtract(docPutItems.cancelQty).multiply(basPackageItems.packWeight).as("item_weight"),
docPutItems.qty.subtract(docPutItems.cancelQty).subtract(docInboundPacking.qty.sum()).as("no_packing_qty"),
docPutItems.qty.subtract(docPutItems.cancelQty).subtract(docInboundPacking.qty.sum()).multiply(basPackageItems.packWeight).as("no_packing_weight"),
docInboundPacking.qty.multiply(basPackageItems.packWeight).sum().as("packing_weight"),
docInboundPacking.qty.sum().as("packing_qty"),
docPutItems.qty.subtract(docPutItems.cancelQty).subtract(docInboundPacking.qty.sum()).as("qty"),
docPutItems.qty.subtract(docPutItems.cancelQty).subtract(docInboundPacking.qty.sum()).as("plan_qty"),
invLotAtt.lot09.as("manufacturer"))
)
.from(docPutItems)
.leftJoin(docInboundPacking).on(
docPutItems.putId.eq(docInboundPacking.putId)
.and(docPutItems.putLineNo.eq(docInboundPacking.putLineNo))
.and(docInboundPacking.packingStatus.lt(CPackingStatus.PACK_OVER))
.and(docInboundPacking.packingType.eq(packingType))
)
.innerJoin(basItem).on(basItem.itemId.eq(docPutItems.itemId))
.innerJoin(basPackage).on(basPackage.packId.eq(docPutItems.packId))
.innerJoin(invLotAtt).on(invLotAtt.lotId.eq(docPutItems.lotId))
.innerJoin(basPackageItems).on(basPackageItems.packId.eq(basPackage.packId).and(basPackageItems.packLevel.eq(CPackLevel.BOX_PACK)))
.where(docPutItems.barcode.eq(barcode).and(docPutItems.inStatus.lt(CInStatus.IN_PACKING_OVER)))
.groupBy(docPutItems.putId, docPutItems.putLineNo, basItem.itemCode, basItem.itemName, basItem.commonName,
docPutItems.qty, docPutItems.cancelQty, basPackage.specification, invLotAtt.lot09, basPackage.producingArea,
invLotAtt, basItem.itemCategory, basItem.itemClassify, basPackageItems.packUnit, basPackageItems.packMeas, basPackageItems.packWeight,
basPackageItems.packWeightRatio, docPutItems.qty, docPutItems.cancelQty, basPackageItems.packWeight, basPackage.isCatchWeight)
.fetch();
}
请注意
过于复杂的 QueryDSL 虽然看起来很强大,但是会让代码变得难以维护。过于复杂的情况,请使用 MyBatis 进行查询。
2. 删除 / 更新 / 插入的使用
基本使用
java
import com.yvan.data.jdbc.QueryDSL;
import com.galaxis.wms.query.QBasPackage;
import static com.galaxis.wms.query.QDocTask.docTask;
import static com.galaxis.wms.query.QDocTaskItems.docTaskItems;
public static void function1() {
QueryDSL queryDSL = DaoFactory.getQueryDSL();
queryDSL.update(docTask).set(docTask.isNotShow, CSysYesNo.YES)
.where(docTask.taskGroup.eq(taskGroup)).execute();
queryDSL.update(docTaskItems)
.set(docTaskItems.outStatus, COutStatus.OUT_PICK_ING)
.where(docTaskItems.taskId.in(taskIds).and(docTaskItems.isActive.eq(CSysYesNo.YES)))
.execute();
// populate 方法可以将实体类的属性值填充到 SQL 语句中
BasPackage basPackage = new BasPackage();
basPackage.setPackId(daoMain.snowId("PACK_ID"));
basPackage.setItemId(basItem.getItemId());
basPackage.setIsEnable(CSysYesNo.YES);
basPackage.setSpecification(itemSpec);
basPackage.setUpdateAt(DateUtils.toTimestamp(new Date()));
basPackage.setIsDefault(CSysYesNo.YES);
queryDsl.insert(QBasPackage.basPackage)
.populate(basPackage)
.execute();
}
批量插入
QueryDSL 的批量插入使用了 JDBC 的批量执行特性, 在语句过长时还会自动分批执行,可以大幅度提升更新效率。
java
public static void function1() {
SQLInsertClause insert = queryDSL.insert(docTask);
SQLUpdateClause update = queryDSL.update(docTaskItems);
for (Map.Entry<String, List<Map<String, Object>>> entry : map.entrySet()) {
insert.set(docTask.taskId, taskId)
.set(docTask.taskCode, dao.nextCode("TASK_CODE"))
.set(docTask.outStatus, COutStatus.OUT_WAVEPACKAGE_OVER)
.set(docTask.pickFrom, pickFrom)
.addBatch();
update.set(docTaskItems.taskId, taskId)
.set(docTaskItems.packageSn, packageSn)
.where(docTaskItems.taskItemsId.eq(DbConv.asLong(value.get("task_items_id"))))
.addBatch();
}
if (insert.getBatchCount() > 0) {
insert.execute();
}
if (update.getBatchCount() > 0) {
update.execute();
}
}
从 map 或其他 bean 中填充数据
这个技巧很重要
从界面下划线规则,到数据库驼峰规则,或者从数据库下划线规则到界面驼峰规则,都需要进行转换。 得到 entity 实体之后,再借助 QueryDSL 的 populate 方法,将实体类的属性值填充到 SQL 语句中。
java
import org.springframework.beans.BeanUtils;
import com.yvan.core.NamingUtils;
import com.yvan.core.mapper.BeanCopyUtils;
import static com.galaxis.wms.query.QDocAsn.docAsn;
public static void function1(DocAllocItems originItem) {
DocAllocItems newAllocItem = new DocAllocItems();
// 从 originItem 中复制属性到 newAllocItem 中
BeanUtils.copyProperties(originItem, newAllocItem);
newAllocItem.setAllocId(9L);
newAllocItem.setQty(1L);
queryDsl.insert(QDocAllocItems.docAllocItems)
.populate(newAllocItem)
.execute();
// 将 asnMap 中下划线 key 转换为驼峰规则,并得到一个新的 asnMapRenamed 对象
Map<String, Object> asnMapRenamed = NamingUtils.underlineToCamel(asnMap);
DocAsn docAsnEntity = BeanCopyUtils.toBean(asnMapRenamed, DocAsn.class);
queryDsl.insert(docAsn)
.populate(docAsnEntity)
.execute();
}