6 Mybatis

原文: https://www.jianshu.com/p/33762942d6b2

大多数开发者应该都有使用过Hibernate 或者 Mybatis 的框架,或多或少的都踩过一些坑!
如在MyBatis/Ibatis中 #和方式无法防止 Sql注入。所以,老司机对新手说,最好用#。 简单的说 #{} 是经过预编译的是安全的, 而${} 是未经过编译的,仅仅是取变量的值,是非安全的,存在sql 注入。

有些特例是需要关注的,有些时候需要解决一些实际问题

如在执行sql 语句时, 你有时候并不希望让变量进行处理,而是直接赋值执行,这时候要用 (${a})l , 在使用时 还要这样赋值:
@Param(value="a") String a

如日期问题:
可能会遇到日期格式的时间段的问题,当数据库的时间为DATE类型时, MyBatis的 jdbcType 应该使用: DATE jdbcTYpe =DATE,
而不是使用:

jdbcType= TIMESTAMP.

如果在使用resultMap 的时候,要把id些在第一行,否则的话,就会报错。
又如最近在做项目的时候,遇到MyBatis的大坑,Mybatis 一直报异常:

java.lang.ArrayIndexOutOfBoundsException,
于是开始代码查错,代码中有存储过程,然后开发使用Root用户执行sql 跑出来的数据结果集是正常的,在测试环境程序运行也是正常,但是在正式环境就是其他用户不行,最后发现数据库内有给该用户授权出了问题。

案例一,
写一个sql语句是用2 个表 ,2个表中的字段名字是一样的都是time 和Content,然后要查询这2 张表的这2个字段都要查询出来放到一个dto中,dto如下图所示:


image.png

sql语如下


image.png

然而运行后却发现结果,几个在数据库表里同名的字段取出来都是null


image.png

但是放到数据库那边执行是没有取出空数据的

image.png

原因: sql 语句查询出来的这个字段名必须和dto的参数名一致,
改成这样就可以:

image.png

当实体类中的属性名和表中的字段名不一致时,使用Mybatis进行查询操作时无法查询出相应的结果的问题,通过用映射字段名和实体字段名的一一对应的关系。这种方式使用Mybatis提供的解决字段名和属性名的映射关系

案例二:

数据库表是用来联合主键,逆向生成的时候生成了2个实体类。看起来变扭,但还是可以用,后来就先取消主键,生成完后再将主键加。还有就是,tinyin本来可以用来标识比较小的整数,结果生成了布尔型的属性。后来表示是否才用tinyin了。逆向生成的sql语句绝对不能认为改动,否则再次生成的时候会重复生成。

案例三;

sum() 和count() 的使用场景不对导致出错:
count(), count(1) ,count(0) 就是指绝对的行数,哪怕某行所有的字段全部为null,也会计算在内。count(1)和 count() 相比,innodb 来说coun(*)效率低。

如果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}

1,Mapper层参数为Map ,由Service 层负责重载。
Mapper由于机制的问题,不能重载,参数设置为Map,但这样会使参数变的模糊,如果想要是代码变的清晰,可以通过service 层来实现重载的目的,对外提供的service 层是重载的,但这些重载的Serviec方法其实是调用同一个Mapper,只不过相应的参数并不一致

也许有人会想,为什么不在Service层也设置成Map呢

试想,你只对Service 层变更,或者DAo层变更,你需要清楚整个流程中Map 传递过来的参数。除非你注释或者文档良好,否则必须把每一层的代码都理解清楚,你才知道传递了那些参数。针对于简单的MVC,那到也还好,但是如果层次复杂后,代码会变得异常复杂,而且如果我增加一个参数。需要每一层的注释都添加上,对于注释,使用方法签名来保证这种代码的可控性会来的更可行一点,因为注释可能是过时的,但是方法签名一般不太可能陈旧的。

2,尽量少用 if chooose 等语句,降低维护的难度。

Mybatis 的配置sql 时,尽量少使用 If choose 等标签。能用sql实现的尽量用sql 来判断(CASE WHEN ,DECODE 等),以便后期维护。否则,一旦sql 膨胀,超级恶心,如果需要调试Mybatis

你可能感兴趣的:(6 Mybatis)