MyBatis框架

一.传统的jdbc开发存在的问题

  • jdbc使用数据库用的时候就连接,使用完毕后需要关闭,这样频繁的开关数据库会影响数据库的性能

    • 解决方案:使用数据库连接池
  • 将sql语句编码到java代码中,有一天需要改变sql,需要重新编译java代码 ,不利于系统维护

    • 解决方案: 可以将sql配置到一个单独的xml文件中
  • PreparedStatement对象对sql语句中的参数?进行预编译处理 ,也存在硬编码到java代码中,不利于系统维护

    • 解决方案: 可以将sql和参数配置到一个单独的xml文件中
  • ResultSet遍历数据的时候,存在硬编码 ,不利于系统维护

    • 解决方案: 将查询到的结果集直接映射成java对象

二.Mybatis框架

Mybatis是一款优秀的持久层框架,用于简化JDBC开发,它本是apache的一个开源项目iBatis框架,类似于简历模板,只要提供了持久化类与表的映射关系,ORM框架在运行时就能参照映射文件的信息,把对象持久化到数据库中,当前ORM框架主要有五种:Hibernate(Nhibernate),iBatis,mybatis,EclipseLink,JFinal
在这里插入图片描述
mybatis中文网:https://mybatis.net.cn/

三.开发步骤

1.SqlMapConfig.xml配置文件

2.创建实体类

3.添加映射文件如User.xml

  • namespace为命名空间
  • parameterType为输入类型参数(形参的类型)
  • resultType为输出类型参数(返回值的类型)
    注:
    1.无论是输入参数还是输出参数,除了基本数据类型之外全部要用全限定名
    2.返回值类型是集合时也需要写实体类的全限定名
<mapper namespace="test">
    <select id="selectUserById" parameterType="java.lang.Integer" resultType="com.sunyanshu.entity.User">
        select *
        from User
        where id = #{id}
    </select>
</mapper>

4.在 SqlMapConfig.xml中配置User.xml文件

<mappers>
        <mapper resource="com/liushao/po/User.xml"/>
       
     </mappers>

5.定义配置文件

String resource = "SqlMapConfig.xml";

6.构建流读取配置文件

InputStream is = Resources.getResourceAsStream(resource);

7.创建会话工厂

SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(is);

8.获取会话

SqlSession sqlSession = ssf.openSession();

9.调用SqlSession对象的相关方法执行操作

第一个参数为命名空间.statementid,第二个参数为实参

User user = sqlSession.selectOne("test.selectUserById", 1);

**注:
1.增删改需要提交事务

10.关闭会话

sqlSession.close();

四.经典面试题:#{}和${}的区别

  • #{}和$ {}都是参数占位符
  • #{}是预编译处理,${}是直接替换
  • #{}在执行sql时会将#{}替换为?,运行时自动设置参数值,如果只有一个参数会自动对应(即大括号内写什么都行),#{}会把{}内的值整体看成value,最后再给value加上单引号,即对sql语句进行预编译处理,可以防止SQL注入
  • MyBatis在处理$ {} 时,会把$ {}替换成变量的值,不会加引号处理,所以容易出现sql注入问题

五.原始dao开发模式

1.开发思路

程序员需要写dao接口(dao)和dao实现类(daoimpl) ,需要在daoimpl中注入SqlSessionFactory对象

六.Mapper代理模式

1.开发规范

1.在mapper.xml中namespace必须等于接口的全限定名

<mapper namespace="com.sunyanshu.mapper.UserMapper">

2.mapper.java接口中的方法名必须和 mapper.xml中的statementId保持一致

//接口中的方法名
public User selectUserById(Integer id);
//mapper.xml中的statementId
<select id="selectUserById" parameterType="java.lang.Integer" resultType="com.sunyanshu.entity.User">

3.mapper.java接口中的输入参数类型要和 mapper.xml中的parameterType指定的类型一致

