目录
一、什么是MyBatis?
1、MyBatis 是一款优秀的持久层框架
持久化是将程序数据在持久状态和瞬时状态间转换的机制。
2、为什么需要持久化服务呢?
那是由于内存本身的缺陷引起的,内存断电后数据会丢失,内存过于昂贵,所以需要持久化到缓存到外存.
3、什么是持久层?
- 完成持久化工作的代码块 . ----> dao层 【DAO (Data Access Object) 数据访问对象】
- 大多数情况下特别是企业级应用,数据持久化往往也就意味着将内存中的数据保存到磁盘上加以固化,而持久化的实现过程则大多通过各种关系数据库来完成。
- 对于应用系统而言,数据持久功能大多是必不可少的组成部分。之所以要独立出一个“持久层”的概念,而不是“持久模块”,“持久单元”,也就意味着,我们的系统架构中,应该有一个相对独立的逻辑层面,专著于数据持久化逻辑的实现.
- 与系统其他部分相对而言,这个层面应该具有一个较为清晰和严格的逻辑边界。 【说白了就是用来操作数据库存在的!】
4、为什么需要Mybatis?
- Mybatis就是帮助程序猿将数据存入数据库中 , 和从数据库中取数据 .
- 传统的jdbc操作 , 有很多重复代码块 .比如 : 数据取出时的封装 , 数据库的建立连接等等... , 通过框架可以减少重复代码,提高开发效率 .
- MyBatis 是一个半自动化的ORM框架 (Object Relationship Mapping) -->对象关系映射
- 所有的事情,不用Mybatis依旧可以做到,只是用了它,所有实现会更加简单!
5、MyBatis的优点
- 简单易学:本身就很小且简单。没有任何第三方依赖,最简单安装只要两个jar文件+配置几个sql映射文件就可以了,易于学习,易于使用,通过文档和源代码,可以比较完全的掌握它的设计思路和实现。
- 灵活:mybatis不会对应用程序或者数据库的现有设计强加任何影响。 sql写在xml里,便于统一管理和优化。通过sql语句可以满足操作数据库的所有需求。
- 解除sql与程序代码的耦合:通过提供DAO层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测试。sql和代码的分离,提高了可维护性。
- 提供xml标签,支持编写动态sql。
二、MyBatis连接数据库
1.导包
2.编写MyBatis核心文件
作用:配置数据库连接信息,绑定Mapper接口配置文件3.编写MyBatis工具类
读取配置文件 ,使用mybatis获取sqlSessionFactory对象String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
4.创建实体类:对应表结构
5.编写Mapper接口类
绑定Mapper接口,利用sql语言实现接口中方法6.编写Mapper.xml核心配置文件
7.实现增删改查传递参数方式:
1.直接在方法中传递参数
在接口方法的参数前加 @Param属性
Sql语句编写的时候,直接取@Param中设置的值即可,不需要单独设置参数类型2.使用万能的Map
在接口方法中,参数直接传递Map;
编写sql语句的时候,需要传递参数类型,参数类型为map
在使用方法的时候,Map的 key 为 sql中取的值即可,没有顺序要求补充:模糊查询like语句应该怎么写?
- 在java代码中加入sql通配符
string wildcardname = “%smi%”; list<name> names = mapper.selectlike(wildcardname); <select id=”selectlike”> select * from foo where bar like #{value} </select>
- 在sql语句中拼接通配符${},会引起sql注入
string wildcardname = “smi”; list<name> names = mapper.selectlike(wildcardname); <select id=”selectlike”> select * from foo where bar like "%"#{value}"%" </select>
#{}和${}的区别?
# {} 是预编译处理,像传进来的数据会加个" "(#将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号). $ {} 就是字符串替换。. 直接替换掉占位符。. $方式一般用于传入数据库对象,例如传入表名. 使用 $ {} 的话会导致 sql 注入。
三、配置分析
1、配置文件包含了深深影响 MyBatis 行为的设置和属性信息。 配置文档的顶层结构如下:
- configuration(配置)
- properties(属性)
- settings(设置)
- typeAliases(类型别名)
- typeHandlers(类型处理器)
- objectFactory(对象工厂)
- plugins(插件)
- environments(环境配置)
- environment(环境变量)
- transactionManager(事务管理器)
- dataSource(数据源)
- databaseIdProvider(数据库厂商标识)
- mappers(映射器)
2、environments(环境配置)
<environments default="development"> <environment id="development"> <transactionManager type="JDBC"> <property name="..." value="..."/> </transactionManager> <dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource> </environment> </environments>
(1)事务管理器(transactionManager):
- JDBC – 这个配置直接使用了 JDBC 的提交和回滚设施,它依赖从数据源获得的连接来管理事务作用域。
- MANAGED – 这个配置几乎没做什么。它从不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文)。 默认情况下它会关闭连接。然而一些容器并不希望连接被关闭,因此需要将 closeConnection 属性设置为 false 来阻止默认的关闭行为。
(2)数据源(dataSource)
UNPOOLED– 这个数据源的实现会每次请求时打开和关闭连接。 5 种属性:
driver
– 这是 JDBC 驱动的 Java 类全限定名url
– 这是数据库的 JDBC URL 地址。username
– 登录数据库的用户名。password
– 登录数据库的密码。defaultTransactionIsolationLevel
– 默认的连接事务隔离级别。defaultNetworkTimeout
– 等待数据库操作完成的默认网络超时时间(单位:毫秒)。POOLED– 这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间。能使并发 Web 应用快速响应请求。
JNDI – 这个数据源实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的数据源引用。这种数据源配置只需要两个属性:
initial_context
– 这个属性用来在 InitialContext 中寻找上下文,如果忽略,那么将会直接从 InitialContext 中寻找 data_source 属性。data_source
– 这是引用数据源实例位置的上下文路径。提供了 initial_context 配置时会在其返回的上下文中进行查找,没有提供时则直接在 InitialContext 中查找。数据源第三方实现:dbcp,c3p0,druid。
(3)Mappers元素
告诉 MyBatis 到哪里去找到这些语句。你可以使用相对于类路径的资源引用,或完全限定资源定位符(包括
file:///
形式的 URL),或类名和包名等。(4)properties优化:
(5)typeAliases优化:
类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean
(6)Settings:
- 懒加载 lazyLoadingEnabled
- 日志实现 logImpl
- 缓存开启关闭 cacheEnabled
(7)生命周期和作用域
SqlSessionFactoryBuilder 的作用在于创建 SqlSessionFactory,创建成功后失去作用,所以它只能存在于创建 SqlSessionFactory 的方法中,而不要让其长期存在。因此 最佳作用域是方法作用域(也就是局部方法变量)。
SqlSessionFactory 可以被认为是一个数据库连接池,作用是创建 SqlSession 接口对象。SqlSessionFactory 的生命周期存在于整个 MyBatis 的应用之中,所以要长期保存它,直至不再使用 MyBatis 应用,所以可以认为 SqlSessionFactory 的生命周期就等同于 MyBatis 的应用周期。因此在一般的应用中我们往往希望 SqlSessionFactory 作为一个单例,让它在应用中被共享。最佳作用域是应用作用域。
SqlSession 就相当于一个数据库连接(Connection 对象),可以在一个事务里面执行多条 SQL,然后通过它的 commit、rollback 等方法,提交或者回滚事务。所以它应该存活在一个业务请求中,处理完整个请求后,应该关闭这条连接,让它归还给 SqlSessionFactory,否则数据库资源就很快被耗费精光,系统就会瘫痪,所以用 try...catch...finally... 语句来保证其正确关闭。所以 SqlSession 的最佳的作用域是请求或方法作用域。public class MybatisUtils { private static SqlSessionFactory sqlSessionFactory; static { try { String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } catch (IOException e) { e.printStackTrace(); } } //获取SqlSession连接 public static SqlSession getSession(){ return sqlSessionFactory.openSession(); } }
四、ResultMap
1、null问题:实体类pojo和数据库字段名不一致
解决方法:取别名或者使用ResultMap
2、自动映射(利用实体类的属性作为HashMap模型)
3、手动映射(手动映射)
五、分页
1、日志工具
- SLF4J
- Apache Commons Logging
- Log4j 2
- Log4j
- JDK logging
标准日志实现:指定 MyBatis 应该使用哪个日志记录实现。如果此设置不存在,则会自动发现日志记录实现。
<settings> <setting name="logImpl" value="STDOUT_LOGGING"/> </settings>
2、Log4j
<settings> <setting name="logImpl" value="LOG4J"/> </settings>
3、分页
方法一:limit分页,利用Map传参,修改sql语句
<select id="selectUser" parameterType="map" resultType="user"> select * from user limit #{startIndex},#{pageSize} </select>
方法二:RowBounds分页,在Java代码层面实现分页
int currentPage = 2; //第几页 int pageSize = 2; //每页显示几个 RowBounds rowBounds = new RowBounds((currentPage-1)*pageSize,pageSize); //通过session.**方法进行传递rowBounds,[此种方式现在已经不推荐使用了] List<User> users = session.selectList("com.kuang.mapper.UserMapper.getUserByRowBounds", null, rowBounds);
方法三:分页插件PageHelper
六、注解开发
mybatis最初配置信息是基于 XML ,映射语句(SQL)也是定义在 XML 中的。而到MyBatis 3提供了新的基于注解的配置。不幸的是,Java 注解的的表达力和灵活性十分有限。最强大的 MyBatis 映射并不能用注解来构建
@select () @update () @Insert () @delete ()
@Param
用于给方法参数起一个名字。以下是总结的使用原则:
- 在方法只接受一个参数的情况下,可以不使用@Param。
- 在方法接受多个参数的情况下,建议一定要使用@Param注解给参数命名。
- 如果参数是 JavaBean , 则不能使用@Param。
- 不使用@Param注解时,参数只能有一个,并且是Javabean。
#与$的区别:
#{} 的作用主要是替换预编译语句(PrepareStatement)中的占位符?
${} 的作用是直接进行字符串替换
七、多对一和一对多
1、多对一:association
方法一:查询嵌套
<resultMap id="StudentTeacher" type="Student"> <!--association关联属性 property属性名 javaType属性类型 column在多的一方的表中的列名--> <association property="teacher" column="{id=tid,name=tid}" javaType="Teacher" select="getTeacher"/> </resultMap> <!--这里传递过来的id,只有一个属性的时候,下面可以写任何值 association中column多参数配置: column="{key=value,key=value}" 其实就是键值对的形式,key是传给下个sql的取值名称,value是片段一中sql查询的字段名。--> <select id="getTeacher" resultType="teacher"> select * from teacher where id = #{id} and name = #{name} </select>
方法二:结果嵌套
<select id="getStudents2" resultMap="StudentTeacher2" > select s.id sid, s.name sname , t.name tname from student s,teacher t where s.tid = t.id </select> <resultMap id="StudentTeacher2" type="Student"> <id property="id" column="sid"/> <result property="name" column="sname"/> <!--关联对象property 关联对象在Student实体类中的属性--> <association property="teacher" javaType="Teacher"> <result property="name" column="tname"/> </association> </resultMap>
2、一对多:collection
方法一:查询嵌套
<select id="getTeacher2" resultMap="TeacherStudent2"> select * from teacher where id = #{id} </select> <resultMap id="TeacherStudent2" type="Teacher"> <!--column是一对多的外键 , 写的是一的主键的列名--> <collection property="students" javaType="ArrayList" ofType="Student" column="id" select="getStudentByTeacherId"/> </resultMap> <select id="getStudentByTeacherId" resultType="Student"> select * from student where tid = #{id} </select>
方法二:结果嵌套
<select id="getTeacher" resultMap="TeacherStudent"> select s.id sid, s.name sname , t.name tname, t.id tid from student s,teacher t where s.tid = t.id and t.id=#{id} </select> <resultMap id="TeacherStudent" type="Teacher"> <result property="name" column="tname"/> <collection property="students" ofType="Student"> <result property="id" column="sid" /> <result property="name" column="sname" /> <result property="tid" column="tid" /> </collection> </resultMap>
八、动态SQL
动态SQL指的是根据不同的查询条件 , 生成不同的Sql语句.
1、if 语句
<select id="queryBlogIf" parameterType="map" resultType="blog"> select * from blog where <if test="title != null"> title = #{title} </if> <if test="author != null"> and author = #{author} </if> </select>
如果 author 等于 null,那么查询语句为 select * from user where title=#{title},但是如果title为空呢?那么查询语句为 select * from user where and author=#{author},这是错误的 SQL 语句,如何解决呢?
2、where
这个“where”标签会知道如果它包含的标签中有返回值的话,它就插入一个‘where’。此外,如果标签返回的内容是以AND 或OR 开头的,则它会剔除掉。<select id="queryBlogIf" parameterType="map" resultType="blog"> select * from blog <where> <if test="title != null"> title = #{title} </if> <if test="author != null"> and author = #{author} </if> </where> </select>
3、set:
set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号(这些逗号是在使用条件语句给列赋值时引入的)。<!--注意set是用的逗号隔开--> <update id="updateBlog" parameterType="map"> update blog <set> <if test="title != null"> title = #{title}, </if> <if test="author != null"> author = #{author} </if> </set> where id = #{id}; </update>
4、choose-when-otherwise
<select id="queryBlogChoose" parameterType="map" resultType="blog"> select * from blog <where> <choose> <when test="title != null"> title = #{title} </when> <when test="author != null"> and author = #{author} </when> <otherwise> and views = #{views} </otherwise> </choose> </where> </select>
5、SQL片段
提取片段
<sql id="if-title-author"> <if test="title != null"> title = #{title} </if> <if test="author != null"> and author = #{author} </if> </sql>
引用SQL片段
<select id="queryBlogIf" parameterType="map" resultType="blog"> select * from blog <where> <!-- 引用 sql 片段,如果refid 指定的不在本文件中,那么需要在前面加上 namespace --> <include refid="if-title-author"></include> <!-- 在这里还可以引用其他的 sql 片段 --> </where> </select>
6、Foreach
对集合进行遍历
<select id="selectPostIn" resultType="domain.blog.Post"> SELECT * FROM POST P WHERE ID in <foreach item="item" index="index" collection="list" open="(" separator="," close=")"> #{item} </foreach> </select>
九、缓存
1、什么是缓存 [ Cache ]?
- 存在内存中的临时数据。
- 将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统性能问题。
2、为什么使用缓存?
- 减少和数据库的交互次数,减少系统开销,提高系统效率。
3、 什么样的数据能使用缓存?
- 经常查询并且不经常改变的数据。
4、MyBatis缓存
- MyBatis包含一个非常强大的查询缓存特性,它可以非常方便地定制和配置缓存。缓存可以极大地提升查询效率。
- MyBatis系统中默认定义了两级缓存:一级缓存和二级缓存。
(1)默认情况下,只有一级缓存开启。(SqlSession级别的缓存,也称为本地缓存)
(2)二级缓存需要手动开启和配置,他是基于namespace级别的缓存。
(3)为了提高扩展性,MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存5、一级缓存(也叫本地缓存)
- 与数据库同一次会话期间查询到的数据会放在本地缓存中。
- 以后如果需要获取相同的数据,直接从缓存中拿,没必须再去查询数据库;
6、一级缓存失效四种情况:
- sqlSession不同
- sqlSession相同,查询条件不同
- sqlSession相同,两次查询之间执行了增删改操作!
- sqlSession相同,手动清除一级缓存 session.clearCache();//手动清除缓存
7、二级缓存(也叫全局缓存)
一级缓存作用域太低了,所以诞生了二级缓存
基于namespace级别的缓存,一个名称空间,对应一个二级缓存;
工作机制
- 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中;
- 如果当前会话关闭了,这个会话对应的一级缓存就没了;但是我们想要的是,会话关闭了,一级缓存中的数据被保存到二级缓存中;
- 新的会话查询信息,就可以从二级缓存中获取内容;
- 不同的mapper查出的数据会放在自己对应的缓存(map)中;
<setting name="cacheEnabled" value="true"/>
8、结论:
- 只要开启了二级缓存,我们在同一个Mapper中的查询,可以在二级缓存中拿到数据
- 查出的数据都会被默认先放在一级缓存中
- 只有会话提交或者关闭以后,一级缓存中的数据才会转到二级缓存中
9、第三方缓存实现--EhCache
导包-->在mappe.xml开启对应缓存-->编写ehcache.xml文件,配置缓存
高频面试点:
Mysql 引擎
InnodB底层原理
索引
索引优化