Mybatis

目录

一、什么是MyBatis?

二、MyBatis连接数据库

三、配置分析

四、ResultMap

五、分页

六、注解开发

七、多对一和一对多

八、动态SQL

九、缓存


一、什么是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 行为的设置和属性信息。 配置文档的顶层结构如下:

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底层原理
索引
索引优化

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值