SEGW中如何正确实现OData的$expand查询?
解决OData多对多关系$expand查询无输出问题
问题场景
在SEGW中为OData服务实现$expand查询选项,UsersSet与ProductsSet为多对多(m:n)关系,通过中间表UsersProducts的userid和productid字段关联。期望通过/UsersSet?$expand=ToProducts返回每个用户关联的所有产品,但网关测试无输出,调试发现er_entityset内容与SQL构建的深度结构不匹配,且read_entityset等方法也会执行。
现有实现代码
MPC_EXT类中定义深度实体类型
types: BEGIN OF TY_USERPRODUCTS , INCLUDE TYPE ZCL_ZALM_FS_APP_MPC_EXT=>ts_users, toproducts TYPE STANDARD TABLE OF ZCL_ZALM_FS_APP_MPC_EXT=>ts_products WITH DEFAULT KEY, END OF TY_USERPRODUCTS. types: TT_USERPRODUCTS TYPE STANDARD TABLE OF TY_USERPRODUCTS.
DPC_EXT类中重写GET_EXPANDED_ENTITYSET方法
method /IWBEP/IF_MGW_APPL_SRV_RUNTIME~GET_EXPANDED_ENTITYSET. DATA : lt_deep_userProd TYPE TABLE OF zcl_zalm_fs_app_MPC_EXT=>ty_userproducts, "Deep Entity Type ls_deep_userProd TYPE zcl_zalm_fs_app_MPC_EXT=>ty_userproducts, "Deep Entity Type ls_product TYPE zcl_zalm_fs_app_MPC_EXT=>ts_products, ls_reluprod TYPE zcl_zalm_fs_app_mpc_ext=>ts_reltableusersproducts, lt_products TYPE TABLE OF zcl_zalm_fs_app_MPC_EXT=>ts_products. DATA(lv_expand) = io_tech_request_context->get_expand( ). DATA(lv_top) = io_tech_request_context->get_top( ). DATA(lv_skip) = io_tech_request_context->get_skip( ). DATA numTop TYPE int8. numTop = lv_top. DATA numSkip TYPE int8. numSkip = lv_skip. IF lv_top IS INITIAL. numTop = 10. ENDIF. * Based on the entity set IF iv_entity_set_name = 'UsersSet'. SELECT * FROM zalm_fs_users INTO TABLE @DATA(lt_users) UP TO @numTop ROWS. IF sy-subrc = 0. SELECT * FROM zalm_fs_reluprod INTO TABLE @DATA(lt_reluprod) FOR ALL ENTRIES IN @lt_users WHERE userid = @lt_users-id. ENDIF. IF lt_users IS NOT INITIAL AND lt_reluprod IS NOT INITIAL. LOOP AT lt_users into DATA(ls_user). ls_deep_userProd-include = CORRESPONDING #( ls_user ). SELECT * FROM zalm_fs_reluprod INTO TABLE @DATA(lt_reluprod2) WHERE userid = @ls_user-id. LOOP AT lt_reluprod2 into ls_reluprod. SELECT SINGLE * FROM ZALM_FS_PRODUCTS WHERE id = @ls_reluprod-productid INTO @DATA(uprod). APPEND uprod TO ls_deep_userProd-toproducts. ENDLOOP. APPEND ls_deep_userprod TO lt_deep_userprod. CLEAR ls_deep_userprod-toproducts. ENDIF. * For converting the data in the deep structure to the output format CALL METHOD me->/iwbep/if_mgw_conv_srv_runtime~copy_data_to_ref EXPORTING is_data = lt_deep_userprod CHANGING cr_data = er_entityset. ENDIF. endmethod.
问题排查与解决方案
1. 严格匹配导航属性名称
OData网关对字段名称大小写敏感,代码中定义的toproducts必须与SEGW中配置的导航属性技术名称完全一致(此处为ToProducts)。修改MPC_EXT中的类型定义:
types: BEGIN OF TY_USERPRODUCTS , INCLUDE TYPE ZCL_ZALM_FS_APP_MPC_EXT=>ts_users, ToProducts TYPE STANDARD TABLE OF ZCL_ZALM_FS_APP_MPC_EXT=>ts_products WITH DEFAULT KEY, END OF TY_USERPRODUCTS.
2. 重构方法逻辑,避免自定义深度实体
无需自定义深度实体类型,直接使用SEGW生成的用户实体类型,通过导航属性关联产品集合,框架会自动处理序列化。同时优化SQL性能,避免循环内查询:
method /IWBEP/IF_MGW_APPL_SRV_RUNTIME~GET_EXPANDED_ENTITYSET. DATA: lt_users TYPE TABLE OF zcl_zalm_fs_app_mpc_ext=>ts_users, ls_user TYPE zcl_zalm_fs_app_mpc_ext=>ts_users, lt_rel TYPE TABLE OF zalm_fs_reluprod, lt_products TYPE TABLE OF zcl_zalm_fs_app_mpc_ext=>ts_products, lv_expand TYPE string. lv_expand = io_tech_request_context->get_expand( ). " 仅当expand包含ToProducts时处理 IF iv_entity_set_name = 'UsersSet' AND lv_expand CS 'ToProducts'. " 获取用户数据(支持top/skip) SELECT * FROM zalm_fs_users INTO TABLE lt_users UP TO ( COND #( WHEN lv_top IS NOT INITIAL THEN lv_top ELSE 10 ) ) ROWS OFFSET ( lv_skip ). IF lt_users IS NOT INITIAL. " 批量获取关联关系 SELECT * FROM zalm_fs_reluprod INTO TABLE lt_rel FOR ALL ENTRIES IN lt_users WHERE userid = lt_users-id. " 批量获取关联产品 SELECT * FROM zalm_fs_products INTO TABLE lt_products FOR ALL ENTRIES IN lt_rel WHERE id = lt_rel-productid. " 关联用户与产品 LOOP AT lt_users INTO ls_user. " 筛选当前用户的产品 DATA(lt_user_products) = VALUE #( FOR rel IN lt_rel WHERE ( userid = ls_user-id ) FOR prod IN lt_products WHERE ( id = rel-productid ) NEXT prod ). " 给用户实体的导航属性赋值 ls_user-ToProducts = lt_user_products. MODIFY lt_users FROM ls_user. ENDLOOP. ENDIF. " 将用户集合赋值给输出 CALL METHOD me->/iwbep/if_mgw_conv_srv_runtime~copy_data_to_ref EXPORTING is_data = lt_users CHANGING cr_data = er_entityset. ELSE. " 无expand时调用标准read_entityset逻辑 super->/iwbep/if_mgw_appl_srv_runtime~get_expanded_entityset( EXPORTING iv_entity_set_name = iv_entity_set_name iv_source_name = iv_source_name io_tech_request_context = io_tech_request_context it_filter_select_options = it_filter_select_options it_order = it_order CHANGING er_entityset = er_entityset es_response_context = es_response_context ). ENDIF. endmethod.
3. 覆盖完整场景,避免read_entityset干扰
当无$expand参数时,在GET_EXPANDED_ENTITYSET中调用父类方法,确保框架不会回退到read_entityset执行,保证逻辑一致性。
4. 检查SEGW导航属性配置
- 确认
Users实体已创建名为ToProducts的导航属性,关联到Products实体,关联类型设为多对多,并正确绑定中间表的字段映射。 - 重新生成服务(Generate Runtime Objects),确保配置变更生效。
内容的提问来源于stack exchange,提问作者MarcL
相关产品推荐
相关产品推荐

