大家好,我是哪吒。
Mybatis中有一级缓存和二级缓存,默认情况下一级缓存是开启的,而且是不能关闭的。
一级缓存是指 SqlSession 级别的缓存,当在同一个
SqlSession 中进行相同的 SQL 语句查询时,第二次以后的查询不会从数据库查询,而是直接从缓存中获取,一级缓存最多缓存 1024 条
SQL。
二级缓存是指可以跨 SqlSession 的缓存。是 mapper 级别的缓存,对于 mapper 级别的缓存不同的 sqlsession 是可以共享的。
第一次发出一个查询 sql,sql 查询结果写入 sqlsession 的一级缓存中,缓存使用的数据结构是一个 map。
同一个 sqlsession 再次发出相同的 sql,就从缓存中取出数据。如果两次中间出现 commit 操作(修改、添加、删除),本 sqlsession 中的
一级缓存区域全部清空,下次再去缓存中查询不到所以要从数据库查询,从数据库查询到再写入缓存。
二级缓存的范围是mapper 级别(mapper同一个命名空间),mapper以命名空间为单位创建缓存数据结构,结构是 map。mybatis 的二
级缓存是通过 CacheExecutor 实现的。CacheExecutor 其实是 Executor 的代理对象。所有的查询操作,在 CacheExecutor 中都会先匹配
缓存中是否存在,不存在则查询数据库。
key:MapperID+offset+limit+Sql+所有的入参
具体使用需要配置:
ORM(Object Relational Mapping),对象关系映射,是一种为了解决关系型数据库数 据与简单Java对象(POJO)的映射关系的技术。简
单的说,ORM是通过使用描述对象和 数据库之间映射的元数据,将程序中的对象自动持久化到关系型数据库中。
频繁创建数据库连接对象、释放,容易造成系统资源浪费,影响系统性能。可以使用连接池 解决这个问题。但是使用jdbc需要自己实现连接
池。
sql语句定义、参数设置、结果集处理存在硬编码。实际项目中sql语句变化的可能性较大, 一旦发生变化,需要修改java代码,系统需要重
新编译,重新发布。不好维护。
使用preparedStatement向占有位符号传参数存在硬编码,因为sql语句的where条件不一 定,可能多也可能少,修改sql还要修改代码,系
统不易维护。
结果集处理存在重复代码,处理麻烦。如果可以映射成Java对象会比较方便。
(1)数据库链接创建、释放频繁造成系统资源浪费从而影响系统性能,如果使用数据库连接 池可解决此问题。
解决:在mybatis-config.xml中配置数据链接池,使用连接池管理数据库连接。
(2)Sql语句写在代码中造成代码不易维护,实际应用sql变化的可能较大,sql变动需要改变 java代码。
解决:将Sql语句配置在XXXXmapper.xml文件中与java代码分离。
(3)向sql语句传参数麻烦,因为sql语句的where条件不一定,可能多也可能少,占位符需 要和参数一一对应。
解决: Mybatis自动将java对象映射至sql语句。
(4)对结果集解析麻烦,sql变化导致解析代码变化,且解析前需要遍历,如果能将数据库记 录封装成pojo对象解析比较方便。
解决:Mybatis自动将sql执行结果映射至java对象。
(1)优点
基于SQL语句编程,相当灵活,不会对应用程序或者数据库的现有设计造成任何影响,SQL 写在XML里,解除sql与程序代码的耦合,便于统一管理;提供XML标签,支持编写动态 SQL语句,并可重用。
与JDBC相比,减少了50%以上的代码量,消除了JDBC大量冗余的代码,不需要手动开关连接。
很好的与各种数据库兼容(因为MyBatis使用JDBC来连接数据库,所以只要JDBC支持的数 据库MyBatis都支持)
(2)缺点
SQL语句的编写工作量较大,尤其当字段多、关联表多时,对开发人员编写SQL语句的功底 有一定要求。
SQL语句依赖于数据库,导致数据库移植性差,不能随意更换数据库。
(1)映射关系
MyBatis 是一个半自动映射的框架,配置Java对象与sql语句执行结果的对应关系,多表关联关系配置简单。
Hibernate 是一个全表映射的框架,配置Java对象与数据库表的对应关系,多表关联关系配置复杂。
(2)SQL优化和移植性
Hibernate 对SQL语句封装,提供了日志、缓存、级联(级联比 MyBatis 强大)等特性, 此外还提供 HQL(Hibernate Query Language)
操作数据库,数据库无关性支持好,但 会多消耗性能。如果项目需要支持多种数据库,代码开发量少,但SQL语句优化困难。
MyBatis 需要手动编写 SQL,支持动态 SQL、处理列表、动态生成表名、支持存储过程。 开发工作量相对大些。直接使用SQL语句操作数据库,不支持数据库无关性,但sql语句优 化容易。
(3)开发难易程度和学习成本
Hibernate 是重量级框架,学习使用门槛高,适合于需求相对稳定,中小型的项目,比如: 办公自动化系统。
MyBatis 是轻量级框架,学习使用门槛低,适合于需求变化频繁,大型的项目,比如:互 联网电子商务系统。
JDBC 中使用对象 PreparedStatement 来抽象预编译语句,使用预编译。预编译阶段可以 优化 SQL 的执行。预编译之后的 SQL 多数情况下
可以直接执行,DBMS 不需要再次编 译,越复杂的SQL,编译的复杂度将越大,预编译阶段可以合并多次操作为一个操作。同时 预编译语句对象可以重复利用。把一个 SQL 预编译后产生的 PreparedStatement 对象缓 存下来,下次对于同一个SQL,可以直接使用这个缓存的
PreparedState 对象。Mybatis 默认情况下,将对所有的 SQL 进行预编译。
使用foreach标签,foreach的主要用在构建in条件中,它可以在SQL语句中进行迭代一个集合。foreach标签的 属性主要有item,index,collection,open,separator,close。
在使用foreach的时候最关键的也是最容易出错的就是collection 属性,该属性是必须指定 的,但是在不同情况下,该属性的值是不一样的,
主要有以下情况:
Mybatis内置的ExecutorType有3种,默认为simple,该模式下它为每个语句的执行创建一 个新的预处理语句,单条提交sql;而batch模式重
复使用已经预处理的语句,并且批量执行 所有更新语句,显然batch性能将更优; 但batch模式也有自己的问题,比如在Insert操作 时,在
事务没有提交之前,是没有办法获取到自增的id,
(1)对于支持主键自增的数据库(MySQL)
<insert id="insertUser" useGeneratedKeys="true" keyProperty="userId" >
insert into user(
user_name, user_password, create_time)
values
(#{userName}, #{userPassword} , #{createTime, jdbcType=TIMESTAMP})
</insert>
parameterType 可以不写,Mybatis可以推断出传入的数据类型。如果想要访问主键,那 么应当parameterType 应当是java实体或者
Map。这样数据在插入之后 可以通过ava实体 或者Map 来获取主键值。通过 getUserId获取主键。
(2)不支持主键自增的数据库(Oracle)
可以使用<selectKey>标签来获取主键的值,这种方式不仅适用于不提供主键自增功能的数据库,也适用于提供主键自增功能的数据库。
<selectKey keyColumn="id" resultType="long" keyProperty="id" order="BEFORE">
</selectKey>
<insert id="insertUser" >
<selectKey keyColumn="id" resultType="long" keyProperty="userId" order="BEFORE">
SELECT USER_ID.nextval as id from dual
</selectKey>
insert into user(
user_id,user_name, user_password, create_time)
values
(#{userId},#{userName}, #{userPassword} , #{createTime, jdbcType=TIMESTAMP})
</insert>
此时会将Oracle生成的主键值赋予userId变量。这个userId 就是USER对象的属性,这样就 可以将生成的主键值返回了。如果仅仅是在
insert语句中使用但是不返回,此时 keyProperty=“任意自定义变量名”,resultType 可以不写。
Oracle 数据库中的值要设置为 BEFORE ,这是因为 Oracle中需要先从序列获取值,然后 将值作为主键插入到数据库中。
(1)通过在查询的SQL语句中定义字段名的别名,让字段名的别名和实体类的属性名一 致。
<select id="getOrder" parameterType="int" resultType="com.jourwon.pojo.Order">
select order_id id, order_no orderno ,order_price price form orders where order_id=#{id};
</select>
(2)通过来映射字段名和实体类属性名的一一对应的关系
<select id="getOrder" parameterType="int" resultMap="orderResultMap">
select * from orders where order_id=#{id}
</select>
<resultMap type="com.jourwon.pojo.Order" id="orderResultMap">
<!–用id属性来映射主键字段–>
<id property="id" column="order_id">
<!–用result属性来映射非主键字段,property为实体类属性名,column为数据库表中的属性–>
<result property ="orderno" column ="order_no"/>
<result property="price" column="order_price" />
</reslutMap>
🏆哪吒多年工作总结:Java学习路线总结,搬砖工逆袭Java架构师。