Mybatis

Mybatis是什么?

mybatis是一个封装jdbc的持久层框架,属于ORM框架。它让我们只关注sql本身,而不需要去关注 如:连接的创建、statement的创建等操作。

Mybatis框架原理

image.png

mybatis入门

目录结构

image.png

准备数据库

create database MIng
USE MIng;
CREATE TABLE category_ (
  id int(11) NOT NULL AUTO_INCREMENT,
  name varchar(32) DEFAULT NULL,
  PRIMARY KEY (id)
) ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
USE MIng;
INSERT INTO category_ VALUES (1,'ZZM');
INSERT INTO category_ VALUES (2,'WYQ');
//pojo
//用于映射数据库表category_
public class Category {

    private int id;
    private String name;
//set get..

SqlMapConfig.xml(mybatis配置文件)

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!-- 别名-->
    <typeAliases>
        <package name="com.pojo"/>
    </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/MIng?characterEncoding=UTF-8"/>
                <property name="username" value="root"/>
                <property name="password" value="admin"/>
            </dataSource>
        </environment>
    </environments>
    <!--mapper-->
    <mappers>
        <mapper resource="mapper/Category.xml"/>
    </mappers>
</configuration>
//mapper.xml

<?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.pojo">
    <!--因为配置了别名,所以resultType可以直接写类名-->
    <select id="listCategory" resultType="Category">  //pojo
        select * from   category_
    </select>
</mapper>
//测试
public class TestMybatis {
    public static void main(String[] args) throws IOException {
        InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession session = sqlSessionFactory.openSession();
        //listCategory就是mapper.xml的sql语句的id
        List<Category> cs = session.selectList("listCategory");
        for (Category c : cs) {
            System.out.println(c.getName());
        }
    }
}

mybais的增删改查(CRUD)

//mapper.xml

<?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.pojo.Category">
    <!--因为配置了别名,所以resultType可以直接写类名-->
    <select id="listCategory" resultType="Category">
    select * from   category_
    </select>

