持久层和数据访问层_自定义实现Mybatis持久层框架

回到目录:

OrangeZh:拉勾教育:JAVA高薪训练营 学习笔记​zhuanlan.zhihu.com

介绍

自定义持久层框架Demo,此Demo仅为个人学习编写,目前实现了单条查询、所有查询以及单条的增删改

项目源码:去看源码

JDBC问题分析及解决思路

public void normalJdbc() {
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        try {
            //加载数据库驱动
            Class.forName("com.mysql.jdbc.Driver");
            //获取数据库链接
            connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/ozdemo?characterEncoding = utf - 8", " root", " 123456");
            // 定义sql ?代表占位符
            String sql = "select * from user where username = ?";
            // 获取预处理Statement
            preparedStatement = connection.prepareStatement(sql);
            //设置参数 第一个参数为参数位置(1开始),第二个参数为参数的值
            preparedStatement.setString(1, "tom");
            // 执行sql,获得查询结果
            resultSet = preparedStatement.executeQuery();
            // 变量结果
            User user = new User();
            while (resultSet.next()) {
                int id = resultSet.getInt("uid");
                String username = resultSet.getString("username");
                // 封装结果
                user.setUid(id);
                user.setUsername(username);
            }
            System.out.println(user);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 释放资源
            if (resultSet != null) {
                try {
                    resultSet.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (preparedStatement != null) {
                try {
                    preparedStatement.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (connection != null) {
                try {
                    connection.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }

问题总结:

  • 连接频繁创建、释放造成了资源浪费,影响系统性能
  • Sql语句在代码中硬编码,不易维护
  • 结果集封装硬编码,不易维护

解决思路

  • 使用连接池
  • 使用配置文件维护sql
  • 封装结果集使用反射、内省

自定义持久层框架思路

使用端(需要使用持久层的项目):引入自定义持久层的jar包

主要提供2部分配置信息:数据库配置信息、sql配置信息(包含:sql语句、参数类型、返回类型)

使用配置文件来提供这2部分配置信息:

  • sqlMapConfig.xml:存放数据库配置信息,存放mapper.xml全路径
  • mapper.xml:存放sql配置信息

自定义持久层框架本身(持久层框架jar):本质就是对JDBC代码进行封装

1、加载配置文件:根据配置文件的路径,加载配置文件成字节输入流,存储在内存中

创建Resources类 方法:InputStream getResourceAsSteam(String path)

2、创建2个javaBean(容器对象):存放的就是对配置文件解析出来的内容

Configuration:核心配置类:存放sqlMapConfig.xml解析出来的内容
MapperStatement:映射配置类:存放mapper.xml解析出来的内容

3、解析配置文件:dom4j

创建类:SqlSessionFactoryBUilder 方法:build(InputSteam in)
    第一:使用dom4j解析配置文件,将解析出来的内容封装到容器对象中
    第二:创建SqlSessionFactory对象,生产sqlSession:会话对象(工厂模式)

4、创建SqlsessionFactory接口及实现DefaultSqlSessionFactory

方法openSeeion():生产sqlSession

5、创建SqlSession接口及实现类DefaultSession

定义对数据库的curd操作:selectList()、selectOne()、update()、delete()、insert()

6、创建Excutor接口及实现类SimpleExcutor实现类

JDBC操作的执行过程:
	1、注册驱动,获取连接
    	2、获取Sql语句 然后转换sql语句,并对#{}里面的值进行解析存储
    	3、获取预处理对象: preparedStatement
    	4、设置参数
	5、执行sql
    	6、返回更新的条数
	
	prepareStatement(Configuration configuration, MappedStatement mappedStatement, Object... params):准备预处理对象(1、2、3、4)
	query(Configuration configuration, MappedStatement mappedStatement, Object... params):查询操作的执行(5、6)
	update(Configuration configuration, MappedStatement mappedStatement, Object... params):增、删、改操作的执行(5、6)6)

自定义持久层框架测试

com.ozdemo.test.IPersistenceTest

public void test() throws Exception {
        InputStream inputStream = Resources.getResourceAsSteam("sqlMapConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();

        //查询
        System.out.println("------------------ 查询 -------------------");
        User user = new User();
        user.setUid(1);
        user.setUsername("张三");

        IUserDao iUserDao = sqlSession.getMapper(IUserDao.class);
        User resUser1 = iUserDao.findOne(user);
        System.out.println("查询结果:" + resUser1.toString());

        //新增
        System.out.println();
        System.out.println("------------------ 新增 -------------------");
        User user1 = new User();
        user1.setUsername("XX7");
        int res1 = iUserDao.insertOne(user1);
        System.out.println("添加成功条数:" + res1);

        //修改
        System.out.println();
        System.out.println("------------------ 修改 -------------------");
        User user2 = new User();
        user2.setUid(1);
        user2.setUsername("XX-update");
        int res2 = iUserDao.updateOne(user2);
        System.out.println("更新成功条数:" + res2);

        //删除
        System.out.println();
        System.out.println("------------------ 删除 -------------------");
        int res3 = iUserDao.deleteById(6);
        System.out.println("删除成功条数:" + res3);
    }

Mybatis框架核心

Configuration:初始化基础配置,所有的配置信息都在Configuration中

SqlSessionfactory:Sqlsession工厂
Sqlsession:Mybatis的顶层api,表示数据库交互的会话,完成数据库的增删查改的一些功能

Executor:Mybatis的执行器,是Mybatis调度的核心,负责sql语句的生成和查询缓存

StatementHandler:封装了JDBC Statement 操作
ParameterHandler:负责将传入的参数转化为JDBC Statment 的参数
ResultSethandler:负责将JDBC返回的Resultset集对象封装成返回集合
TypeHandler:负责将数据库的类型何实体类型之间的转化何映射

MappedStatement:MappedStatement是select,update,delete,insert节点的封装

SqlSource:负责根据用户传递的parameterObject 动态的生成一条sql语句
Boundsql:表示动态生成的sql何对应的参数信息

Mybatis执行过程

d570c779cce81c8a7ff9190f63537d5c.png
Mybatis执行流程图

Mybatis相关问题

1、Mybatis动态sql是做什么的?都有哪些动态sql?简述一下动态sql的执行原理?

  • 动态sql是通过在xml文件中添加条件判断标签,从而动态判断拼接成sql语句;
  • 动态sql标签包括有:if、choose(when,otherwise)、trim、 where、set、foreach
  • 动态sql其实就是使用Ognl表达式,通过sql的参数对象计算出表达式的值,然后进行动态的判断拼接

2、Mybatis是否支持延迟加载?如果支持,它的实现原理是什么?

  • Mybatis支持延迟加载,只能在resultMap中使用,支持一对一和一对多的延迟加载,分别使用association和cllection标签
  • 原理:通过CGLib创建目标对象的代理对象,当调用目标对象的方法时,调用目标对象的查询方法,从而实现使用时再查询,减轻了数据库的压力;即当需要关联查询时再进行数据查询

3、Mybatis都有哪些Executor执行器?它们之间的区别是什么?

  • Mybatis有3种Executor执行器:SimpleExecutor、ReuseExecutor、BatchExecutor
  • 区别:
SimpleExecutor:每次执行都会创建一个Statement对象 
ReuseExecutor:每次执行都会缓存Statement对象,并以sql作为可以,如果存在就直接使用,不存在就创建 
BatchExecutor:批处理执行器,仅支持update,先将所有sql都通过addBatch()方法添加到批处理中,然后通过executeBatch()方法统一执行
  • 可在Mybatis配置文件中配置默认执行器类型,也可以在创建SqlSession时传入ExecutorType参数

4、简述下Mybatis的一级、二级缓存(分别从存储结构、范围、失效场景。三个方面来作答)?

  • 一级缓存:Mybatis的一级缓存是SqlSession下的缓存,作用在Sqlsession中,当Sqlsession关闭或者查询的数据update时缓存失效;内部使用HashMap进行存储,以CacheKey对象做为key,当查询时会先去查询缓存中是否存在,如果存在直接返回或者查询数据库
  • 二级缓存:二级缓存属于Mapper的缓存,多个SqlSession共用二级缓存,当同一个Mapper中的增删改时缓存失效;内部同样使用HashMap进行存储,同样使用CacheKey对象做为key;Mybatis中默认不开启二级缓存,可通过cacheEnabled来设置是否开启二级缓存;在分布式环境下可配置redis等来做为二级缓存

5、简述Mybatis的插件运行原理,以及如何编写一个插件?

  • Mybatis的4大组件ParameterHandler、ResultSetHandler、StatementHandler、Executor提供了扩展插件机制;Mybatis使用动态代理,来为需要拦截的接口生产代理对象,以实现接口方法拦截功能;
  • 实现Interceptor接口并重写intercept()方法,然后加上@Interceptors注解,指定需要拦截的方法,将自定义的插件配置到配置文件中

下一篇:

OrangeZh:Mybatis应用及源码分析​zhuanlan.zhihu.com

回到目录:

OrangeZh:拉勾教育:JAVA高薪训练营 学习笔记​zhuanlan.zhihu.com

文章内容输出来源:拉勾教育 Java高薪训练营

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值