mybatis的参数获取:${}、#{}
本质:就好像 JDBC 中 预编译的俩种参数化查询方式
${}
:字符串拼接,可能造成SQL注入,需要手动拼接‘’
#{}
:占位符赋值
为什么可以防止SQL注入?
#{}
通过set方法为参数赋值会将传入参数字符串的'
字符进行转义\'
大多数情况建议直接使用#{}
,尽量避免使用${}
。有什么情况必须使用${}
的?
当我们需要拼接的变量上不能带单引号时,就必须使用${}
通过字符串拼接方式。例如:
1.当sql中表名是从参数中取的情况
2.order by排序语句中,因为order by 后边必须跟字段名,这个字段名不能带引号,如果带引号会被识别会字符串,而不是字段。
顺便贴下JDBC:JDBC编程之预编译SQL与防注入、JDBC连接如何防止SQL注入?
在JDBC编程中,常用Statement、PreparedStatement 和 CallableStatement三种方式来执行查询语句,其中 Statement 用于通用查询, PreparedStatement 用于执行参数化查询,而 CallableStatement则是用于存储过程。
预编译的好:
① 安全性不同: PreparedStatement可以有效防止sql注入,而Statment不能防止sql注入。
② 语法不同:PreparedStatement可以有效防止sql注入,而Statment不能防止sql注入。
③ 效率不同:SQL 语句被预编译并存储在 PreparedStatement 对象中。然后可以使用此对象多次高效地执行该语句。
mybatis参数值获取的各种情况
1、Mapper接口方法的参数为单个的:
可以通过${}和#{}
以任意的名称获取参数值,但是需要注意${}
的单引号问题
2、Mapper接口方法的参数为多个:
Mybatis会将参数放在一个map集合中,以俩种方式进行存储
a > 以arg0,arg1…为键,以参数为值
b > 以param1,param2…为键,以参数为值
因此只需要通过#{}和${}
以键的方式访问值即可,但是需要注意${}
的单引号问题
3、Mapper接口方法多个参数,手动将参数放在一个map集合:
只需要通过#{}和${}
以键的方式访问值即可,但是需要注意${}
的单引号问题
4、Mapper接口方法的参数是实体类类型参数:
只需要通过#{}和${}
以属性的方式访问属性值即可,但是需要注意${}
的单引号问题
5、使用@Param注解命名参数:2和3的结合
Mybatis会将参数放在一个map集合中,以俩种方式进行存储
a > 以@Param注解的值为键,以参数为值
b > 以param1,param2…为键,以参数为值
mybatis的查询结果
1,若查询出的数据只有一条
a > 可以通过实体类对象接收
b > 可以通过集合接收
c > 可以通过map
2,若查询出的数据有多条,一定不能通过实体类对家接收,此时会抛异常TooManyResultsException
a > 可以通过一个实体类类型的list集合接收
b > 可以通过一个map类型的list集合接收
c > 可以在mapper接口的方法上添加@MapKey注解,此时将每条数据转化的map集合作为值,注解标注的某个字段作为键,放在同一个map中
mybatis的模糊查询,三种写法
1、
select * from user where username like '%${username}%'
存在SQL注入风险
2、select * from user where username like concat('%',#{username},'%')
3、select * from user where username like "%"#{username}"%"
mybatis插入获取自增主键
在映射文件的SQL语句中,使用useGeneratedKeys和keyProperty参数
useGeneratedkeys:设置当前标签中的sqL使用了自增的主键
keyProperty:将自增的主键的值赋值给传输到映射文件中参数的某个属性
mybatis 解决字段名和属性名映射问题的三种方法
1、通过resultMap自定义映射关系
resultMap的子标签:
id:主键字段
result:普通字段
association:关联字段,一对一
collection:关联字段,一对多
2、查询字段起别名 select user_id userId from user
3、通过全局配置mapUnderscoreToCamelCase的值为true,将下划线_自动映射为驼峰
解决一对一关联映射关系,
a > 级联属性复制
b > association
c > 分步查询(推荐,实现延迟加载,mybatis默认不开启)
解决一对多关联映射关系,
a > collection
b > 分步查询(推荐,实现延迟加载,mybatis默认不开启)
可以通过子查询参数 fetchType设置或通过全局mybatis参数进行控制:
lazyLoadingEnabled: 全局懒加载开关 (默认false)
aggressiveLazyLoading: 任意方法触发加载 (默认false)
mybatis的缓存
一级缓存:默认开启,sqlSession级别,对于同一个sqlSession查询的数据会进行缓存
一级缓存失效:
1、不同sqlSession对应不同一级缓存
2、同一sqlSession不同查询调节剂
3、同一sqlSession俩次查询间进行过其他操作
4、同一sqlSession俩次查询间清除了缓存
二级缓存:SQLSessionFactory级别,同一sqlSessionFactory创建的sqlSession查询的数据进行缓存
二级缓存开启条件:
a>在核心配置文件中,设置全局配置属性cacheEnabled=“true”,默认为true,不需要设置
b>在映射文件中设置标签<cache/>
c>级缓存必须在SqISession关闭或提交之后有效
d>查询的数据所转换的实体类类型必须实现序列化的接口
使二级缓存失效的情况:
两次查询之间执行了任意的增剧改,会使一级和二级缓存同时失效
》
二级缓存相关配置:
- eviction是缓存的淘汰算法,可选值有"LRU"、“FIFO”、“SOFT”、“WEAK”,缺省值是>- LRU
- flashInterval指缓存过期时间,单位为毫秒,60000即为60秒,缺省值为空,即只要容量足够,永不过期
- size指缓存多少个对象,默认值为1024
- readOnly是否只读,如果为true,则所有相同的sql语句返回的是同一个对象(有助于提高性能,但并发操作同一条数据时,可能不安全),如果设置为false,则相同的sql,后面访问的是cache的clone副本。
- type:二级缓存类型,默认使用mybatis,可以使用第三方缓存
》缓存查询顺序:先查大范围
1、先查询二级缓存,因为二级缓存中可能会有其他程序已经查出来的数据,可以拿来直接使用。
2、如果二级缓存没有命中,再查询一级缓存(未close或commit)
3、如果一级缓存也没有命中,则查询数据库
4、SqlSession关闭之后,一级缓存中的数据会写入二级缓存
mybatis 批处理
1、循环调用
2、foreach标签
3、批处理(ps:千万记得关闭sqlSession)
这里只贴一个批处理
比较
:网传,都是在说批处理快
我的测试结果:
分析:
影响因素①:开启了日志打印
影响因素②:我插入的对象只有俩个字段(不知道)
批处理优点:不需设置分批次批量插入也可(当然,设置了可能更快呢?)
速度:还行
分批次插入,中间可以设置等待时间,避免一次性提交过多数据库压力过大
@Autowired
private SqlSessionFactory sqlSessionFactory;
@Override
public void insertBatch(List<Xxx> xxxs) {
// 批处理
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH, false);
XxxMapper mapper = sqlSession.getMapper(XxxMapper.class);
xxxs.stream().forEach(xxx -> {
mapper.insertZdDd(xxx);
});
sqlSession.commit();
sqlSession.clearCache();
sqlSession.close();
}