作者:似水的流年
原文链接:
https://yq.aliyun.com/roundtable/49835
大多数开发者应该都使用过Hibernate或者Mybatis的框架,或多或少都踩过一些坑!
如在MyBatis/Ibatis中#和$的区别,#方式能够很大程度防止sql注入,$方式无法防止Sql注入。所以,老司机 对新手说,最好用#。简单的说#{}是经过预编译的,是安全的,而是未经过预编译的,仅仅是取变量的值,是非安全的,存在sql注入。有些特例是需要关注的,有的时候需要用 解决解决一些实际问题。
如在执行sql语句时你有时并不希望让变量进行处理,而是直接赋值执行,这时就要用到(${a})了,在使用时还要这样赋值 @Param(value="a") String a
如日期问题:
可能会遇到日期格式的时间段问题,当数据库的时间为DATE类型时,MyBatis的jdbcType应该使用DATE
jdbcType=DATE,
而不是使用
jdbcType=TIMESTAMP
如在使用resultMap的时候,要把ID写在第一行,否则的话,就会报错。
又如最近在做的项目,遇到myBatis的大坑,Mybatis一直报异常:
Java.lang.ArrayIndexOutOfBoundsException,
于是开始代码查错,代码中有存储过程,然后开发使用ROOT用户执行SQL跑出来的数据结果集是正常的,在测试环境程序运行也正常,但是在正式环境就其他用户不行,最后发现是因为数据库没有给该用户授权出了问题。
作为新手,在此记下刚踩的一个坑,(踩踩更健康= =踩过痛过才不会再次错),写了一个sql语句用到两张表,两张表中有两个字段名字是一样的都是Time和Content,然后要查询这两张表的这两个字段都要查出来放到一个dto中,dto如下图所示,
sql语句如下,
然而运行后却发现后几个在数据库表里同名的字段取出来都是null,
但是放到数据库那边执行是没有取出空数据的,
真是苦恼= =,后来经大神指点,sql语句查询出来的这个字段名必须和dto的参数名一致,改成这样就通过了,
数据都取出来了。。。。。。。。。。还记得在hibernate里用hql时放到dto里,select new dto名()参数顺序和类型一致就可以取出来。。。这应该算一个不同点吧,,感觉还是hql用起来舒服,,,求大神科普两者的差别优缺点
当实体类中的属性名和表中的字段名不一致时,使用MyBatis进行查询操作时无法查询出相应的结果的问题,当时上网查了很多才知道,看到的一个解决方法分享给大家,通过来映射字段名和实体类属性名的一一对应关系。这种方式是使用MyBatis提供的解决方式来解决字段名和属性名的映射关系的!
数据库表使用了联合主键,逆向生成的时候生成了两个实体类。看起来别扭。但还是可以用。后来就先取消主键,生成完后再将主键加上。还有就是,tinyint本来以为用来表示比较小的整数,结果生成了布尔型的属性。后来就表示是和否才用tinyint了。逆向生成的sql语句绝对不能人为改动,否则再次生成的时候会重复生成。但是,尽管踩过坑,我还是觉得mybatis超级好用,比hibernate好多了。虽然hibernate我只试过一点之后就完全转向了mybatis了。
sum()和count()使用场景不对导致出错:
count()、count(1)、count(0)就是指绝对的行数,哪怕某行所有字段全部为null也会计算在内。count(1)和count()相比,innodb来说count(*)效率低。
如果count(列名)查询出来的结果就是查出列名中不为null的行数;
sum(列名)对指定列名进行求和
MyBatis把int类型的0处理成空串’’和mysql处理空串’’为0的问题,在Mybatis的Mapper中整数类型条件该如何判断?
当数据库字段类型是整数,如果参数变量为空字符串或者NULL,Mybatis会自动将参数赋值0,所以如果要判断整数参数的多种状态在传递数值到Mapper之前就要判断是否为空字符串和NULL并将相应的状态数值赋值给该参数,否则参数值等于空字符串、NULL和0得到的结果是一样的。
一般情况下,涉及到int类型的操作的时候,在Service中会统一把数字类型先变成字符串类型,然后再传递到Mapper中操作。
在创建新记录的时候把这个字段设置为当前时间,但以后修改时,不再刷新它(可以给createtime使用这个):
TIMESTAMP DEFAULT CURRENT_TIMESTAMP
在创建新记录和修改现有记录的时候都对这个数据列刷新(可以给update使用这个):
TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
在使用resultMap的时候,要把ID写在第一行,否则的话,就会报错。
XML转义字符,如果直接写就会报错,需要用左边一列的转义字符
< < 小于号 > > 大于号
& & 和
' ' 单引号
" " 双引号
前几天在项目中碰到,来说下吧。大神可绕道。在使用selectOne查询个数时,
如果你写了resultType为Integer,然后在业务代码中很自然的用一个变量int去接当前这个方法的返回,如果按照你传入的条件在数据库中没有找到相关的值,此时selectOne方法的返回值会是一个null,当你使用Java的自动拆箱机制的时候会报出一个无情的NPE。
原因:Java在自动拆箱的时候会调用Integer类中的intValue方法,如果当前对象为null,则抛出NPE。
因此,在接受的时候要判空,否则可能异常。
MyBatis的查询或者更新中,如果需要多个参数有如下几种办法:
对象映射,建立一个Java对象,并作为接口的参数,对象的属性可以直接使用#{属性名}的方式访问;
Map, 参数为一个Map, key对于属性名,value对于参数值,这个方法就是传参数是需要建立一个Map的临时对象;
@param参数注解,在接口方法参数前加入参数名称注解,这样就可以直接在Mapper中通过参数名访问;
通过序号访问,第一个参数#{0}或#{param1}, 第二个参数#{1}, #{param2}
MyBatis中时间字段的使用–返回;
时间字段的返回目前笔者采用放回字符串的方式:
date_format(update_time, ‘%Y-%c-%d %H:%i:%s’) updatetime
采用MySQL的时间格式化方法。
或者放回Timestamp类型的数据,要求放回对象属性参数为Timestamp.
MyBatis中时间字段的使用–参数
如果需要查询一段时间范围的数据时,可以通过以下动态SQL的方式查询数据:
and lbr.update_time > #{startTime}
and lbr.update_time < #{endTime, javaType=Date, jdbcType=TIMESTAMP}
对于的接口方法名称如下:
… Date startTime, Date endTime…
我想这个方法会比通过格式转换的效率要高一些
MyBatis中时间字段的使用–写入
写入可是直接写入Timestamp的数据,需要描述一些写入的jdbcType,如下:
{installTime, jdbcType=TIMESTAMP}
Mapper由于机制的问题,不能重载,参数一般设置成Map,但这样会使参数变得模糊,如果想要使代码变得清晰,可以通过service层来实现重载的目的,对外提供的Service层是重载的,但这些重载的Service方法其实是调同一个Mapper,只不过相应的参数并不一致。
也许有人会想,为什么不在Service层也设置成Map呢?我个人是不推荐这么做的,虽然为了方便,我在之前的项目中也大量采用了这种方式,但很明显会给日后的维护工作带来麻烦。因为这么做会使你整个MVC都依赖于Map模型,这个模型其实是很不错的,方便搭框架,但存在一个问题:仅仅看方法签名,你不清楚Map中所拥有的参数个数、类型、每个参数代表的含义。
试想,你只对Service层变更,或者DAO层变更,你需要清楚整个流程中Map传递过来的参数,除非你注释或者文档良好,否则必须把每一层的代码都了解清楚,你才知道传递了哪些参数。针对于简单MVC,那倒也还好,但如果层次复杂之后,代码会变得异常复杂,而且如果我增加一个参数,需要把每一个层的注释都添加上。相对于注释,使用方法签名来保证这种代码可控性会来得更可行一些,因为注释有可能是过时的,但方法签名一般不太可能是陈旧的。
Mybatis的配置SQL时,尽量少用if choose 等标签,能用SQL实现判断的尽量用SQL来判断(CASE WHEN ,DECODE等),以便后期维护。否则,一旦SQL膨胀,超级恶心,如果需要调试Mybatis中的SQL,需要去除大量的判断语句,非常麻烦。另一方面,大量的if判断,会使生成的SQL中包含大量的空格,增加网络传输的时间,也不可取。
而且大量的if choose语句,不可避免地,每次生成的SQL会不太一致,会导致ORACLE大量的硬解析,也不可取。
我们来看看这样的SQL:
这样的if判断,其实是完全没有必要的,我们可以很简单的采用DECODE来解决默认值问题:
当然有人会想,引入CASE WHEN,DECODE会导致需要ORACLE函数解析,会拖慢SQL执行时间,有兴趣的同学可以回去做一下测试,看看是否会有大的影响。就个人经验而言,在我的开发过程,没有发现因为函数解析导致SQL变慢的情形。影响SQL执行效率的一般情况下是JOIN、ORDER BY、DISTINCT、PARTITATION BY等这些操作,这些操作一般与表结构设计有很大的关联。相对于这些的效率影响程度,函数解析对于SQL执行速度影响应该是可以忽略不计的。
SELECT * FROM TEST
WHERE COL1 > 1 -- 这里是注释AND COL2 = #{a}
CDATA内部所有东西都会被解析器忽略
select type, shopCouId, Path
from adinfo
WHERE delFlg ='0'
and fromDate < #{date} and toDate >= #{date}
SELECT sum(b.netCnt) + #{netCnt,jdbcType=INTEGER}
FROM collectcnt b
WHERE b.shopId = #{shopId,jdbcType=INTEGER}
AND b.delflg = '0'
newCnt = newCnt + 1,
netCnt = netCnt +1,
sumCnt = sumCnt + 1,
AND o.oTime LIKE CONCAT('%',#{sdate},'%')
当数据库字段类型是整数,如果参数变量为空字符串或者NULL,Mybatis会自动将参数赋值0,所以如果要判断整数参数的多种状态在传递数值到Mapper之前就要判断是否为空字符串和NULL并将相应的状态数值赋值给该参数,否则参数值等于空字符串、NULL和0得到的结果是一样的。
一般情况下,涉及到int类型的操作的时候,在Service中会统一把数字类型先变成字符串类型,然后再传递到Mapper中操作。
推荐阅读:
喜欢我可以给我设为星标哦
好文章,我 在看