框架:Mybatis

ORM框架(object relational mapping)

官方文档: https://mybatis.org/mybatis-3/zh/index.html

原理

执行流程

  1. 应用程序调用Mybatis框架接口要数据
  2. mbatis找到数据库中找要数据
    1. 在项目资源路径下找mybatis-config.xml配置文件 定位哪个数据库, 找到mappers配置文件
    2. 通过mappers配置文件找到对应sql标签, 根据parameterType属性 传参 执行语句
    3. 基于sql标签的 resultMap 属性, 把返回的数据库记录封装在Javabean对象中
    4. 把多个查询结果对象装在一个结果集合中
  3. 返回一个结果集合
    ​​​​在这里插入图片描述
    在这里插入图片描述
  • SqlSession 接口
    SqlSession代表一种可关闭的连接,表示的是数据库客户端和数据库服务端之间的会话,用来连接数据库,执行sql语句

    1. 默认实现类:DefaultSqlSession
    2. 常用实现类:SqlSessionTemplate (纳入了spring bean生命周期的管理)
  • SessionFactory 接口
    是生产 SqlSession 的工厂,用来打开SqlSession会话

    1. 默认实现类:DefaultSqlSessionFactory

每次打开-关闭SqlSession 浪费资源,使用数据库连接池:一次连接用完 关闭SqlSession实例时,只是把数据库连接对象放回到对象池中,并没有直接销毁

  • SqlSessionFactoryBean
    是生产 SqlSessionFactory 的工厂bean

1. 数据库 配置

构建 SqlSessionFactory

https://mybatis.org/mybatis-3/zh/configuration.html

作用: 配置 数据库连接信息mappers配置文件地址

XMLConfigBuilder类解析xml文件,把configuration标签信息保存在 Configuration类对象里,environment 标签信息保存在Configuration.Environment 对象里

  • 2种方式配置
    1. mybatis-config.xml 配置文件
    2. application.properties + @Mapper接口 (spring-boot项目)
//配置文件路径
String resource = "path/mybatis-config.xml";
// 构建 SqlSessionFactory
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 通过 SqlSession 实例来直接执行已映射的 SQL 语句
SqlSession session = sqlSessionFactory.openSession();

作用域(Scope)和生命周期

  • 依赖注入框架可以创建线程安全的、基于事务的 SqlSession 和映射器,并将它们直接注入到你的 bean 中,因此可以直接忽略它们的生命周期
  1. SqlSessionFactoryBuilder
    一旦方法创建了 SqlSessionFactory 就不再需要它, 因此最佳作用域是 方法作用域
  2. SqlSessionFactory
    一旦被创建就应该在应用的运行期间一直存在, 最佳作用域是 应用作用域, 最简单的就是使用单例模式或者静态单例模式
  3. SqlSession
    每个线程都应该有它自己的 SqlSession 实例, 线程不安全, 最佳的作用域是 请求或方法作用域 , 最好是HTTP 请求相似的作用域中, 每次收到 HTTP 请求就打开一个 SqlSession返回响应就关闭
  4. 映射器实例
    从技术层面上来讲,映射器实例的最大作用域与请求它们的 SqlSession 相同。但映射器实例应该在调用它们的方法中被获取,使用完毕之后丢弃, 所以最佳的作用域是 方法作用域

事物管理

在 MyBatis 中有两种类型的事务管理器( type="[JDBC | MANAGED]"):

  1. JDBC – 这个配置直接使用了 JDBC 的提交和回滚设施,它依赖从数据源获得的连接来管理事务作用域。
  2. MANAGED – 这个配置几乎没做什么。它从不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文)。 默认情况下它会关闭连接。然而一些容器并不希望连接被关闭,因此需要将 closeConnection 属性设置为 false 来阻止默认的关闭行为

settings标签 设置缓存

Mybatis缓存是基于PerpetualCache 的 HashMap本地缓存
在这里插入图片描述

一级缓存 localCacheScope

一级缓存 默认开启
一级缓存的共享范围就是一个SqlSession内部

mybatis-config.xml 配置文件

<settings>
  <setting name="localCacheScope" value="SESSION | STATEMENT"/>

同一个session 查询两次相同的对象。第一次会去数据库中取数据,但是第二次就不会访问数据库了,而是直接从session中取出来。

一级缓存失效情况:

  1. sqlSession不同。
  2. sqlSession相同,查询条件不同.(当前一级缓存中还没有这个数据)
  3. sqlSession相同,两次查询之间作用域(一级缓存Session)进行了 C/U/D 操作
  4. sqlSession相同,手动清除了一级缓存(Session flush 或 close)
二级缓存 cacheEnabled

二级缓存作用域是SessionFactory
可以在多个 SqlSession 直接共享缓存(在 SqlSession 关闭或提交 之后才会生效)

