Java日常探秘-从小疑问到实践智慧的编程之旅(2)


前言

所有分享的内容源于日常思考和实践,探讨Java编程中的小知识点和实用场景,加深自己对编程技巧和理解Java深层次的原理,期待发现妙招和解决实际问题的新思路。


一、常见错误

400错误

也称400 Bad Request,是一种HTTP状态码,表示客户端发送给服务器的请求在语法上存在问题,导致服务器无法理解或处理该请求。

在进行web开发中,参数错误是导致400 Bad Request错误的常见原因之一:

  1. 参数错误:缺少必要参数,客户端未提供必要的参数,导致服务器无法处理请求。
  2. 参数格式不正确:客户端提供的参数格式不符合服务器的要求。

举例说明:

//后端接口
public AjaxResult check(@RequestParam("ids")List<Long>ids){
   //执行业务代码
}

前端在传递ids参数时,不小心传成了[5,6,7]这种列表形式,服务器报400错误,后来对于ids参数传成5,6,7的形式,服务器成功处理该请求


401错误

401错误(未授权):未授权访问的提示,那就意味着你被挡在了门外,没有得到进入的许可。就像是你去参加一个派对,却被门口的保安拦住了,因为你没有邀请函或者身份验证。

常见原因:
1.缺少身份验证信息:请求未包含身份验证信息或身份验证信息不完整。通常,身份验证信息应该包含在请求的头部字段Authorization中。
2.无效的身份验证信息:请求包含的身份验证信息无效,可能是由于凭据错误、过期的凭据、无效的令牌等导致的。


404错误

404错误信息通常是在目标页面被更改或移除,或客户端输入页面地址错误后显示的页面,或者浏览器请求后端地址时路径没对应上。
前端js请求

function exportDetail(crossId){
    return request({
        url: '/system/cross/exportDetail/'+crossId,
        method: 'get'
    })
}

后端控制层代码

//后端接口
@GetMapping(value = "/system/cross/{crossId}")
public AjaxResult exportDetail(Pathvariable("crossId")String crossId){
   //执行业务代码
}

这时请求就会报404 Not Found错误,将后端接口路径修改为和前端js一致即可。

@GetMapping方法修改为

//后端接口
@GetMapping(value = "/system/cross/exportDetail/{crossId}")
public AjaxResult exportDetail(Pathvariable("crossId")String crossId){
   //执行业务代码
}

MismatchedInputException

MismatchedInputException是Java中Jackson库在反序列化过程中抛出的异常。Jackson是一个快速的JSON处理库,用于将Java对象转换成JSON格式,以及将JSON转换成Java对象。当Jackson尝试将JSON数据转换为Java对象时,如果JSON的结构与目标Java类的结构不匹配,就会抛出MismatchedInputException: Cannot deserialize instance of

通过一个简单的例子来解析Jackson的反序列化过程

public class TaskResp {
    private String id;
    private String name;
    // getters and setters
}

public class Main {
    public static void main(String[] args) {
        String json = "[{\"id\":\"1\",\"name\":\"Task1\"},{\"id\":\"2\",\"name\":\"Task2\"}]";
        ObjectMapper mapper = new ObjectMapper();
        try {
            // 错误:尝试将数组反序列化为单个对象
            TaskResp taskResp = mapper.readValue(json, TaskResp.class);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
    }
}

我们尝试将一个包含多个TaskResp对象的JSON数组反序列化为单个TaskResp对象,,因此会抛出MismatchedInputException。


Mybatis的NumberFormatException

<where>
  <if test="isExpired != null and isExpired=='Y'">and msg.expire_time &lt; now() </if>
</where>

mybatis使用字符串参数的时候 mybatis java.lang.NumberFormatException: For input string: ‘Y’

报错原因:mybatis是用OGNL表达式来解析的,在OGNL的表达式中,'Y’会被解析成字符,Java是强类型的,char和一个string会导致不等,所以if标签中的sql不会被解析。在做字符串的比较的情况下,需要将字符转为字符串类型。

<where>
  <if test="isExpired != null and isExpired=='Y'.toString()">and msg.expire_time &lt; now() </if>
</where>

Mybatis的Available parameters are [collection,list]]报错

mybatis可以支持多种传参模式,主要讲解mybatis的list集合或数组传参时遇到的一个问题。

<select id="queryImIdByEmails" parameterType="java.util.List" resultType="java.lang.String">
        select uid from employee where email in
        <foreach collection="param1" item="item"  separator="," open="(" close=")" index="">  
              #{item}    
        </foreach> 
</select>

本地调试时报了这个错误:org.apache.ibatis.binding.BindingException: Parameter ‘customerIdList’ not found. Available parameters are [collection, list]

报错原因:集合或者数组传参给mybatis时,mybatis会使用map来封装参数,如果是list传参时,map的默认key为list,数组传参时,map默认key为array,在不指定传参名称时,必须使用默认的key为sql中collection的值

