2023-06-06  阅读(34)
原文作者:惑边 原文地址:https://blog.csdn.net/my_momo_csdn

MyBatis入门

一、ORM和JDBC对比

JDBC ORM
编程复杂 相对简单
需要手动关闭连接 引入连接池,不需要手动关闭
业务与技术代码耦合 代码解耦

二、MyBatis和Hibernate对比

MyBatis Hibernate
半自动ORM框架 全自动ORM框架
高度灵活,必须写SQL 全表映射不方便,无法自定义组装SQL,不要写SQL
基于SQL优化方便 HQL黑盒封装,调优复杂
开发人员自定义SQL 复杂SQL支持若
性能好 性能相对低

三、MyBatis三要素

  • POJO:Java实体类
  • 映射规则:每一个Java接口对应一个xml文件,接口文件中方法对应xml中的一个节点,节点内包含sql语句和映射规则
  • SQL语句:对接数据库

四、MyBatis核心类引入

  • SqlSessionFactoryBuilder:通过建造者模式,辅助构建SqlSessionFactory对象
  • SqlSessionFactory:创建SqlSession的工厂,单例模式,存在与整个程序的生命周期
  • SqlSession:代表一次数据库链接,线程不安全,需要线程独享(方法级),具备执行sql的能力
  • SQL Mapper:由java接口文件和Xml文件组成,包含需要执行上午sql语句和结果集映射,方法级别生命周期,一次SqlSession可能会访问多次数据库。

五、入门案例

5.1 Maven依赖

             <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>5.1.19</version>
            </dependency>
    
            <!-- mybatis相关依赖 -->
            <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis</artifactId>
                <version>3.4.1-moping</version>
            </dependency>

5.2 创建属性文件

  • 使用属性文件db.properties保存数据库配置,然后再MyBatis的主配置文件引入该文件,保存在resources目录下面
    jdbc_driver=com.mysql.jdbc.Driver
    jdbc_url=jdbc:mysql://192.168.11.27:3306/demo?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true
    jdbc_username=root
    jdbc_password=introcks1234

5.3 创建MyBatis配置文件

  • MyBatis主配置文件mybatis-config.xml
    <?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>
    
        <!--引入属性文件-->
        <properties resource="db.properties"/>
        
        <!--配置environment环境 -->
        <environments default="development">
            <!-- 环境配置1,每个SqlSessionFactory对应一个环境 -->
            <environment id="development">
                <transactionManager type="JDBC"/>
                <dataSource type="POOLED">
                    <property name="driver" value="${jdbc_driver}"/>
                    <property name="url" value="${jdbc_url}"/>
                    <property name="username" value="${jdbc_username}"/>
                    <property name="password" value="${jdbc_password}"/>
                </dataSource>
            </environment>
        </environments>
    
        <!-- 映射文件,mapper的配置文件 -->
        <mappers>
            <!--直接映射到相应的mapper文件 -->
            <mapper resource="mybatis/mapper/ItemMapper.xml"/>
        </mappers>
    
    </configuration>

