每一年都奔走在自己热爱里

没有人是一座孤岛,总有谁爱着你

Mybatis基础学习笔记(三)——自定义Mybtis

Mybatis基础学习笔记(三)

传统的dao执行sql
  • 在前面直接使用Sqlsession执行sql语句时,与dao接口并没有直接关系
  • 使用传统dao执行sql,方法是创建dao接口的实现类,然后配置mysql创建Sqlseesion对象执行sql语句,依然繁琐,重复
  • 分析:如果使用传统方法实现sql语句调用,dao接口及其所调用的方法即为id坐标,方法的返回值即可选择Sqlsession对象需要执行的方法,这些信息都可以使用反射获取到
Mybatis框架执行sql语句
  • Mybatis根据配置信息获取执行sql所需要的信息,包括id坐标,返回值... ...使用动态代理技术,代理dao接口,实现dao接口的实现类的功能,免去了实现dao接口的繁琐

  • <!--观察mapper映射配置文件可以知道,方法的id坐标:namespace+id方法名,结果类型,方法参数... ...都包含-->
    <mapper namespace="com.itheima.dao.IAccountDao">
    select="com.Xxx.dao.IUserDao.findById"></association>
        <!-- 查询所有 -->
        <select id="findAll" resultMap="accountUserMap">
            select * from account
        </select>
    
  • 使用Mybatis代理的要求:

    • namespace必须是dao接口的全限定类名
    • mapper文件中id方法名需要时接口中对应的方法名称

自定义Mybatis实现的关键步骤

  • 自建SqlSession类,实现getMapper方法,根据传入的字节码,通过动态代理技术获取sql语句执行所需要的信息返回被代理的,接口的,代理对象proxy

  • public class DefaultSqlSession implements SqlSession {
    
        private Configuration cfg;
        private Connection connection;
    
        public DefaultSqlSession(Configuration cfg){
            this.cfg = cfg;
            //根据配置对象获取连接信息,创建连接
            connection = DataSourceUtil.getConnection(cfg);
        }
    
        /**
         * 用于创建代理对象
         * @param daoInterfaceClass dao的接口字节码
         */
        @Override
        public <T> T getMapper(Class<T> daoInterfaceClass) {
            return (T) Proxy.newProxyInstance(daoInterfaceClass.getClassLoader(),
                    new Class[]{daoInterfaceClass},new MapperProxy(cfg.getMappers(),connection));
        }
    
        /**
         * 用于释放资源
         */
        @Override
        public void close() {
            if(connection != null) {
                try {
                    connection.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
  • 一个代理的执行具体增强对象

  • public class MapperProxy implements InvocationHandler {
    
        //map的key是全限定类名+方法名
        private Map<String,Mapper> mappers;
        private Connection conn;
    
        public MapperProxy(Map<String,Mapper> mappers,Connection conn){
            this.mappers = mappers;
            this.conn = conn;
        }
        /**
         * 用于对方法进行增强的,我们的增强其实就是调用selectList方法
         */
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            //1.获取方法名
            String methodName = method.getName();
            //2.获取方法所在类的名称
            String className = method.getDeclaringClass().getName();
            //3.组合key
            String key = className+"."+methodName;
            //4.获取mappers中的Mapper对象
            Mapper mapper = mappers.get(key);
            //5.判断是否有mapper
            if(mapper == null){
                throw new IllegalArgumentException("传入的参数有误");
            }
            //6.调用工具类执行查询所有
            return new Executor().selectList(mapper,conn);
        }
    }
    
  • 一个执行具体操作的执行器

  • public class Executor {
        public <E> List<E> selectList(Mapper mapper, Connection conn) {
            PreparedStatement pstm = null;
            ResultSet rs = null;
            try {
                //1.取出mapper中的数据
                String queryString = mapper.getQueryString();//select * from user
                String resultType = mapper.getResultType();//com.itheima.domain.User
                Class domainClass = Class.forName(resultType);
                //2.获取PreparedStatement对象
                pstm = conn.prepareStatement(queryString);
                //3.执行SQL语句,获取结果集
                rs = pstm.executeQuery();
                //4.封装结果集
                List<E> list = new ArrayList<E>();//定义返回值
                while(rs.next()) {
                    //实例化要封装的实体类对象
                    E obj = (E)domainClass.newInstance();
    
                    //取出结果集的元信息:ResultSetMetaData
                    ResultSetMetaData rsmd = rs.getMetaData();
                    //取出总列数
                    int columnCount = rsmd.getColumnCount();
                    //遍历总列数
                    for (int i = 1; i <= columnCount; i++) {
                        //获取每列的名称,列名的序号是从1开始的
                        String columnName = rsmd.getColumnName(i);
                        //根据得到列名,获取每列的值
                        Object columnValue = rs.getObject(columnName);
                        //给obj赋值:使用Java内省机制(借助PropertyDescriptor实现属性的封装)
                        PropertyDescriptor pd = new PropertyDescriptor(columnName,domainClass);//要求:实体类的属性和数据库表的列名保持一种
                        //获取它的写入方法
                        Method writeMethod = pd.getWriteMethod();
                        //把获取的列的值,给对象赋值
                        writeMethod.invoke(obj,columnValue);
                    }
                    //把赋好值的对象加入到集合中
                    list.add(obj);
                }
                return list;
            } catch (Exception e) {
                throw new RuntimeException(e);
            } finally {
                release(pstm,rs);
            }
        }
    //释放资源
    }
    
posted @ 2020-12-08 13:08  雨下整夜~  阅读(119)  评论(0)    收藏  举报