不要试图挑战锻炼自己的自律,要想着把欲望彻底消除。
封装流程
mybatis在jdbc上进行封装
- 把数据库的链接 关闭的操作 进行隐藏执行 不用显性执行
- 抽离出了sql 到持久层xml文件中
- 事务
- 缓存的支持(本地缓存,微服务会有脏数据)
-
一级缓存
默认开启 sqlsession生命周期 缓存查询结果 写入删除 不能跨sql导致脏数据 -
二级缓存
默认关闭 namespace生命周期 缓存查询结果 写入删除
-
![](https://img.haomeiwen.com/i15379026/11fba314799d9eb9.png)
batch是批量插入,缓存编译之后的sql,多条一并执行。
延时加载
在xml中配置,a表对b表关联,可以实现用到b表数据时在去获取b表的数据。
动态代理cglib实现的。
#$区别
- #是预编译处理,会加引号。
- ac.company_id IN (
21212,21211
) 会变成("21212,21211"
) 导致查询数据错误。 - order by
order_id
->order by"order_id"
- ac.company_id IN (
- $是字符串替换
map接受数据
- 好处:方便 不用写接受 entity
- 不好的地方:
- 结果未知,不直观,转换会出问题(long->int)
-
null
的字段不返回(处理数据空指针)
mybatis:configuration:call-setters-on-nulls: true
- Mybatis中表中 tinyint 1 会自动转为 boolean
- 链接配置:
?tinyInt1isBit=false
- 改为tinyint 2 上线之后 一样的代码 还是会转换为 boolean。。 改为mysql 为 int类型
- 链接配置:
默认类型的一些研究
一般我们用mybatis有几种情况:
- 用tk.mybatis或者mybatisPlus,不创建xml,直接创建实体接受mysql的数据。
- 在xml配置文件里指定字段的类型:
<result column="bank_account" property="bankAccount" jdbcType="BIGINT" />
但是我看我们公司的代码有的是在xml里Map接收数据,没有配置字段到实体类型的映射,这种情况字段映射的类型是未知的,如果映射为Long,用(Interger)强转的方式映射数据,就会报错:
Exception in thread "main" java.lang.ClassCastException: java.lang.Long cannot be cast to java.lang.Integer
这种问题。我就遇到过这个问题,所以就比较好奇具体是如何映射的,通过debug一层层寻找发现逻辑在 com.mysql.jdbc.ResultSetMetaData类的 getClassNameForJavaType方法中:
static String getClassNameForJavaType(int javaType, boolean isUnsigned, int mysqlTypeIfKnown, boolean isBinaryOrBlob, boolean isOpaqueBinary,
boolean treatYearAsDate) {
switch (javaType) {
case Types.BIT:
case Types.BOOLEAN:
return "java.lang.Boolean";
case Types.TINYINT:
if (isUnsigned) {
return "java.lang.Integer";
}
return "java.lang.Integer";
case Types.SMALLINT:
if (isUnsigned) {
return "java.lang.Integer";
}
return "java.lang.Integer";
case Types.INTEGER:
if (!isUnsigned || mysqlTypeIfKnown == MysqlDefs.FIELD_TYPE_INT24) {
return "java.lang.Integer";
}
return "java.lang.Long";
case Types.BIGINT:
if (!isUnsigned) {
return "java.lang.Long";
}
return "java.math.BigInteger";
case Types.DECIMAL:
case Types.NUMERIC:
return "java.math.BigDecimal";
case Types.REAL:
return "java.lang.Float";
case Types.FLOAT:
case Types.DOUBLE:
return "java.lang.Double";
case Types.CHAR:
case Types.VARCHAR:
case Types.LONGVARCHAR:
if (!isOpaqueBinary) {
return "java.lang.String";
}
return "[B";
case Types.BINARY:
case Types.VARBINARY:
case Types.LONGVARBINARY:
if (mysqlTypeIfKnown == MysqlDefs.FIELD_TYPE_GEOMETRY) {
return "[B";
} else if (isBinaryOrBlob) {
return "[B";
} else {
return "java.lang.String";
}
case Types.DATE:
return (treatYearAsDate || mysqlTypeIfKnown != MysqlDefs.FIELD_TYPE_YEAR) ? "java.sql.Date" : "java.lang.Short";
case Types.TIME:
return "java.sql.Time";
case Types.TIMESTAMP:
return "java.sql.Timestamp";
default:
return "java.lang.Object";
}
}
这个方法大家可以参考,实际情况还会有不一样的情况。比如:mysql类型是TINYINT,测试和线上就被解析成了不同的类型一个Integer一个boonlean。最后还是让我把mysql字段硬改成了Integer类型 -_~!。
使用tk.mybatis一些经验
insert
: 把实体类里面所有字段,拼接sql,执行插入。(需要所有的字段都有默认值,否则不为null的字段会报错)
insertSelective
: 只会对指定的字段,拼接sql,执行插入。(没有提到的字段会根据表默认值进行插入)text
类型字段,没有默认值,必须指定。
insertList
: 是按照insert的方式来的,需要每个字段指定值,否则就是以null形式插入,不为null的字段就会有问题。
@ID
字段注解不能删除,否则.deleteByPrimaryKey(id);
会报错。
MySQL数据存储加密
在一些安全扫描中,如果你的密码是用md5存储的,肯定会说有安全漏洞。我们可以再加一层aes加密,前端传md5,修改和插入时加密存储。登录检查时解密,和前端传值解密。
大致有3种方法,typehandler,plugin, 直接写逻辑。在简单场景中,推荐直接写逻辑。typehandler可以对字段加个封装类,然后加一个转换类,在set和get方法中写入逻辑,配置中指定handler位置。
流式操作
代替普通查询方法,在查询大数据可以避免OOM,每次只传一条数据。游标查询每次会查多条,但是性能会慢很多。
大数据查询分页可以用记录id+limit来深分页查询,但是需要是有顺序的记录。
网友评论