mybatis-config.xml 配置文件 <setting>标签

<settings>
  <setting name="cacheEnabled" value="true"/>

对应的mappers映射器配置 <cache>标签

<mapper namespace="me.gacl.mapping.userMapper">
<!-- 开启二级缓存这个必须开启,全部使用默认的配置-->
<cache/>

//TODO:cache标签详细用法

如果两次查询基于同一个SessionFactory,那么就从二级缓存中取数据,而不用到数据库里去取了

// 同一个 SessionFactory
        InputStream is = Resources.getResourceAsStream("myBatis-config.xml");
        SqlSessionFactoryBuilder builderObj = new SqlSessionFactoryBuilder();
        factory = builderObj.build(is);

//第一次查询 得到结果之后,mybatis自动将查询结果放入到当前用户的一级缓存
        sqlSession1 = factory.openSession();
        UserDao dao =  sqlSession.getMapper(userDao.class);
        User user = dao.findByUsertNo(1);
	// sqlSession关闭或提交, 触发从当前一级缓存中将User对象保存到二级缓存
        sqlSession.commit();
        sqlSession.close();
// 同SessionFactory第二次查询
		SqlSession session2 = factory.openSession();
        UserDao dao =  sqlSession.getMapper(userDao.class);
        User user = dao.findByUsertNo(1);
延迟加载

一对多查询时, 平时只查询"一", 用到多时在查询"多"

    <settings> 
         <!-- 打开延迟加载的开关 --> 
         <setting name="lazyLoadingEnabled" value="true" /> 
         <!-- 将积极加载改为消息加载即按需加载 --> 
         <setting name="aggressiveLazyLoading" value="false"/> 
    </settings> 
  • 原理
    使用 CGLIB 创建目标对象的代理对象,当调用目标方法时,进入拦截器方法
    调用 a.getB().getName(), 拦截器先把 B 查询回来

2. mappers映射器 配置

https://mybatis.org/mybatis-3/zh/sqlmap-xml.html

作用: 配置 mappers映射器,sql语句,输入输出对象信息
XMLMapperBuilder类会解析这个配置,拿到sql语句

  • 映射器可以使用2中形式

    1. mapper文件(xxxMapper.xml)
    2. mapper接口(interface xxxMapper.java)
  • 多种配置方式

    1. 实体类 + mapper文件
      mybatis-config.xml 里<mapper resource="">指向 Mapper.xml文件
      mapper.xml 的namespace为 实体类
    2. 实体类 + Mapper接口
      mybatis-config.xml 里<mapper class="">指向 Mapper.java接口
      实体类的Mapper接口里 用注解配置sql
    3. 实体类 + mapper文件 + Mapper接口
      mybatis-config.xml 里 <mapper resource="">指向 Mapper.xml文件
      mapper.xml 的namespace为 Mapper接口

1. 实体类对象

// 种类对象, 每个种类包含多个产品
public class Category {
    private int id;
    private String name;
    List<Product> products;
}

// 产品对象
public class Product {
    private int id;
    private String name;
    private float price;
    private Category category;
}

2. mapper 配置-XML方式

<mapper namespace="com.CategoryMapper">

1.1 查询所有
<!-- 
List<Category> cs = session.selectList("listCategory");
 -->
        <select id="listCategory" resultType="Category">
            select * from   category_     
        </select> 

1.2 一对一查询
<!-- 
Category c= session.selectOne("getCategory",3);
 -->
        <select id="getCategory" parameterType="_int" resultType="Category">
            select * from   category_  where id= #{id}   
        </select>


1.3 多条件查询
<!-- 
Map<String,Object> params = new HashMap<>();
params.put("id", 3);
params.put("name", "cat");
List<Category> cs = session.selectList("listCategoryByIdAndName",params);
 -->
        <select id="listCategoryByIdAndName"  parameterType="map" resultType="Category">
             select * from   category_  where id> #{id}  and name like concat('%',#{name},'%')
        </select>

