SSM框架学习之MyBatis浅谈(二)
关系型数据库和非关系型数据库
关系型数据库,是指采用了关系模型来组织数据的数据库,其以行和列的形式存储数据,以便于用户理解,关系型数据库这一系列的行和列被称为表,一组表组成了数据库。非关系型数据库(NoSql,Not Only SQL)用于区别于关系型数据库,它们不保证关系数据的ACID特性。简单来说,关系型数据库一般数据都和系统中的对象有关联,用某个对象的属性去接收数据库的字段,例如:‘tb_user’表中的‘username’就可以和系统‘User’对象的'username'相关联;而非关系型数据库就不一定是这样,数据库中的数据不一定要有个系统对象相匹配,甚至数据的存储都不一定要通过Sql!例如Redis就可以通过set和get方法存储、查询数据。
常见的关系型数据库:Oracle、SQLServer、DB2、Mysql
常见的非关系型数据库:Mongodb、Redis
Mybatis的介绍
Mybatis是一个半ORM(对象关系映射)框架,它内部封装了JDBC,开发时 只需要关注 SQL 语句本身,不需要花费精力去处理加载驱动、创建连接、创建 statement 等繁杂的过程。程序员直接编写原生态 sql,可以严格控制 sql 执行性 能,灵活度高。至于说为什么说是“半ORM”,是因为它和传统的关系型数据库(如Hibernate)不太一样,Mybatis虽然数据和对象有关系但是它的数据和系统对象关系又没有那么紧密,往往需要程序员手写Sql和处理数据和对象的映射关系。
优点:
- 与JDBC相比,减少了 50%以上的代码量,消除了 JDBC 大量冗余的代码,不需要手动开关连接;
- 能够与Spring很好的集成;
- 基于SQL语句编程,不会对应用程序或者数据库的现有设计造成任何影响,SQL写在XML里,解除sql与程序代码的耦合,便于统一管理;提供XML标签,支持编写动态SQL语句,并可重用,非常灵活;
- 很好的与各种数据库兼容,因为MyBatis使用JDBC来连接数据库,所以只要JDBC支持的数据库MyBatis都支持;
- 提供映射标签,支持对象与数据库的 ORM 字段关系映射;提供对象关系映射 标签,支持对象关系组件维护。
缺点:
- SQL语句的编写工作量较大,尤其当字段多、关联表多时,对开发人员编写 SQL 语句的功底有一定要求。
- SQL语句依赖于数据库,导致数据库移植性差,不能随意更换数据库。
和Hibernate的不同:
Mybatis不完全是一个ORM框架,因为MyBatis需要 程序员自己编写Sql语句,可以严格控制sql执行性能,灵活度高,非常适合对关系数据模型要求不高的软件开发,因为这类软件需求变化频繁,一但需 求变化要求迅速输出成果。但是灵活的前提是 mybatis 无法做到数据库无关性, 如果需要实现支持多种数据库的软件,则需要自定义多套 sql 映射文件,工作量大。Hibernate关系映射强,可以减少许多Sql的书写,减少开发工作量,但是性能方面的控制就不一定有Mybatis强,特别是一些复杂多对象数据的查询。
Mybatis的分页查询
Mybatis 使用 RowBounds 对象进行分页,它是针对 ResultSet 结果集执行的内存分页,而非物理分页。可以在 sql 内直接书写带有物理分页的参数来完成物理分页功能,也可以使用分页插件来完成物理分页。(分页插件的基本原理是在插件的拦截方法内拦截待执行的 sql,然后重写 sql,添加对应的物理分页语句和物理分页参数。例如使用PageHelper插件时,原sql是‘select * from user’,在插件的拦截器方法中拦截sql,拼接上'limit ?,? ')。
Mybatis常用分页插件:Pagehelper、Mybatis-plus
Mybatis的缓存机制
Mybatis的缓存机制
为了提高查询效率Mybatis和Mysql一样内置了缓存机制,而且分为两种级别:一级缓存(默认开启)、二级缓存(配置开启)。
一级缓存
每当我们使用MyBatis开启一次和数据库的会话,MyBatis会创建出一个SqlSession对象表示一次数据库会话。为了减少资源浪费,MyBatis会在表示会话的SQLSession对象中建立一个简单的缓存,将每次查询到的结果缓存起来,当下次查询的时候,如果判断现有个完全一样的查询,会直接从缓存中将结果取出来,返回给用户,不需要再进行一次查询数据库了。
Mybatis在开启一个数据库会话时,会创建一个新的SqlSession对象,SqlSession对象中会有一个Executor对象,Executor对象中持有一个PerpetualCache缓存对象;当会话结束时,SqlSession对象及其内部的Executor对象还有PerpetualCache对象也一并释放掉。所以一级缓存的作用域是SqlSession。
工作流程:Mybatis开启一次查询时会根据statementId(sql语句ID)、Params(sql参数)、rowBounds(查询范围)来构建一个key值,然后根据这个key去缓存里匹配,如果命中则直接返回结果无需再查询数据库;如果没有命中则去查询数据库,将查询到的数据作为value存到缓存中去,然后返回给客户端数据。
判断两次查询是否一样:根据‘key’值,即查询的statementId、Sql、Params、rowBounds。
二级缓存
二级缓存与一级缓存其机制相同,默认也是采用PerpetualCache缓存对象存储数据。不同在于二级缓存存储作用域为 Namespace,并且可自定义存储源,如 Ehcache、OScache。作用域为Namespace 是指对该Namespace 对应的配置文件中所有的 select 操作结果都缓存,这样不同线程之间就可以共用二级缓存。而且一级缓存是默认开启,二级缓存需要手动配置!
二级缓存开启配置:
- 在总配置Mybatis-config.xml文件中配置:<setting name="cacheEnabled" value="true"/>
- 在XxxMapper.xml文件中配置:<cache></cache>
- 在statement配置中使用缓存:useCache="true"(默认配置也是true,所以这一步可以省略)
- 如果查询数据关联Bean对象,则该对象需要实现Serializable接口(可序列化)
Mybatis缓存数据清除:
- 与数据库会话结束,释放SqSession对象时,会清除PerpetualCache缓存对象(缓存对象都没了,数据自然也被清除了)。
- SqlSession调用close()方法,会清除PerpetualCache缓存对象。
- SqlSession调用clearCache()方法,只会清空PerpetualCache缓存对象中的数据,对象仍可使用。
- SqlSession执行了任何一个update(),delete(),insert()方法,都会清空PerpetualCache缓存对象中的数据,对象仍可使用。
Mybatis的事务管理
Mybatis是在JDBC的基础上开发的数据库连接框架,所以其实Mybatis的事务管理和JDBC基本一样:
- mybatis框架自己管理事务,自己采用原生的JDBC代码去管理事务:conn.setAutoCommit(false);(开启事务,关闭自动提交)。
- 如果你编写的代码是:SqlSession sqlSession=sqlSessionFactory.openSession(true); 表示没有开启事务。因为这种方式压根不会执行:conn.setAutoCommit(false),如果没有执行conn.setAutoCommit(false)那么autoCommit(自动提交)就是true,就表示无论事务有没有中断或者方法有没有执行完毕,Sql都会自动提交,也就是相当于没开启事务!
在mybatis-config.xml配置事务:
<environment id="development"> <!--transactionManager事务管理,可以使用【JDBC | MANAGED】 JDBC:在SQL执行时,事务管理默认交给JDBC管理,事务的提交和回滚需要手动来进行处理 MANAGED:事务交由其他框架管理,如spring --> <transactionManager type="JDBC" /> <!--dataSource表示数据源,type属性表示连接池使用方式,可以使用【POOLED | UNPOOLED | JNDI】 POOLED:表示使用数据库连接池缓存数据【通常选择将连接池交给数据库】 UNPOOLED:表示不使用数据库连接池缓存数据 JNDI:表示使用上下文中的数据源连接池 --> <dataSource type="POOLED"> <!--获取连接驱动,${}获取属性文件中的属性--> <property name="driver" value="${jdbc.driver}"/> <!--获取连接字符串--> <property name="url" value="${jdbc.url}"/> <!--获取连接用户名--> <property name="username" value="${jdbc.username}"/> <!--获取连接密码--> <property name="password" value="${jdbc.password}"/> </dataSource> </environment>
补充:
一般使用中都会用‘try-catch-finally’包裹住执行sql的代码片段,一方面可以捕捉异常便于后续问题的排查,另一方面也是便于事务管理,可以根据不同异常选择在catch中Commit事务还是Rollback。当然最后千万要记得在finally中close连接(connection)和调用对象(statement)!