MyBatis记录同一涂鸦bug解决进程:velocity中获url中之参数

一、总结

  1. 在Webx的Velocity中获取url中参数:$rundata.getRequest().getParameter(‘userId’)
  2. 以Webx项目蒙,防止CSRF攻击(Cross-site
    request
    forgery,跨站请求伪造),在form表单提交中假如加入$!csrfToken.ajaxUniqueToken
  3. 于MyBatis的mapper层,使用标签association实现目标的关联,一个bean配多独association标签。

二、Bug描述:Velocity从URL中获取parameter参数

  于路IDCM中,使用webx容器进行路之开销。前端的模板引擎采用了velocity,在项目被,当于列表页跳到详情页的当儿,通常咱们的screen层是以如下方式开展展现的:

public class EditRules extends BaseScreen {
    @Autowired
    private AutoAssignSupplierBo autoAssignSupplierBo;
    @Autowired
    private SupplierBo           supplierBo;
    @Autowired
    private AddressBo            addressBo;
    @Autowired
    private SiteBo               siteBo;

    public void execute(@Param("id")
    String id, Context context) throws Exception {
        QueryAssignRulesrDo query = new QueryAssignRulesrDo();
        if (StringUtils.isBlank(id)) {
            throw new ServiceException("id is empty ");
        }
        query.setRuleId(Long.parseLong(id));
        BoResultDTO<List<AssignRulesVo>> result = autoAssignSupplierBo.selectByQuery(query);
        List<AssignRulesVo> list = result.getData();
        if (CollectionUtils.isNotEmpty(list)) {
            // 当前规则的详情
            if (StringUtils.isNotBlank(list.get(0).getType())) {
                list.get(0).setTypeVal(list.get(0).getType());
              list.get(0).setType(WorkOrderCst.RelocationType.getNameByStrValue(list.get(0).getType()));
            }
            // 如果有数量信息需要展示
            if (list.get(0).getRuleContent().contains("数量")) {
                String[] numDes = list.get(0).getRuleContent().split(" ");
                for (String str : numDes) {
                    if (str.contains("数量")) {
                        String[] sz = str.split(":");
                        if (2 == sz.length) {
                            list.get(0).setAssetNum(sz[1]);
                        }
                    }
                }
            }
            context.put("ruleInfos", JSON.toJSONString(list.get(0)));
            // 补全控件信息
            Map<String, Object> map = fullInfo(list.get(0).getRuleJsonVal());
            if (list.get(0).getRuleContent().contains("数量")) {
                map.put("assetNum", list.get(0).getAssetNum());
            }
            context.put("ruleDes", JSONUtils.toJSONString(map));
        }
        // 初始化类型信息
        Map<String, Object> RelocationTypeList = WorkOrderCst.RelocationType.getRelocationTypeList();
        context.put("RelocationTypeList", RelocationTypeList);
        // 物流供应商信息
        context.put("logisticsSps", supplierBo.queryAllByType(WorkOrderCst.SpType.logistics.name()));
        // 传入设备类型
        this.setorderDeviceType(context);
    }
}

  上图代码是半自动分配物流供应商从列表页跳反至详情页的时候,需要展示调用screen层的跟*.vm同名的*.java方法,通过传播参数id,即挑了因定行,后台会将查询及的多寡封装到对象被,在vm中可一直动用,而不用更走ajax请求,提升了系的反映速度。其中于BaseScreen.java中负国有日志的输出,当前权限的获取,以及部分官性质的动态获取。但是,有一些政工场景中,我们当进展跳转的时,只需要理解跳转过来的脚下id,并不需要后端来加载数据。这时候,如果会由vm中一直拿走跳转过来的url的parameter参数,那么尽管可以省去与*.vm同名的*.java中的execute方法。

// 唯一正确的用法:
<input type="hidden" id="user_id" value=" $rundata.getRequest().getParameter('userId')"/>

//以下几种用法都无法获取到参数的值
$!request.parameter.userId
$!request.paarmater.getParameter('userId')

除此以外,Velocity更多用细节参考英文官方文档或造这里。

三、Bug描述:$!csrfToken.hiddenField

  CSRF(跨站请求伪造),它经过伪装来自于信赖用户的请求来使用为信赖的网站。在IDCM项目遭到,在*.vm页面会产生大量底<from>表单提交,在表单提交的时节,为了以防跨站请求伪造,要当form标签后紧跟$!csrfToken.ajaxUniqueToken。

<form name="xxx" action="xxx" method="post">
  $!csrfToken.hiddenField 
</form>

上述代码中,每当VM文件的form表单中补充加了token。拖欠表单请求极有或涉嫌数量增删改,需要防范CSRF,请确认使用POST请求,增加token参数,并于劳务端校验token。

老三、Bug描述:MyBatis中mapper层的association标签下