2 增加
<!-- 
Category c = new Category();
c.setName("新增加的Category");
//调用addCategory对应的SQL语句,#{name}会自动获取c对象的name属性值
session.insert("addCategory",c);  
 -->
        <insert id="addCategory" parameterType="Category" >
            insert into category_ ( name ) values (#{name})   
        </insert>

3 删除
<!-- 
Category c = new Category();
c.setId(6);
session.delete("deleteCategory",c);
 -->
        <delete id="deleteCategory" parameterType="Category" >
            delete from category_ where id= #{id}  
        </delete>

4 修改
<!-- 
Category c= session.selectOne("getCategory",3);
c.setName("修改了的Category名称");
session.update("updateCategory",c);
 -->
        <update id="updateCategory" parameterType="Category" >
            update category_ set name=#{name} where id=#{id}   
        </update>
</mapper>
<!-- 
session.commit();
session.close();
 -->
查询参数映射 parameterType

参数类型(parameterType)

几乎总是可以根据参数对象的类型确定 javaType,除非该对象是一个 HashMap。这个时候,你需要显式指定 javaType 来确保正确的类型处理器(TypeHandler)被使用。

查询结果映射 resultMap

MyBatis 创建时的一个思想是:数据库不可能永远是你所想或所需的那个样子。 我们希望每个数据库都具备良好的第三范式或 BCNF 范式,可惜它们并不都是那样。
ResultMap 就是 MyBatis 想做一种数据库映射模式,完美适配所有的应用程序

隐式配置 resultType

用 resultType 属性, 不需要显式配置 也可以使用

直接写在 resultType , MyBatis 会在幕后自动创建一个 ResultMap,再根据属性名来映射列到 JavaBean 的属性上

  • 如果列名和属性名不能匹配上,可以在 SELECT 语句中设置列别名
        <select id="listCategory" resultType="Category">
            select 
                user_id             as "id",
                user_name           as "name",
                user_products       as "products"
            from   category_     
        </select> 
显式配置 resultMap

resultMap 显式配置

一对一, 多对一, 一对多, 多对多
  • 返回值个数
    1. selectOne()查询 返回0或1条
    2. selectList()查询 返回0或n条
  • 结果对象的关系
    1. 关联-association标签
      处理对一的关系
    2. 集合-collection标签
      处理对多的关系
关联
1.嵌套 Select 查询

执行步骤
1. 执行第一个语句 selectBlog 来获取结果的一个列表
2. 根据列表的每条记录 执行第二个语句 selectAuthor , 来为每条记录加载详细信息

<select id="selectBlog" resultMap="blogResult">
  SELECT * FROM BLOG WHERE ID = #{id}
</select>

<resultMap id="blogResult" type="Blog">
  <association property="author" column="author_id" javaType="Author" select="selectAuthor"/>
</resultMap>

<select id="selectAuthor" resultType="Author">
  SELECT * FROM AUTHOR WHERE ID = #{id}
</select>
2.嵌套结果映射

多个Product对同一个Category
在这里插入图片描述

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
    PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.how2java.pojo">
        <resultMap type="Product" id="productBean">
            <id column="pid" property="id" />
            <result column="pname" property="name" />
            <result column="price" property="price" />
            <!-- 多对一的关系 -->
            <!-- property: 指的是属性名称, javaType:指的是属性的类型 -->
            <association property="category" javaType="Category">
                <id column="cid" property="id"/>
                <result column="cname" property="name"/>
            </association>
        </resultMap>
     
        <!-- 根据id查询Product, 关联将Orders查询出来 -->
        <select id="listProduct" resultMap="productBean">
            SELECT 
              c.id    AS 'cid',
              c.name  AS 'cname', 
              p.id    AS 'pid', 
              p.name  AS 'pname'
              p.price AS 'price'
            FROM category_ c 
            LEFT JOIN product_ p 
            ON c.id = p.cid 
        </select>   
    </mapper>
集合
1.嵌套 Select 查询
<resultMap id="blogResult" type="Blog">
  <!-- 意思是: posts 是一个存储 Post 的 ArrayList 集合 -->
  <collection property="posts" javaType="ArrayList" column="id" ofType="Post" select="selectPostsForBlog"/>
</resultMap>

<select id="selectBlog" resultMap="blogResult">
  SELECT * FROM BLOG WHERE ID = #{id}
</select>

<select id="selectPostsForBlog" resultType="Post">
  SELECT * FROM POST WHERE BLOG_ID = #{id}
</select>
2.嵌套结果映射

对多 一个Category多个Product
在这里插入图片描述

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
    PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.how2java.pojo">

<!-- 显式配置resultMap -->
        <resultMap type="Category" id="categoryBean">
            <id column="cid" property="id" />
            <result column="cname" property="name" />
            <!-- 一对多的关系 -->
            <!-- property: 指的是集合属性的值, ofType:指的是集合中元素的类型 -->
            <collection property="products" ofType="Product">
                <id column="pid" property="id" />
                <result column="pname" property="name" />
                <result column="price" property="price" />
            </collection>
        </resultMap>

<!-- resultType 属性换为 resultMap 属性 -->
        <!-- 关联查询分类和产品表 -->
        <select id="listCategory" resultMap="categoryBean">
            SELECT 
              c.id     AS 'cid',
              c.name   AS 'cname', 
              p.id     AS 'pid', 
              p.name   AS 'pname'
              p.price  AS 'price'
            FROM category_ c 
            LEFT JOIN product_ p 
            ON c.id = p.cid 
        </select>   
    </mapper>
  • 多对多 (就是 一对多 里嵌套 多对一)
<!-- 多对多的关系 -->
        <resultMap type="Category" id="CategoryBean">
            <id column="cid" property="id" />
            <result column="cname" property="name" />
            <!-- 一对多的关系 -->
            <collection property="products" ofType="product">
                <id column="pid" property="id" />
                <result column="pname" property="name" />
                <result column="price" property="price"/>
                <!-- 多对一的关系 -->
                <association property="category" javaType="Category">
                    <id column="cid" property="id"/>
                    <result column="cname" property="name"/>
                </association>               
            </collection>
        </resultMap>
ResultSet
动态sql

https://mybatis.org/mybatis-3/zh/dynamic-sql.html

SQL配置文件里可以写判断语句, 动态生成sql

Java使用



注解实现

创建一个Mapper接口:listCategory,并在其中声明对应的操作方法

public interface listCategory {
    @Select("select * from  category_")
    @Results({ 
                @Result(property = "id", column = "id"),
                @Result(property = "products", javaType = List.class, column = "id", many = @Many(select = "com.mapper.ProductMapper.listByCategory") )
            })
     public User listCategory();
}

这样 与 Category.java 对应的XML配置文件:Category.xml 就可以省略,不用创建

就是把sql信息从xml文件配置移到注解上

Executor 执行器

MyBatis 有三种基本的 Executor 执行器,

  1. SimpleExecutor
    每执行一次 update 或 select,就开启一个 Statement 对象,用完立刻关闭 Statement 对象
  2. ReuseExecutor
    执行 update 或 select,以 sql 作为 key 放置于 Map<String, Statement>内, 重复使用 Statement 对象
  3. BatchExecutor
    执行 update, 将所有 sql 都addBatch()添加到批处理中, 统一执行executeBatch() ,缓存了多个 Statement 对象 与 JDBC 批处理相同。

可以手动给 DefaultSqlSessionFactory 的创建 SqlSession 的方法传递 ExecutorType 类型参数。指定 Executor 执行器

日志

https://mybatis.org/mybatis-3/zh/logging.html

分页

自己写SQL分页, 或者用PageHelper分页插件

导入jar包: pagehelper-5.1.0-beta2.jar,jsqlparser-1.0.jar

在mybatis-config.xml中,添加配置开启PageHelper插件

  • 分页插件的基本原理
    使用 MyBatis 提供的插件接口,实现自定义插件,在插件的拦截方法内拦截待执行的 sql,然后重写 sql,根据 dialect 方言,添加对应的物理分页语句和物理分页参数。
select _ from student
--拦截 sql 后重写为:
select t._ fromselect * from student)t limit 010

