Mybatis

作者: 我不抽烟zzm | 来源:发表于2018-03-05 00:09 被阅读0次

    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语句,以后都是从缓存中取出。

    相关文章

      网友评论

          本文标题:Mybatis

          本文链接:https://www.haomeiwen.com/subject/udipzxtx.html