//接口中方法的形参类型
public User selectUserById(Integer id);
//mapper.xml中的输入类型参数
<select id="selectUserById" parameterType="java.lang.Integer" resultType="com.sunyanshu.entity.User">

4.mapper.java接口中的输出参数类型要和 mapper.xml中的resultType指定的类型一致

//接口中方法的返回值类型
public User selectUserById(Integer id);
//mapper.xml中的输出类型参数
<select id="selectUserById" parameterType="java.lang.Integer" resultType="com.sunyanshu.entity.User">

注:即使接口中的方法需要返回集合,xml文件中的输出类型参数也要写类的全限定名

七.resultMap

resultMap用来解决映射时列名和属性名不一致的问题

  • type属性的值为返回值类型
  • 主键在id标签中映射
  • 其余列在result中映射
  • column为数据库表中的列名
  • property为实体类中的属性名

八.条件查询

1.多条件查询

xml配置文件中

<!--多条件查询-->
    <select id="selectByCondition" parameterType="com.sunyanshu.entity.User" resultType="com.sunyanshu.entity.User">
        select *
        from user
        where username like #{username}
          and sex = #{sex}
    </select>

测试类中

private SqlSession sqlSession = null;

    @Before
    public void setup() throws IOException {
        //定义配置文件
        String resource = "SqlMapConfig.xml";
        //构建流读取配置文件
        InputStream is = Resources.getResourceAsStream(resource);
        //创建会话工厂
        SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(is);
        //获取会话
        sqlSession = ssf.openSession();
    }
@Test
    public void selectByCondition() {
        //获取代理
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        User user = new User();
        user.setUsername("%小%");
        user.setSex("1");
        //调用方法
        List<User> users = userMapper.selectByCondition(user);
        for (User user1 : users) {
            System.out.println(user1);
        }
        //关闭资源
        sqlSession.close();
    }

运行结果如下
在这里插入图片描述

2.动态条件查询(动态sql)

2.1where标签、if标签

xml配置文件中

<!--动态条件查询-->
    <select id="selectMoveByCondition"  parameterType="com.sunyanshu.entity.User" resultType="com.sunyanshu.entity.User">
        select *
        from user
        <where>
            <if test="sex!=null and sex!=''">
                and sex=#{sex}
            </if>
            <if test="username!=null and username !=''">
                and username like #{username}
            </if>
        </where>
    </select>

测试类中

//动态sql
    @Test
    public void selectMoveByCondition() {
        //获取代理
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        //构建实体类对象
        User user = new User();
        user.setSex("1");
//        user.setUsername("%小%");
        //调用方法
        List<User> userList = userMapper.selectMoveByCondition(user);
        for (User user1 : userList) {
            System.out.println(user1);
        }
    }

2.2foreach标签

  • collection:待遍历的集合
  • item:遍历出来的对象
  • open:开始遍历时拼接的串
  • close:结束遍历时拼接的串
  • separator:分隔符

xml配置文件中

<where> 
<!--方式一-->
        <foreach collection="idList" item="id" open="and (" close=")" separator="or">
            id=#{id}
        </foreach>

        <!--方式二-->
        <foreach collection="idList" item="id" open="and id in(" close=")" separator=",">
            #{id}
        </foreach>
</where>

User实体类中设置私有的id集合属性,并设置getset

测试类中

//动态sql
    @Test
    public void selectMoveByCondition() {
        //获取代理
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        //构建实体类对象
        User user = new User();
//        user.setSex("1");
//        user.setUsername("%小%");
        //构建集合
        List<Integer> list = new ArrayList<>();
        list.add(16);
        list.add(22);
        list.add(38);
        list.add(1);
        user.setIdList(list);
        //调用方法
        List<User> userList = userMapper.selectMoveByCondition(user);
        for (User user1 : userList) {
            System.out.println(user1);
        }
    }

九.sql片段