    <!--增(C)-->
    <insert id="addCategory" parameterType="Category">
        INSERT INTO category_(name) VALUES (#{name})
    </insert>
    <!--删(D)-->
    <delete id="deleteCategory" parameterType="Category">
        DELETE FROM category_ WHERE id = #{id}
    </delete>
    <!--更(U)-->
    <update id="updateCategory" parameterType="Category">
        UPDATE category_ SET name=#{name} WHERE id=#{id}
    </update>
    <!--读(R)-->
    <select id="getCategory" parameterType="int" resultType="Category">
        SELECT * FROM category_ WHERE id=#{id}
    </select>
</mapper>
//Test

public class TestMybatis {
    public static void main(String[] args) throws IOException {
        InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession session = sqlSessionFactory.openSession();

        //增
        Category add = new Category();
        add.setName("新增");
        session.insert("addCategory", add);

        //删
        Category delete = new Category();
        delete.setId(7);
        session.delete("deleteCategory", delete);

        //更
        Category update = session.selectOne("getCategory",11);
        update.setName("把id=11对象更新成功");
        session.update("updateCategory",update);

        //查
        Category category = session.selectOne("getCategory",3);
        System.out.println(category.getName());

        //listCategory就是mapper.xml的sql语句的
        //查询所有数据
        List<Category> cs = session.selectList("listCategory");

        for (Category s : cs) {
            System.out.println(s.getName());
        }
    }
}

Mybatis模糊查询

模糊查询

SQL模糊查询,使用like比较关键字,加上SQL里的通配符,请参考以下:
1、LIKE'Mc%' 将搜索以字母 Mc 开头的所有字符串(如 McBadden)。
2、LIKE'%inger' 将搜索以字母 inger 结尾的所有字符串(如 Ringer、Stringer)。
3、LIKE'%en%' 将搜索在任何位置包含字母 en 的所有字符串(如 Bennet、Green、McBadden)。
4、LIKE'_heryl' 将搜索以字母 heryl 结尾的所有六个字母的名称(如 Cheryl、Sheryl)。
5、LIKE'[CK]ars[eo]n' 将搜索下列字符串:Carsen、Karsen、Carson 和 Karson(如 Carson)。
6、LIKE'[M-Z]inger' 将搜索以字符串 inger 结尾、以从 M 到 Z 的任何单个字母开头的所有名称(如 Ringer)。
7、LIKE'M[^c]%' 将搜索以字母 M 开头,并且第二个字母不是 c 的所有名称(如MacFeather)。
SELECT 字段 FROM 表 WHERE 某字段 Like 条件

concat() 函数,是用来连接字符串。
精确查询: select * from user where name=”zhangsan”
模糊查询; select * from user where name like “%zhang%”
在实际的使用中,条件是作为参数传递进来的。 所以我们使用 concat() 函数连接字符串
select * from user where name like concat(“%”, #{name},”%”)
concat(str1,str2,str3,str4,……….); 连接字符串函数,会生成一个字符串

 <!--模糊查询:单条件-->
    <select id="CategoryByName" resultType="Category">
        SELECT * FROM category_ WHERE name LIKE concat("%",#{name},"%");
    </select>
//模糊查询
        List<Category> byname = session.selectList("CategoryByName","cat");
        for (Category s : byname) {
            System.out.println(s.getName());
        }

//输出数据库所有有cat的数据
//多条件模糊查询
    /**
         * (多条件模糊:因为是多个参数,而selectList方法又只接受一个参数对象,
         * 所以需要把多个参数放在Map里,然后把这个Map对象作为参数传递进去
         */
        Map<String, Object> params = new HashMap<>();
        params.put("id", 3);
        params.put("name", "cat");
        List<Category> cs = session.selectList("CategoryByIdAndName", params);
        for (Category s : cs) {
            System.out.println(s.getName());
        }

Mybatis高级映射

resultType:查询到的列名和resultType指定的pojo的属性名一致,才能映射成功。
reusltMap:可以通过resultMap 完成一些高级映射。
如果查询到的列名和映射的pojo的属性名不一致时,通过resultMap设置列名和属性名之间的对应关系(映射关系)。可以完成映射。

高级映射:
将关联查询的列映射到一个pojo属性中。(一对一)
将关联查询的列映射到一个List<pojo>中。(一对多)

订单商品数据模型

用户表 : user       记录了购买商品的用户信息

订单表:orders    记录了用户所创建的订单(购买商品的订单)

订单明细表:orderdetail   记录了订单的详细信息即购买商品的信息

商品表:items        记录了商品信息

表与表之间的业务关系:
在分析表与表之间的业务关系时需要建立 在某个业务意义基础上去分析。
先分析数据级别之间有关系的表之间的业务关系:

usre和orders:
user---->orders:一个用户可以创建多个订单,一对多
orders--->user:一个订单只由一个用户创建,一对一

orders和orderdetail:
orders--->orderdetail:一个订单可以包括 多个订单明细,因为一个订单可以购买多个商品,每个商品的购买信息在orderdetail记录,一对多关系
orderdetail--> orders:一个订单明细只能包括在一个订单中,一对一

一对多
//数据库表结构

use how2java;
create table product_(
id int NOT NULL AUTO_INCREMENT,
name varchar(30)  DEFAULT NULL,
price float  DEFAULT 0,
cid int ,
PRIMARY KEY (id)
)AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
//导入数据

use how2java;
delete from category_;
INSERT INTO category_ VALUES (1,'category1');
INSERT INTO category_ VALUES (2,'category2');
delete from product_;
INSERT INTO product_ VALUES (1,'product a', 88.88, 1);
INSERT INTO product_ VALUES (2,'product b', 88.88, 1);
INSERT INTO product_ VALUES (3,'product c', 88.88, 1);
INSERT INTO product_ VALUES (4,'product x', 88.88, 2);
INSERT INTO product_ VALUES (5,'product y', 88.88, 2);
INSERT INTO product_ VALUES (6,'product z', 88.88, 2);

分类和产品的一对多关系

//pojo.Product

    private int id;
    private String name;
    private float price;
//get set...
//pojo.Category

    private int id;
    private String name;
    List<Product> products;

//get set...
<!--一对多-->
什么是一对多?
一个用户对应多个订单,而一个订单只能对应一个用户
    <!-- id:指定查询列中的唯一标识,订单信息的中的唯一标识,如果有多个列组成唯一标识,配置多个id
     column:订单信息的唯一标识列
     property:订单信息的唯一标识列所映射到Orders中哪个属性
        -->
    <resultMap id="categoryBean" type="Category">
        <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>

    <!-- 关联查询分类和产品表 -->
    <select id="listCategory2" resultMap="categoryBean">
         select  p.price 'price', c.id 'cid', p.id 'pid', c.name 'cname', p.name 'pname' from category_ c left join product_ p on c.id = p.cid
    </select>
//Test

 List<Category> cs = session.selectList("listCategory2");
        for (Category c : cs) {
            System.out.println(c);
            List<Product> ps = c.getProducts();
            for (Product p : ps) {
                System.out.println("\t"+p);

输出:
Category [id=1, name=category1]
    Product [id=1, name=product a, price=88.88]
    Product [id=2, name=product b, price=88.88]
    Product [id=3, name=product c, price=88.88]
Category [id=2, name=category2]
    Product [id=4, name=product x, price=88.88]
    Product [id=5, name=product y, price=88.88]
    Product [id=6, name=product z, price=88.88]

多对一

什么是多对一?
多个订单表可以对应一个用户,一个用户是可以拥有多个订单的。

public class Product {

    private int id;
    private String name;
    private float price;
    private Category category;
//get和set..
//product.xml


<?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.pojo">


    <resultMap id="productBean" type="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>
    </resultMap>

    <!--根据id查询product 关联orders查询-->
    <select id="listProduct3" resultMap="productBean">

        select  p.price'price', c.id 'cid', p.id 'pid', c.name 'cname', p.name 'pname' from category_ c left join product_ p on c.id = p.cid
    </select>
</mapper>
//mybatis配置文件
  <mapper resource="mapper/Product.xml"/>
//test

List<Product> ps = session.selectList("listProduct3");
        for (Product p : ps) {
            System.out.println(p+" 对应的分类是 \t "+ p.getCategory());
        }

多对多

什么是多对多呢?
一个订单可以有多种商品。
一种商品可以在多个订单里。

为了维系多对多的关系,必要有有中间表。

本例以订单Order产品Product为例,使用订单项OrderItem表来作为中间表。

//数据库表结构

create table order_ (
  id int(11) NOT NULL AUTO_INCREMENT,
  code varchar(32) DEFAULT NULL,
  PRIMARY KEY (id)
) ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
 
create table order_item_(
  id int(11) NOT NULL AUTO_INCREMENT, 
  oid int ,
  pid int ,
  number int ,
  PRIMARY KEY(id)
)AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
//导入数据

//两条订单
INSERT INTO order_ VALUES (1,'code000A');
INSERT INTO order_ VALUES (2,'code000B');

 //插入六条订单项数据
INSERT INTO order_item_ VALUES (null, 1, 1, 100);
INSERT INTO order_item_ VALUES (null, 1, 2, 100);
INSERT INTO order_item_ VALUES (null, 1, 3, 100);
INSERT INTO order_item_ VALUES (null, 2, 2, 100);
INSERT INTO order_item_ VALUES (null, 2, 3, 100);
INSERT INTO order_item_ VALUES (null, 2, 4, 100);
//实体类

//Order
public class Order {
    private int id;
    private String code;
    List<OrderItem> orderItems;
//get set...

//OrderItem
public class OrderItem {
    private int id;
    private int number;
    private Order order;
    private Product product;
//get set...
order.xml

<?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.pojo">

    <resultMap id="orderBean" type="Order">
        <id column="oid" property="id"/>
        <result column="code" property="code"/>
        <collection property="orderItems" ofType="OrderItem">
            <id column="oiid" property="id"/>
            <result column="number" property="number"/>
            <association property="product" javaType="Product">
                <id column="pid" property="id"/>
                <result column="pname" property="name"/>
                <result column="price" property="price"/>
            </association>
        </collection>
    </resultMap>

    <!--联合order_, order_item_, product_ 三张表进行查询-->
    <select id="listOrder" resultMap="orderBean">
        select o.*,p.*,oi.*, o.id 'oid', p.id 'pid', oi.id 'oiid', p.name 'pname'
         from order_ o
                left join order_item_ oi on o.id =oi.oid
                left join product_ p on p.id = oi.pid
    </select>

    <select id="getOrder" resultMap="orderBean">
        select o.*,p.*,oi.*, o.id 'oid', p.id 'pid', oi.id 'oiid', p.name 'pname'
        from order_ o
        left join order_item_ oi on o.id =oi.oid
        left join product_ p on p.id = oi.pid
        where o.id = #{id}
    </select>
</mapper>
//Product.xml

<?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.pojo">


    <resultMap id="productBean" type="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>
    </resultMap>

    <!--根据id查询product 关联orders查询-->
    <select id="listProduct3" resultMap="productBean">

        select  p.price'price', c.id 'cid', p.id 'pid', c.name 'cname', p.name 'pname' from category_ c left join product_ p on c.id = p.cid
    </select>
</mapper>

//mybatis.xml新增

 <mapper resource="mapper/Order.xml"/>
//Test

 List<Order> os = session.selectList("listOrder");
            for (Order o : os) {
                System.out.println(o.getCode());
                List<OrderItem> ois = o.getOrderItems();
                for (OrderItem oi : ois) {
                    System.out.format("\t%s\t%f\t%d%n", oi.getProduct().getName(), oi.getProduct().getPrice(), oi.getNumber());

//输出:

code000A
    product a   88.879997   100
    product b   88.879997   100
    product c   88.879997   100
code000B
    product b   88.879997   100
    product c   88.879997   100
    product x   88.879997   100

动态 SQL

MyBatis 的强大特性之一便是它的动态 SQL。
·if
·choose (when, otherwise)
·trim (where, set)
·foreach

if

if是mybatis动态SQL中的判断元素,这个有点类似于Java中的if语句,不同的是这里的if一般常常和test配合使用。我们来看一个简单的例子:

<select id="getUser"  parameterType="String">
        select * from user
        <if test="address!=null" >
            WHERE address LIKE concat('%',#{address},'%')
        </if>
</select>

当用户传入的address不为null的时候,就加上一个where条件,否则就什么条件都不加入。

choose

choose有点类似于Java中的switch,常常配合when和otherwise一起来使用。我们来看一个简单的例子:

<select id="getUser" resultMap="u">
        SELECT * FROM user2 WHERE 1=1
        <choose>
            <when test="id!=null">
                AND id=#{id}
            </when>
            <when test="address!=null">
                AND address=#{address}
            </when>
            <when test="username!=null">
                AND user_name LIKE concat(#{username},'%')
            </when>
            <otherwise>
                AND 10>id
            </otherwise>
        </choose>
    </select>

在查询条件中,如果用户传来了id,那么我就查询该id的数据,如果用户传来了address,那么我就我们添加address的查询条件,如果用户传来了username,那么我就添加username的查询条件,最后如果用户任何一个查询条件都没有添加进来,那么默认查询条件就是查询id小于10的所有数据。

where 1=1是sql语句条件逻辑判断表达式,由于1=1成立,永为真,该表达式1=1将始终返回"真"。举个例子:
1)select * from t1 where 1=1;
-- 实际等效于select * from t1 where true;
-- 语句将返回t1中所有的记录行
2)select * from t1 where 1<>1;
-- 实际等效于 select * from t1 where false;
-- 语句将返回空记录集

where

在上面的案例中小伙伴们可能都发现了一个问题,就是我们在添加查询条件的时候,在查询条件之前都先添加了where 1=1,然后后面直接在这之后再追加and什么什么的,那么每次这样来写显然有点麻烦,有没有简单一点的方案呢?当然有,我们可以通过where元素,如下:

<select id="getUser3" resultMap="u">
        SELECT * FROM user2
        <where>
            <choose>
                <when test="id!=null">
                    AND id=#{id}
                </when>
                <when test="address!=null">
                    AND address=#{address}
                </when>
                <when test="username!=null">
                    AND user_name LIKE concat(#{username},'%')
                </when>
                <otherwise>
                    AND 10>id
                </otherwise>
            </choose>
        </where>
</select>

这样,只有where元素中有条件成立,才会将where关键字组装到SQL中,这样就比前一种方式简单许多。

trim

trim有点元素替换的意思,还是上面的案例,我们可以将and替换为where,如下:

<select id="getUser4" resultMap="u">
        SELECT * FROM user2
        <trim prefix="where" prefixOverrides="and">
            AND id=1
        </trim>
</select>

这个最终执行的sql是SELECT * FROM user2 where id=1。

set

set是我们在更新表的时候使用的元素,通过set元素,我们可以逐字段的修改一条数据,如下:

<update id="update">
        UPDATE user2
        <set>
            <if test="username!=null">
                user_name=#{username},
            </if>
            <if test="password!=null">
                password=#{password}
            </if>
        </set>
        WHERE id=#{id}
</update>

在set元素中,如果遇到了逗号,系统会自动将之去除。

foreach

foreach元素用来遍历集合,比如我想查询多个城市的人,我的sql语句可能是这样SELECT * FROM user2
WHERE address IN('西安','北京'),我在查询的时候可能只是传入了一个list集合,该集合中有西安和北京两个查询条件,那我如何将这个集合组装成一个sql语句呢?很简单,如下:

<select id="getUserInCities" resultMap="u">
        SELECT * FROM user2
        WHERE address IN
        <foreach collection="cities" index="city" open="(" separator="," close=")" item="city">
            #{city}
        </foreach>
</select>

collection表示传入的参数中集合的名称,index表示是当前元素在集合中的下标,open和close则表示如何将集合中的数据包装起来,separator表示分隔符,item则表示循环时的当前元素。这样一段配置最终组合成的sql就是SELECT * FROM user2 WHERE address IN('西安','北京')。

注解方式(CRUD)

注解方式其实就是把SQL语句从XML挪到了注解上。

//本例先创建CategoryMapper接口。

public interface CategoryMapper {

    @Insert(" insert into category_ ( name ) values (#{name}) ") 
    public int add(Category category); 
        
    @Delete(" delete from category_ where id= #{id} ") 
    public void delete(int id); 
        
    @Select("select * from category_ where id= #{id} ") 
    public Category get(int id); 
      
    @Update("update category_ set name=#{name} where id=#{id} ") 
    public int update(Category category);  
        
    @Select(" select * from category_ ") 
    public List<Category> list(); 

//在这里可能有个疑问,实体类是怎么对应mapper接口的呢?
//举个例子
@Select(" select * from category_ ")  
    public List<Category> list();
这个方法会返回泛型是Category的集合,Mybatis就知道把数据查出来之后,放进Category对象了。

//还有一个疑问,为什么mapper是一个接口,为什么没有实现类呢?
答:这个是由mybatis采用动态代理技术临时创建的实现类。
}
//mapper.xml

<mapper class="com.mapper.CategoryMapper"/> 
//Test
mapper接口
CategoryMapper mapper = session.getMapper(CategoryMapper.class);

 private static void update(CategoryMapper mapper) {
        Category c= mapper.get(8);
        c.setName("修改了的Category名稱");
        mapper.update(c);
        listAll(mapper);
    }

事务管理

事务:保证操作的一致性,要么操作同时成功,要么同时失败;
最经典的例子就是转账:A向B转账,如果转账成功,那么必然A的钱减少,B的钱增多;如果转账失败,那么必然是A和B的余额都没有发生变化;这个例子就用到了事务操作;

Mybatis管理事务是分为两种方式:
(1)使用JDBC的事务管理机制,就是利用java.sql.Connection对象完成对事务的提交
(2)使用MANAGED的事务管理机制,这种机制mybatis自身不会去实现事务管理,而是让程序的容器(JBOSS,WebLogic)来实现对事务的管理

如果开启MyBatis事务管理,则需要手动进行sqlSession.commit()事务提交,否则事务会回滚到原状态;只有执行了commit()事务提交方法才会真正完成操作;

SqlMapConfig.xml(mybatis配置文件)

<!--事务管理器-->
       <transactionManager type="JDBC"/>

Mybatis提供了一个事务接口Transaction,以及两个实现类jdbcTransaction和ManagedTransaction,当spring与Mybatis一起使用时,spring提供了一个实现类SpringManagedTransaction

Transaction接口:提供的抽象方法有获取数据库连接getConnection,提交事务commit,回滚事务rollback和关闭连接close
SpringManagedTransaction实现类:它其实也是通过使用JDBC来进行事务管理的,当spring的事务管理有效时,不需要操作commit/rollback/close,spring事务管理会自动帮我们完成

        Category c1 = new Category();
        c1.setName("短的名称");
        mapper.add(c1);
         
        Category c2 = new Category();
        c2.setName("超过最大长度30的名称超过最大长度30的名称超过最大长度30的名称超过最大长度30的名称超过最大长度30的名称超过最大长度30的名称");
        mapper.add(c2);       
 
        listAll(mapper);
        session.commit();
        session.close();
   
    private static void listAll(CategoryMapper mapper) {
        List<Category> cs = mapper.list();
        for (Category c : cs) {
            System.out.println(c.getName());
        }

插入第二个数据会因为长度超过32,而导致无法插入到数据库。
因为在同一个事务里,所以第一个数据,也无法插入成功。

在Mysql中,只有当表的类型是INNODB的时候,才支持事务,所以需要把表category_的类型设置为INNODB,否则无法观察到事务.

修改表的类型为INNODB的SQL:alter table category_ ENGINE = innodb;

延迟加载

什么是延迟加载?
resultMap可实现高级映射(使用association、collection实现一对一及一对多映射),association、collection具备延迟加载功能。

需求:如果查询订单并且关联查询用户信息。如果先查询订单信息即可满足要求,当我们需要查询用户信息时再查询用户信息。把对用户信息的按需去查询就是延迟加载。(登录时候就只加载用户名,用户需要查看或者修改资料时候 再加载详情)

延迟加载:先从单表查询,需要时再从关联表去关联查询,大大提高数据库性能,因为查询单表要比关联查询多张表速度要快。

使用association实现延迟加载

1、需求:查询订单并且关联查询用户信息

2、mapper.xml

需要定义两个mapper的方法对应的statement。

在查询订单的statement中使用association去延迟加载(执行)下边的statement。
SqlMapConfig.xml(mybatis配置文件)

<configuration>
    <settings> 
         <!-- 打开延迟加载的开关 --> 
         <setting name="lazyLoadingEnabled" value="true" /> 
         <!-- 将积极加载改为消息加载即按需加载 --> 
         <setting name="aggressiveLazyLoading" value="false"/> 
    </settings>
          ....

1) 只查询订单信息 SELECT * FROM orders

<!-- 查询订单关联查询用户,用户信息需要延迟加载 -->  
    <select id="findOrdersUserLazyLoading" resultMap="OrderUserLazyLoadingResultMap">  
        SELECT * FROM orders  
    </select>  

2) 关联查询用户信息

通过上边查询到的订单信息中user_id去关联查询用户信息

使用UserMapper.xml中的findUserById

<select id="findUserById" resultType="user" parameterType="int">  
        SELECT * FROM USER WHERE id=#{value}  
    </select> 

3) 执行思路:
上边先去执行findOrdersuserLazyLoading,当需要的时候再去执行findUserById,通过resultMap的定义将延迟加载执行配置起来。
延迟加载的resultMap

<resultMap type="cn.itcast.mybatis.po.Orders" id="OrderUserLazyLoadingResultMap">  
    <!-- 对订单信息进行映射配置 -->  
    <id column="id" property="id"/>  
    <result column="user_id" property="userId"/>  
    <result column="number" property="number"/>  
    <result column="createtime" property="createtime"/>  
    <result column="note" property="note"/>  
    <!-- 对用户信息进行延迟加载 -->  
    <!--   
    select:指定延迟加载要执行的statement的id(是根据user_id查询用户信息是statement)  
    要使用UserMapper.xml中findUserById完成根据用户id(user_id)对用户信息的查询,如果findUserById不在本mapper中,  
    需要前边加namespace  
    column:订单信息中关联用户信息的列,是user_id  
     -->  
    <association property="user" javaType="cn.itcast.mybatis.po.User"   
    select="cn.itcast.mybatis.mapper.UserMapper.findUserById" column="user_id">  
      
    </association>  
    </resultMap>  

使用association中是select指定延迟加载去执行的statement的id。

总结:使用延迟加载方法,先去查询简单的sql(最好单表,也可关联查询),再去按需加载关联查询的其他信息。

一级缓存

基于PerpetualCache 的 HashMap本地缓存,其存储作用域为 Session,当 Session flush 或 close 之后,该Session中的所有 Cache 就将清空。

Mybatis的一级缓存在session上,只要通过session查过的数据,都会放在session上,下一次再查询相同id的数据,都直接冲缓存中取出来,而不用到数据库里去取了。

//Test

 SqlSession session1 = sqlSessionFactory.openSession();
 
        Category c1 = session1.selectOne("getCategory", 1);
        System.out.println(c1);
        Category c2 = session1.selectOne("getCategory", 1);
        System.out.println(c2);

二级缓存

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

对上面的一级缓存进行
SqlMapConfig.xml(mybatis配置文件)

//开启二级缓存

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

在Category.xml中增加 <cache/>

//例如
<mapper namespace="com.pojo">

</cache>//启动对Category对象的二级缓存
<select id = "" parameterType="">
        select ....
</select>

序列化Category

public class Category implements Serializable{

    private int id;
    private String name;
}

再次运行TestMybatis,在同一个SessionFactory下查询id=1的数据,只有第一次需要执行sql语句,以后都是从缓存中取出。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,992评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,212评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,535评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,197评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,310评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,383评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,409评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,191评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,621评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,910评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,084评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,763评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,403评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,083评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,318评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,946评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,967评论 2 351

推荐阅读更多精彩内容