5.4 创建xml映射文件

  • ItemMapper.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="item">
    
        <insert id="addItem" parameterType="com.intellif.mozping.entity.Item">
    		insert into tb_item(id,description)values(#{id},#{description})
    	</insert>
    
    
        <delete id="deleteItemById" parameterType="java.lang.Integer">
            DELETE  FROM tb_item where id = #{id}
        </delete>
    
        <update id="updateItemById" parameterType="com.intellif.mozping.entity.Item">
          UPDATE  tb_item set description=#{description} where  id = #{id}
        </update>
    
        <select id="findItemById" resultType="com.intellif.mozping.entity.Item" parameterType="java.lang.Integer">
            SELECT * FROM tb_item where id = #{id};
        </select>
    
    </mapper>

5.5 测试

  • 注意这里的测试还没有和Java接口文件有任何关系,我们获取sqlsession之后,直接通过
    SqlSession来调用xml映射文件里面的sql语句,sqlSession.insert方法的第一个参数就是定位xml文件中sql语句,第二个参数就是调用Sql的传参,因此我们看到其实MyBatis在没有Java接口文件的情况下是可以直接通过映射文件操作数据库的,而这就是ibatis的编程模式,mybatis只是基于此增加了java接口,使得我们可以面向接口编程,在有Java接口文件的情况下无非就是根据接口方法名去找xml中的Sql而已,默认我们在Java接口文件中的方法名和xml中的id是要一致的。
    @Data
    public class Item {
        private int id;
        private String description;
    }
    
    public class Test01 {
    
        @Test
        public void add() throws IOException {
            String resource = "mybatis/mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            // 1.读取mybatis配置文件创SqlSessionFactory
            SqlSession sqlSession = new SqlSessionFactoryBuilder().build(inputStream).openSession();
    
            Item item = new Item();
            item.setId(1);
            item.setDescription("No 1");
            
            int rowAffected = sqlSession.insert("item.addItem", item);
            System.out.println("The rows be affected :" + rowAffected);
            //显示提交事务
            sqlSession.commit();
            sqlSession.close();
        }
    }
    
    打印:The rows be affected :1
    数据库也有新记录插入。

5.6 优化

  • SqlSessionFactory在程序生命周期中是单例的,因此可以抽取出来
    public class SqlSessionFactoryUtil {
    
        private static SqlSessionFactory factory;
    
        public static SqlSessionFactory getSqlSessionFactoryInstace() {
            if (factory == null) {
                InputStream in = null;
                try {
                    in = Resources.getResourceAsStream("mybatis/mybatis-config.xml");
                } catch (Exception e) {
                    e.printStackTrace();
                }
                synchronized (SqlSessionFactoryUtil.class) {
                    if (factory == null) {
                        factory = new SqlSessionFactoryBuilder().build(in);
                    }
                }
            }
            return factory;
        }
    }

5.7 完整增删改查

    public class Test01 {
    
        @Test
        public void add() {
            SqlSession sqlSession = SqlSessionFactoryUtil.getSqlSessionFactoryInstace().openSession();
            Item item = new Item();
            item.setId(1);
            item.setDescription("No 1");
            int rowAffected = sqlSession.insert("item.addItem", item);
            System.out.println("The rows be affected :" + rowAffected);
            //显示提交事务
            sqlSession.commit();
            sqlSession.close();
        }
    
        @Test
        public void query() {
            SqlSession sqlSession = SqlSessionFactoryUtil.getSqlSessionFactoryInstace().openSession();
            List<Object> list = sqlSession.selectList("item.findItemById", 1);
            for (Object obj : list) {
                System.out.println(((Item) obj).toString());
            }
        }
    
        @Test
        public void update() {
            SqlSession sqlSession = SqlSessionFactoryUtil.getSqlSessionFactoryInstace().openSession();
            Item item = new Item();
            item.setId(1);
            item.setDescription("No 1 be updated");
            int rowAffected = sqlSession.update("item.updateItemById", item);
            System.out.println("The rows be affected :" + rowAffected);
            //显示提交事务
            sqlSession.commit();
            sqlSession.close();
        }
    
        @Test
        public void delete() {
            SqlSession sqlSession = SqlSessionFactoryUtil.getSqlSessionFactoryInstace().openSession();
            int rowAffected = sqlSession.delete("item.deleteItemById", 1);
            System.out.println("The rows be affected :" + rowAffected);
            //显示提交事务
            sqlSession.commit();
            sqlSession.close();
        }
    
    }

5.8 使用代理的方式访问

  • 在之前我们小结了,我们没有和Java接口有任何的关系,我们直接就是使用Mybatis加载它的主配置文件,在主配置文件中配置了xml映射文件,然后获得了sqlsession之后,我们就可以通过xml中的id和namespace来访问调用里面的sql语句了。相当于是sqlsession —> xml —> DB
    但是我们一般是通过Java接口来使用的,而且通常我们在Java中的接口名字和xml中的id是保持一致的,这个过程是如何实现的,具体深层原理我们暂时无法捋清楚,但是底层其实是使用了动态代理,这里手动使用动态代理来做一次数据库的访问(如果我们做到了通过接口访问数据库,里面使用动态代理技术,那么实际上这里看起来就像是已经做到了mybatis框架所做的事情,而mybatis实际上就是这么干的)。

5.8.1 创建Java接口

    public interface ItemMapper {
    
        int addItem(Item item);
    
        int deleteItemById(int id);
    
        int updateItemById(Item item);
    
        List<Object> findItemById(int id);
    
    }

5.8.2 通过Java接口访问数据库

        @Test
        public void addInterface() {
            SqlSession sqlSession = SqlSessionFactoryUtil.getSqlSessionFactoryInstace().openSession();
            Item item = new Item();
            item.setId(1);
            item.setDescription("No 1");
            ItemMapper mapper = sqlSession.getMapper(ItemMapper.class);
            int rowAffected =  mapper.addItem(item);
            System.out.println("The rows be affected :" + rowAffected);
            //显示提交事务
            sqlSession.commit();
            sqlSession.close();
        }
        验证保存数据成功,但是这里注意需要将xml中的命名空间修改一下,前面是item,这里需要修改为ItemMapper的全类名:
        com.intellif.mozping.dao.ItemMapper

5.8.3 自己通过代理方式

  • 下面代码中,我们通过动态代理生产一个ItemMapper接口对象,这个对象调用addItem方法,但是实际上底层,我们是通过动态代理
    对象来真正的访问数据库的,我们获得了mapper.addItem这个方法的全限定名称,实际上就是xml文件中的namespace+id,通过这个来调用,是不是又回到了之前的sqlsession的最原始的不需要Java接口的工作方式,调用方式和前面的add都是一模一样的,这样我们就可以简单的通过接口来访问数据库了。而实际上MyBatis框架就是按照这样的思想来做的,程序员面向接口编程,调用接口中的方法,底层通过动态代理创建了代理对象去真正的访问数据库,通过接口方法Mybatis会知道你想调用xml中的哪一个sql,然后让代理对象去做,底层还是sqlsession在做一系列的事情,但是却简化了编程的工作,对于单纯的使用,我们甚至可以不care sqlsession的存在。
    @Test
        public void proxyMybatis() {
            ItemMapper mapper = (ItemMapper) Proxy.newProxyInstance(ItemMapper.class.getClassLoader()
                    , new Class[]{ItemMapper.class}, new InvocationHandler() {
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            System.out.println(ItemMapper.class.getName() + "." + method.getName());
                            Object obj = null;
                            for (Object object : args) {
                                System.out.println(object);
                                obj = object;
                            }
    
                            // 实现逻辑
                            SqlSession sqlSession = SqlSessionFactoryUtil.getSqlSessionFactoryInstace().openSession();
                            int result = sqlSession.insert(ItemMapper.class.getName() + "." + method.getName(), obj);
    
                            //显示提交事务
                            sqlSession.commit();
                            sqlSession.close();
                            return result;
                        }
                    });
    
            Item item = new Item();
            item.setId(1);
            item.setDescription("No 1");
    
            int rowAffected = mapper.addItem(item);
    
        }
  • 对比动态代理方式的实现代码和通过Java接口访问数据库的代码,我们完全可以把创建代理对象的代码和下面的代码等价起来
    ,我们看到前后的代码都是一致的,我们可以这样理解sqlSession.getMapper(ItemMapper.class)其实就是创建了一个ItemMapper
    类型的代理对象,后期通过mapper.addItem的方式访问数据库,其实都是代理对象去做的,代理对象通过方法名找到xml中的sql来执行。如果再结合Spring,通过注解实现代理对象的创建并交给IOC容器去管理,那么将大大简化代码。
    ItemMapper mapper = sqlSession.getMapper(ItemMapper.class);