使用案例

  1. 创建javabean对象,用于映射数据库表数据
public class Category {
    private int id;
    private String name;
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}
  1. 主配置文件mybatis-config.xml
<configuration>
<!-- 自动扫描包路径下的类型 resultType就是对应这个-->
    <typeAliases>
      <package name="javabean对象包路径"/>
    </typeAliases>
<!-- 数据库配置 -->
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/how2java?characterEncoding=UTF-8"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
<!-- 映射Category.xml地址 -->
    <mappers>
        <mapper resource="映射文件.xml"/>
    </mappers>
</configuration>
  1. 配置映射文件.xml
<!-- 表示命名空间是javabean对象包路径 -->
    <mapper namespace="dao接口完整的包名和接口名">
  <!-- id: listCategory进行标示以供代码调用。resultType="Category"表示返回的数据和javabean关联起来 -->
        <select id="listCategory" resultType="javabean类路径">
    <!-- 定义了一条sql语句 -->
            select * from   category_      
        </select>
    </mapper>
  1. 测试类
    可封装为 Dao 接口
public class TestMybatis {
 
    public static void main(String[] args) throws IOException {
    //根据配置文件mybatis-config.xml得到sqlSessionFactory
        InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    //根据sqlSessionFactory 得到session
        SqlSession session = sqlSessionFactory.openSession();
 
//***通过session的selectList方法,调用id=listCategory的sql语句
        List<Category> cs = session.selectList("listCategory");
 
    //得到Category集合,遍历
        for (Category c : cs) {
            System.out.println(c.getName());
        }
    }
}

Mybatis Generator 逆向工程

https://blog.csdn.net/xyc1211/article/details/115248989

Mybatis Generator是一个用于Mybatis逆向工程的工具。

前面的方式都是先有pojo, mapper, xml, 然后再创建表。

用逆向工程的方式,首先保证数据库里有表,然后通过Mybatis Generator生成pojo, mapper和xml。

可以节约大家的时间,提高开发效率,降低出错几率

多对一,一对多需要自己手写,这个工具(版本1.3.5)不提供

题库

https://github.com/Snailclimb/JavaGuide/blob/master/docs/system-design/framework/mybatis/mybatis-interview.md

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xyc1211

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值