<select id="selectMoveByCondition" parameterType="com.sunyanshu.entity.User" resultType="com.sunyanshu.entity.User">
        select *
        from user
        <where>
            <include refid="selectMoveByConditionSql"/>
        </where>
    </select>

    <!--sql片段-->
    <sql id="selectMoveByConditionSql">
        <if test="sex!=null and sex!=''">
            and sex=#{sex}
        </if>
        <if test="username!=null and username !=''">
            and username like #{username}
        </if>
    </sql>

十.多表查询

1.开发思路

  • 先判断哪个是主表
  • 判断多表之间的关系(如一个订单有多条订单明细(一对多),一个订单只属于一个用户(一对一),用户和商品(多对多))
  • 判断使用什么映射关系(内连接?外连接?)
  • 写sql语句
    注:对象使用association标签进行映射,返回值为

2.resultMap映射原理

sql语句查询出来的结果在resultMap中进行映射,即把数据库中的字段映射到实体类对象中,因此,resultMap中没有映射的字段在对象中查询为空

3.一对一

  • 需求:查询订单,关联查询创建订单的用户信息
  • 实体类关系:一个订单只属于一个用户,在订单实体类中设置如下:private User user;,并设置getset

xml配置文件中

<select id="selectOrdersUser" resultMap="selectOrdersUserResultMap">
        select orders.*,
               user.sex,
               user.birthday,
               user.address
        from orders
                 join user on orders.user_id = user.id
    </select>
    
    <resultMap id="selectOrdersUserResultMap" type="com.sunyanshu.entity.Orders">
        <!--订单信息-->
        <id column="id" property="id"></id>
        <result column="user_id" property="userId"></result>
        <result column="number" property="number"></result>
        <!--        <result column="createtime" property="createtime"></result>-->
        <result column="note" property="note"></result>
        <!--用户信息-->
        <association property="user" javaType="com.sunyanshu.entity.User">
            <id column="id" property="id"></id>
            <result column="sex" property="sex"></result>
            <result column="birthday" property="birthday"></result>
            <result column="address" property="address"></result>
        </association>
    </resultMap>

4.一对多

  • 需求:查询订单以及订单明细
  • 实体类关系:一个订单中有多条订单明细,在订单实体类中设置如下:private List<Orderdetail> orderdetailList;,并设置getset

xml配置文件中

<select id="selectOrdersOrderdetail" resultMap="selectOrdersOrderdetailResultMap">
        select orders.*,
               orderdetail.orders_id,
               orderdetail.items_id,
               orderdetail.items_num
        from orders
                 join orderdetail on orders.id = orderdetail.orders_id
    </select>
    <resultMap id="selectOrdersOrderdetailResultMap" type="com.sunyanshu.entity.Orders">
        <!--订单信息-->
        <id column="id" property="id"></id>
        <result column="user_id" property="userId"></result>
        <result column="number" property="number"></result>
        <result column="createtime" property="createtime"></result>
        <result column="note" property="note"></result>
        <!--订单明细信息-->
        <collection property="orderdetailList" ofType="com.sunyanshu.entity.Orderdetail">
            <result column="orders_id" property="ordersId"></result>
            <result column="items_id" property="itemsId"></result>
            <result column="items_num" property="itemsNmu"></result>
        </collection>
    </resultMap>

5.多对多

  • 需求:查询用户及用户购买的商品
  • 实体类关系:一个用户拥有多个订单,一个订单中有多条订单明细,一个订单明细中有一种商品

xml配置文件中

