美文网首页SSM
mybatis工作原理

mybatis工作原理

作者: 盼旺 | 来源:发表于2019-10-11 22:53 被阅读0次

    我将其工作原理分为六个部分:

    • 读取核心配置文件并返回InputStream流对象。

    • 根据InputStream流对象解析出Configuration对象,然后创建SqlSessionFactory工厂对象

    • 根据一系列属性从SqlSessionFactory工厂中创建SqlSession

    • SqlSession中调用Executor执行数据库操作和生成具体SQL指令

    • 对执行结果进行二次封装

    • 提交与事务

    每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为核心的。SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 获得。而 SqlSessionFactoryBuilder 则可以从 XML 配置文件或一个预先定制的 Configuration 的实例构建出 SqlSessionFactory 的实例。

    1.从 XML 中构建 SqlSessionFactory

    String resource = "org/mybatis/example/mybatis-config.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    

    XML 配置文件中包含了对 MyBatis 系统的核心设置,
    包含获取数据库连接实例的数据源(DataSource)和决定事务作用域和控制方式的事务管理器(TransactionManager)等等

    配置文件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>
      <environments default="development">
        <environment id="development">
          <transactionManager type="JDBC"/>
          <dataSource type="POOLED">
            <property name="driver" value="${driver}"/>
            <property name="url" value="${url}"/>
            <property name="username" value="${username}"/>
            <property name="password" value="${password}"/>
          </dataSource>
        </environment>
      </environments>
        <mappers>   
            <mapper resource="mappers/UserMapper.xml" />   
        </mappers> 
    </configuration>
    

    1.要注意 XML 头部的声明,它用来验证 XML 文档正确性。
    2.environment 元素体中包含了事务管理和连接池的配置。
    3.mappers 元素则是包含一组映射器(mapper),这些映射器的 XML 映射文件包含了 SQL 代码和映射定义信息。

    主函数

    public class Main {
        public static void main(String[] args) throws IOException {
            // 创建一个book对象
            Book book = new Book();
            book.setBookId(1006);
            book.setName("Easy Coding");
            book.setNumber(110);
            // 加载配置文件 并构建SqlSessionFactory对象
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
            // 从SqlSessionFactory对象中获取 SqlSession对象
            SqlSession sqlSession = factory.openSession();
            // 执行操作
            sqlSession.insert("insert", book);
            // 提交操作
            sqlSession.commit();
            // 关闭SqlSession
            sqlSession.close();
        }
    }
    

    作用域(Scope)和生命周期

    SqlSessionFactoryBuilder

    这个类可以被实例化、使用和丢弃,一旦创建了 SqlSessionFactory,就不再需要它了。 因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。 你可以重用 SqlSessionFactoryBuilder 来创建多个 SqlSessionFactory 实例,但是最好还是不要让其一直存在,以保证所有的 XML 解析资源可以被释放给更重要的事情。

    SqlSessionFactory

    SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。 使用 SqlSessionFactory 的最佳实践是在应用运行期间不要重复创建多次,多次重建 SqlSessionFactory 被视为一种代码“坏味道(bad smell)”。因此 SqlSessionFactory 的最佳作用域是应用作用域。 有很多方法可以做到,最简单的就是使用单例模式或者静态单例模式。

    public class MyBatisUtil {
        private final static SqlSessionFactory sqlSessionFactory;
        static {//在程序的一次执行过程中,static{}语句块中的内容只被执行一次
            String resource = "mybatis-config.xml";
            Reader reader = null;
            try {
                reader = Resources.getResourceAsReader(resource);
            } catch (IOException e) {
                System.out.println(e.getMessage());
    
            }
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
        }
    
        public static SqlSessionFactory getSqlSessionFactory() {
            return sqlSessionFactory;
        }
    }
    

    SqlSession

    每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。 绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。 也绝不能将 SqlSession 实例的引用放在任何类型的托管作用域中,比如 Servlet 框架中的 HttpSession。 如果你现在正在使用一种 Web 框架,要考虑 SqlSession 放在一个和 HTTP 请求对象相似的作用域中。 换句话说,每次收到的 HTTP 请求,就可以打开一个 SqlSession,返回一个响应,就关闭它。 这个关闭操作是很重要的,你应该把这个关闭操作放到 finally 块中以确保每次都能执行关闭。 下面的示例就是一个确保 SqlSession 关闭的标准模式:
    在你的所有的代码中一致地使用这种模式来保证所有数据库资源都能被正确地关闭。

    映射器实例

    映射器是一些由你创建的、绑定你映射的语句的接口。映射器接口的实例是从 SqlSession 中获得的。因此从技术层面讲,任何映射器实例的最大作用域是和请求它们的 SqlSession 相同的。尽管如此,映射器实例的最佳作用域是方法作用域。 也就是说,映射器实例应该在调用它们的方法中被请求,用过之后即可丢弃。 并不需要显式地关闭映射器实例,尽管在整个请求作用域保持映射器实例也不会有什么问题,但是你很快会发现,像 SqlSession 一样,在这个作用域上管理太多的资源的话会难于控制。 为了避免这种复杂性,最好把映射器放在方法作用域内。下面的示例就展示了这个实践

    实例展示

    一个POJO:User.java

    public class User {
        private String name;
        private Integer age;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Integer getAge() {
            return age;
        }
    
        public void setAge(Integer age) {
            this.age = age;
        }
    
        public User(String name, Integer age) {
            super();
            this.name = name;
            this.age = age;
        }
    
        public User() {
            super();
        }
    }
    

    一个UserMapper接口,只是定义了访问数据表的接口:

    public interface UserMapper {
        public void insertUser(User user);
        public User getUser(String name);
    }
    

    数据访问接口映射文件UserMapper.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="包名.UserMapper">
        <!-- 这里namespace必须是UserMapper接口的路径,加上包名” -->
        <insert id="insertUser" parameterType="User">
            insert into user(name,age) values(#{name},#{age})
            <!-- 这里sql结尾不能加分号,否则报“ORA-00911”的错误 -->
        </insert>
    
        <!-- 这里的id必须和UserMapper接口中的接口方法名相同 -->
        <select id="getUser" resultType="User" parameterType="java.lang.String">
            select * from user where name=#{name}
        </select>
    </mapper>  
    

    在mybatis-config.xml中由mappers/mapper节点的resource指定UserMapper.xml。



    在全局维护一个SqlSessionFactory

    public class MyBatisUtil {
        private final static SqlSessionFactory sqlSessionFactory;
        static {//在程序的一次执行过程中,static{}语句块中的内容只被执行一次
            String resource = "mybatis-config.xml";
            Reader reader = null;
            try {
                reader = Resources.getResourceAsReader(resource);
            } catch (IOException e) {
                System.out.println(e.getMessage());
    
            }
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
        }
    
        public static SqlSessionFactory getSqlSessionFactory() {
            return sqlSessionFactory;
        }
    }
    

    测试代码

    public class TestMyBatis {
        static SqlSessionFactory sqlSessionFactory = null;
        static {
            sqlSessionFactory = MyBatisUtil.getSqlSessionFactory();
        }
    
        public static void main(String[] args) {
            testAdd();
            getUser();
        }
    
        public static void testAdd() {
            SqlSession sqlSession = sqlSessionFactory.openSession();
            try {
                UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
                User user = new User("lisi", new Integer(25));
                userMapper.insertUser(user);
                sqlSession.commit();// 这里一定要提交,不然数据进不去数据库中
            } finally {
                sqlSession.close();//可以看到作用域为方法区
            }
        }
    
        public static void getUser() {
            SqlSession sqlSession = sqlSessionFactory.openSession();
            try {
                UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
                User user = userMapper.getUser("zhangsan");
                System.out.println("name: " + user.getName() + "|age: "
                        + user.getAge());
            } finally {
                sqlSession.close();
            }
        }
    }
    

    参考文件
    https://www.cnblogs.com/zhanjindong/p/3397828.html
    https://mybatis.org/mybatis-3/zh/getting-started.html

    相关文章

      网友评论

        本文标题:mybatis工作原理

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