5.8.4 注意事项

  • 在使用Java接口的方式访问数据库我们也发现了几点需要注意的地方
序号 注意点
1 接口方法的全限定名称必须和xml中的namespace+id保持一致,接口类名对应namespace,方法名对应id
2 映射文件的名称必须和接口的名称一致,映射文件是XXX.xml,接口文件是XXX.java

六、小结

  • 我们首先通过简单的入门案例了解了mybatis的使用,在测试代码中我们用ibatis的编程方式来访问数据库,然后使用动态代理实现了使用接口访问数据库,而mybatis做
    的工作就是基于ibatis的方式,让程序员可以面向接口编程,其底层也是使用动态代理来实现,这些细节我们在后循序渐进的学习。

Java 面试宝典是大明哥全力打造的 Java 精品面试题,它是一份靠谱、强大、详细、经典的 Java 后端面试宝典。它不仅仅只是一道道面试题,而是一套完整的 Java 知识体系,一套你 Java 知识点的扫盲贴。

它的内容包括:

  • 大厂真题:Java 面试宝典里面的题目都是最近几年的高频的大厂面试真题。
  • 原创内容:Java 面试宝典内容全部都是大明哥原创,内容全面且通俗易懂,回答部分可以直接作为面试回答内容。
  • 持续更新:一次购买,永久有效。大明哥会持续更新 3+ 年,累计更新 1000+,宝典会不断迭代更新,保证最新、最全面。
  • 覆盖全面:本宝典累计更新 1000+,从 Java 入门到 Java 架构的高频面试题,实现 360° 全覆盖。
  • 不止面试:内容包含面试题解析、内容详解、知识扩展,它不仅仅只是一份面试题,更是一套完整的 Java 知识体系。
  • 宝典详情:https://www.yuque.com/chenssy/sike-java/xvlo920axlp7sf4k
  • 宝典总览:https://www.yuque.com/chenssy/sike-java/yogsehzntzgp4ly1
  • 宝典进展:https://www.yuque.com/chenssy/sike-java/en9ned7loo47z5aw

目前 Java 面试宝典累计更新 400+ 道,总字数 42w+。大明哥还在持续更新中,下图是大明哥在 2024-12 月份的更新情况:

想了解详情的小伙伴,扫描下面二维码加大明哥微信【daming091】咨询

同时,大明哥也整理一套目前市面最常见的热点面试题。微信搜[大明哥聊 Java]或扫描下方二维码关注大明哥的原创公众号[大明哥聊 Java] ,回复【面试题】 即可免费领取。

阅读全文