将collection="param1"改为collection=“list”

<select id="queryImIdByEmails" parameterType="java.util.List" resultType="java.lang.String">
        select uid from employee where email in
        <foreach collection="list" item="item"  separator="," open="(" close=")" index="">  
              #{item}    
        </foreach> 
</select>

Mybatis之Double类型的字段的值为0.0传入不了sql中

原因:Mybatis对于数值型的参数,如果是0会被转换成空字符串, 所以动态条件不成立无法拼接后续条件。

解决方法:如果是数值型吗,判断0.0,需要加 .toString()进行判断。

dto接收前端传入进来的0的数字,经过后端的dto接收后,变成了0.0Double类型的数据。

 @ApiModelProperty("绩点")
 @JsonProperty(value="GPAValue")
 private java.lang.Double GPAValue;

在xml文件需要加上’0’.toString()如:

<if test="GPAValue != null and GPAValue != '' or GPAValue == '0'.toString() ">
         and  JD = #{GPAValue,jdbcType=NUMERIC}
</if>

最好的处理方式:保持好习惯,不要乱写判断,数字类型不会出现空字符串就不要去判断,String类型才需要有空字符串判断。


二、SQL效率

数据库字段类型和传入参数类型不匹配

给employee表的name字段加上索引

CREATE TABLE `employee` (
  `name` varchar(30)  DEFAULT NULL COMMENT '姓名',
  KEY `index_name` (`name`)
) ENGINE=InnoDB;

1.查询name为数字时(全表扫描-发生了隐式类型转换,索引失效)

explain select * from employee where name=1

2.查询name为字符串时,成功走了索引

explain select * from employee where name='1'

所以在开发时一定要注意参数的类型和数据库的类型是否匹配的问题,不然一不小心就可能会导致索引失效。


小表驱动大表

小表驱动大表,也就是说用小表的数据集驱动大表的数据集。

假如有order和user两张表,其中order表有10000条数据,而user表有100条数据,现在想查询所有有效的用户下过的订单列表

可以使用in关键字实现,因为如果sql语句中包含了in关键字,则它会优先执行in里面的子查询语句,然后再执行in外面的语句。如果in里面的数据量很少,作为条件查询速度更快。

select * from order 
where user_id in (select id from user where status=1)

而如果sql语句中包含了exists关键字,它优先执行exists左边的语句(即主查询语句)。然后把它作为条件,去跟右边的语句匹配。如果匹配上,则可以查询出数据。如果匹配不上,数据就被过滤掉了。

select * from order 
where exists (select 1 from user where order.user_id = user.id and status=1)

思考:为什么小表驱动大表会提高效率,不管大表是驱动表还是小表是驱动表,单纯的分析指令执行次数,都是相同的,那提效在哪里?

//小表驱动大表
for(i = 0; i < 100; i++){
	for(j = 0; j < 10000; j++){
	 //处理业务逻辑代码
	}
}

//大表驱动小表
for(i = 0; i < 10000; i++){
	for(j = 0; j < 100; j++){
	 //处理业务逻辑代码
	}
}

看以上两个for循环,总共循环的次数是一样的。但是对于mysql数据库而言,并不是这样了,我们尽量选择第②个for循环,也就是小表驱动大表。数据库最伤神的就是跟程序链接释放,第一个建立了10000次链接,第二个建立了50次。假设链接了两次,每次做上百万次的数据集查询,查完就走,这样就只做了两次;相反建立了上百万次链接,申请链接释放反复重复,这样系统就受不了了。


减少与数据库之间的连接

1.业务中出现在循环中频繁查询数据库对比数据,频繁连接数据库耗费资源。
优化方法:可以将数据取出放进list进行操作。

List<A> listA = dao.getAlist();
Map<String,Object> mapA = new HashMap<String,Object>();
//将a放进map中
for(A a : listA){
 mapA.put(a.getId(),a);
}

List<B> listB =  dao.getBlist();
Map<String, Object> A_B = new HashMap<String, Object>();
for(B b : listB){
   A a = mapA.get(b.getAId());
   //业务代码
}

2.随着业务需要,有时我们需要将数据批量添加到数据库,mybatis提供了将list集合循环添加到数据库的方法,以减少频繁连接数据库。

<insert id="insertBatch">
    INSERT INTO t_user (name, del_flag)
    VALUES
    <foreach collection ="list" item="user" separator =",">
         (#{user.name}, #{user.delFlag})
    </foreach >
</insert>

总结

本文介绍了几种常见的HTTP状态码、Mybatis的易报错场景、数据库效率的优化。以上文索引失效的场景为例,只有我们明确了什么情况下索引会失效,才不会发生我们添加了索引,不仅占用了资源,还没有生效的场景出现。


  • 14
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JinziH Never Give Up

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值