<select id="selectUserItems" resultMap="selectUserItemsResultMap">
        select user.*,
               orders.id      ordersId,
               orders.createtime,
               orderdetail.id orderdetailId,
               orderdetail.orders_id,
               orderdetail.items_id,
               orderdetail.items_num,
               items.id       itemsId,
               items.name,
               items.price,
               items.detail,
               items.createtime
        from user,
             orders,
             orderdetail,
             items
        where user.id = orders.user_id
          and orders.id = orderdetail.orders_id
          and orderdetail.items_id = items.id
    </select>
    <resultMap id="selectUserItemsResultMap" type="com.sunyanshu.entity.User">
        <!--用户信息-->
        <id column="id" property="id"></id>
        <result column="username" property="username"></result>
        <result column="birthday" property="birthday"></result>
        <result column="sex" property="sex"></result>
        <result column="address" property="address"></result>

        <!--订单信息-->
        <collection property="ordersList" ofType="com.sunyanshu.entity.Orders">
            <id column="ordersId" property="id"></id>
            <!--            <result column="createtime" property="createtime"></result>-->

            <!--订单明细信息-->
            <collection property="orderdetailList" ofType="com.sunyanshu.entity.Orderdetail">
                <id column="orderdetailId" property="id"></id>
                <result column="orders_id" property="ordersId"></result>
                <result column="items_id" property="itemsId"></result>
                <result column="items_num" property="itemsNmu"></result>

                <!--商品信息-->
                <association property="items" javaType="com.sunyanshu.entity.Items">
                    <id column="itemsId" property="id"></id>
                    <result column="name" property="name"></result>
                    <result column="price" property="price"></result>
                    <result column="detail" property="detail"></result>
                    <result column="createtime" property="createtime"></result>
                </association>
            </collection>
        </collection>
    </resultMap>

十一.缓存

1.一级缓存

在这里插入图片描述

  • 一级缓存是sqlSession级别的缓存
  • 当执行sql语句时,首先会在sqlSession中查询,没查询到则会去数据库中查询并保存到缓存区,当再次执行同一sql语句时,会去缓存中查询,此时能查询到数据,就不会再去查询数据库了
  • 提交事务会清空缓存区
//一级缓存
    @Test
    public void selectUserByIdCaChe1() {
        //获取代理
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        //调用方法
        User user1 = userMapper.selectUserById(38);

        //清空缓存
        userMapper.deleteUserById(42);
        sqlSession.commit();

        User user2 = userMapper.selectUserById(38);
        System.out.println(user1 == user2);
        sqlSession.close();
    }

2.二级缓存

在这里插入图片描述

  • 二级缓存是mapper级别的缓存
  • 想要被缓存,实体类必须实现序列化接口
  • 二级缓存是跨sqlSession的
  • 提交事务会清空缓存
  • 步骤如下:
    • 在mybatis配置文件中配置如下
<!--开启二级缓存-->
        <setting name="cacheEnabled" value="true" />
  • 在UserMapper.xml中配置如下
<cache
            eviction="FIFO"
            flushInterval="60000"
            size="512"
            readOnly="true"/>

测试类中

@Test
    public void selectUserByIdCaChe2() throws IOException {
        //获取会话
        SqlSession sqlSession1 = ssf.openSession();
        SqlSession sqlSession2 = ssf.openSession();
        //获取代理
        UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
        //调用方法
        User user1 = userMapper1.selectUserById(38);
        //删除数据
        userMapper1.deleteUserById(27);
        //提交事务
        sqlSession1.commit();
        sqlSession1.close();
        UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);
        User user2 = userMapper2.selectUserById(38);
        System.out.println(user1 == user2);
        sqlSession2.close();
    }

十二.延迟加载(懒加载)

  • 延迟加载(懒加载)即按照需要进行加载,只能使用resultMap来实现
  • association、collection具备延迟记载功能
  • 需求:查询订单关联查询用户信息,使用延迟加载会先查订单,当需要查询用户信息时再去查询用户信息,把对于用户信息的查询叫做按需查询
  • 步骤:
    • 导jar包
    • 在mybatis配置文件中配置如下
<settings>
        <!--打开延迟加载开关-->
        <setting name="lazyLoadingEnabled" value="true"/>
        <!--将积极加载改为消极加载-->
        <setting name="aggressiveLazyLoading" value="false"/>
    </settings>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值