API接口调用,web层/openapi/rack代码如下,RpcResult做也接口查询返回的靶子,包括Data、info、Success,所以该层合理的代码应该包含try
catch,正确的逻辑打印是的消息。一旦接口调用错误,要回合适的信。

//根据房间和机柜名称,批量查询机柜信息    @ResourceMapping("batchQueryRackByRoomNameAndRackName")
    public RpcResult<Object> batchQueryRackByRoomNameAndRackName(@RequestParam(name = "queryParam")
    String queryParam) {
        RpcResult<Object> rpcResult = new RpcResult<Object>();
        try {
            @SuppressWarnings("unchecked")
            List<RackAndRoom> list = (List<RackAndRoom>) JsonToBeanUtil.JsonToJavaBean(queryParam, RackAndRoom.class);
            List<Rack> rackList = new ArrayList<Rack>();
            if (CollectionUtils.isNotEmpty(list)) {
                for (RackAndRoom rackAndRoom : list) {
                    checkParameter(rackAndRoom);
                    Rack rack = rackBo.batchQueryRackByRoomNameAndRackName(rackAndRoom.getRackName(),                                                                           rackAndRoom.getRoomName());
                    rackList.add(rack);
                }
                rpcResult.setData(rackList);
                rpcResult.setInfo("查询成功!");
                rpcResult.setSuccess(true);
            }
        } catch (Exception e) {
            logger.error(" batchQueryRackByRoomNameAndRackName error:" + e.getMessage(), e);
            rpcResult.setSuccess(false);
            rpcResult.setInfo("查询失败,具体异常信息为:" + e.getMessage());
        }
        return rpcResult;
    }

上述的代码结构在接口查询中,利用了分类的思量,针对接口查询成功与破产,分别针对info、success、data进行赋值。并且要失败,会发日记记录。异常的抓获在RPC层(addError)、Bo层(事务回滚)、OpenAPI层要区别对待。对于RPC中的左,因为它是透过浏览器与用户交互的,所以一般会拿错误添加至Error对象被,将错误信息反馈让浏览器端的用户,使得用户可改好之操作,达到预期的功能。

    /**
     * 修改排序
     */
    @ResourceMapping("updateOrderIng")
    public RpcResult<Object> updateOrderIng(@RequestParams
    AssignRulesVo assignRulesVo, @RequestParam(name = "type")
    String type, ErrorContext error) {
        RpcResult<Object> result = new RpcResult<Object>();
        result.setSuccess(true);
        try {
            if (null == assignRulesVo || null == assignRulesVo.getOrdering() || StringUtils.isBlank(type)) {
                throw new SerialException("缺少排序信息和规则信息,无法修改");
            }
            autoAssignSupplierBo.updateOrderIng(assignRulesVo, type);
            result.setInfo("修改排序成功");
            result.setData(true);
        } catch (Exception e) {
            result.setSuccess(false);
            logger.error("updateOrderIng err : ", e.getMessage(), e);
            addError(error, ErrorCode.Sys_Error.getCode(), e.getMessage(), result);
        }
        return result;
    }

对待可以看RPC中,通常会流传Error对象,使用addError方法,将错误信息反馈到前端。接着讲openapi中之询问接口,它通过调用bo层、boImpl层、Ext层的rackMapperExt.batchQueryRackByRoomNameAndRackName(map);来询问。

  <select id="batchQueryRackByRoomNameAndRackName" parameterType="java.util.Map" resultMap="BaseResultMap_Ext">
          select
            <include refid="Base_Column_List_Ext" />
            <include refid="Base_Column_List_Room" />
            <include refid="Base_Column_List_Site" />
        FROM
        idc_rack temp_rack
        INNER JOIN idc_room temp_room ON temp_rack.room_id = temp_room.room_id
        INNER JOIN idc_site temp_site ON temp_room.site_id = temp_site.site_id
        WHERE
            temp_rack.is_deleted = 'n'
        AND temp_room.is_deleted = 'n'
        AND temp_site.is_deleted = 'n'
        AND temp_rack.rack_name = #{rackName}
        AND temp_room.room_name = #{roomName}
  </select>

当代码中,查询及之参数采用了<include>标签,将要查询的room、rack、site等信息捞出来,最后映射到BaseResultMap_Ext中,如下:

<resultMap type="com.alibaba.tboss.dal.mysql.rack.Rack" id="BaseResultMap_Ext" extends="BaseResultMap">
        <result column="rack_name" property="rackName" jdbcType="VARCHAR" />
        <result column="room_name" property="roomName" jdbcType="VARCHAR" />
        <result column="site_name" property="siteName" jdbcType="VARCHAR" />
        <result column="siteType" property="siteType" jdbcType="VARCHAR" />
        <result column="lock_num" property="lockNum" jdbcType="INTEGER" />
        <result column="power_state" property="powerState" jdbcType="VARCHAR" />
          <association property="room" javaType="com.alibaba.tboss.dal.mysql.room.Room">
              <id column="room_id" property="roomId" jdbcType="INTEGER" />
              <result column="site_id" property="siteId" jdbcType="INTEGER" />
              <result column="creator" property="creator" jdbcType="INTEGER" />
              <result column="gmt_create" property="gmtCreate" jdbcType="TIMESTAMP" />
              <result column="modifier" property="modifier" jdbcType="INTEGER" />
              <result column="gmt_modified" property="gmtModified" jdbcType="TIMESTAMP" />
              <result column="is_deleted" property="isDeleted" jdbcType="CHAR" />
              <result column="room_name" property="roomName" jdbcType="VARCHAR" />
              <result column="room_full_name" jdbcType="VARCHAR" property="roomFullName" />
              <result column="type" property="type" jdbcType="VARCHAR" />
              <result column="cmdb_room_id" property="cmdbRoomId" jdbcType="INTEGER" />
          </association>
          <association property="site" javaType="com.alibaba.tboss.dal.mysql.site.Site">
              <id column="site_id" jdbcType="INTEGER" property="siteId" />
              <result column="site_name" jdbcType="VARCHAR" property="siteName" />
              <result column="country" jdbcType="VARCHAR" property="country" />
              <result column="region" jdbcType="VARCHAR" property="region" />
              <result column="province" jdbcType="VARCHAR" property="province" />
              <result column="city" jdbcType="VARCHAR" property="city" />
              <result column="address" jdbcType="VARCHAR" property="address" />
              <result column="type" jdbcType="VARCHAR" property="type" />
              <result column="site_full_name" jdbcType="VARCHAR" property="siteFullName" />
              <result column="supplier" jdbcType="VARCHAR" property="supplier" />
              <result column="repair_api" jdbcType="VARCHAR" property="repairApi" />
              <result column="is_valid" jdbcType="BIT" property="isValid" />
              <result column="creator" jdbcType="VARCHAR" property="creator" />
              <result column="modifier" jdbcType="VARCHAR" property="modifier" />
              <result column="gmt_create" jdbcType="TIMESTAMP" property="gmtCreate" />
              <result column="gmt_modified" jdbcType="TIMESTAMP" property="gmtModified" />
              <result column="is_deleted" jdbcType="CHAR" property="isDeleted" />
              <result column="sign_company" jdbcType="VARCHAR" property="signCompany" />
              <result column="first_user_id" jdbcType="BIGINT" property="firstUserId" />
              <result column="second_user_id" jdbcType="BIGINT" property="secondUserId" />
              <result column="third_user_id" jdbcType="BIGINT" property="thirdUserId" />
              <result column="comments" jdbcType="VARCHAR" property="comments" />
              <result column="cmdb_site_id" property="cmdbSiteId" jdbcType="INTEGER" />
          </association>
  </resultMap>

经标签<association>实现了目标映射的当儿的同样对几近的涉,上述resultMap使用extends=”BaseResultMap”扩展了目标映射,其中BaseResultMap来自于Mybatis自动生成的mapper中之炫耀关系。

季、批量查询接口性能优化

ext层:

@Resource
public interface RackMapperExt extends RackMapper {
    List<Rack> batchQueryRackByRoomNameAndRackName(List<RackVo> list);
}

mapper层:

  <select id="batchQueryRackByRoomNameAndRackName" parameterType="java.util.List" resultMap="BaseResultMap_Ext">
          select
            <include refid="Base_Column_List_Ext" />
            <include refid="Base_Column_List_Room" />
            <include refid="Base_Column_List_Site" />
        FROM
        idc_rack temp_rack
        INNER JOIN idc_room temp_room ON temp_rack.room_id = temp_room.room_id
        INNER JOIN idc_site temp_site ON temp_room.site_id = temp_site.site_id
        WHERE
            temp_rack.is_deleted = 'n'
        AND temp_room.is_deleted = 'n'
        AND temp_site.is_deleted = 'n'
        AND
        <foreach item="item" index="index" collection="list" open="(" separator="OR" close=")">
            (temp_rack.rack_name = #{item.rackName} AND temp_room.room_name = #{item.roomName})
        </foreach>
  </select>

五、MyBatis中<![CDATA[…]]>的使用

  以xml文件中,处于CDATA部分受之有所内容还见面让解析器忽略,避免由于>&等sql字符影响xml文档结构,segmentfault问题:前去这里。

 

六、待上:阿里巴巴缓存用bacardi、tail等。

附录:

阿里巴巴-基础架构事业群业务,项目路线:

MyBatis 1

基础架构事业群业务。

网站地图xml地图