认识MyBatis

MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射,

MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。

MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。

mybatis核心流程三大阶段:

1、初始化阶段

读取xml配置文件和注解中的配置信息,创建配置对象,并完成各个模块的初始化的工作。

    读取mybatis配置文件,创建SqlSessionFactroy

        String resource = "mybatis/mybatis-config.xml";

        InputStream is = Resources.getResourceAsStream(resource);

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

        is.close();

2、代理阶段

封装iBatis的编程模型。使用mapper接口开发的初始化工作

    获取sqlSession

        SqlSession sqlSession = factroy.openSession();

    3、获取对应mapper

UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

3、数据读写阶段

遵循JDBC规范,通过SqlSession完成SQL的解析,参数的映射、SQL的执行、结果的反射解析过程。

    执行查询语句并返回单条数据

        User user = userMapper.getUser(1);

        System.out.println(user.getUsername());

理解SqlSession:

    SqlSession意味着创建数据库会话,代表了一次与数据库的连接;

    是mybatis对外提供数据访问的主要API,实际上SqlSeesion的功能都是基于Executor来实现的。

    Executor是mybatis核心接口之一,定义了数据库操作最基本的方法。

    Executor的三个重要组件

    StatementHandler:它的作用是使用数据库的Statement或PrepareStatement执行操作,启承上启下作用;

    ParameterHandler:对预编译的SQL语句进行参数设置

    ResultSetHandler:对数据库返回的结果集(ResultSet)进行封装,返回用户指定的实体类型;

    简单mybatis实现思路:

1、创建SqlSessionFactroy实例;

            private static final String MAPPER_CONFIG_LOCATION = "mappers";

            private static final String DB_CONFIG_FILE = "db.properties";

            private final Configuration config = new Configuration();

            public SqlSessionFactory() {

        //加载数据库信息(db.properties)

        loadDbInfo();

        //加载mapper.xml信息

        loadMappersInfo();

    }

2、实例化过程中,加载配置文件创建configuration对象;

        public class Configuration {

        private String jdbcDriverClass;

        private String jdbcUrl;

        private String jdbcUsername;

        private String jdbcPassword;

        //存放多个mapper.xml文件的信息()

        private Map<String,MapperdStatement> mapperdStatements = new HashMap<>();

        }

        public class MapperdStatement {

        private String namespace; //命名空间

        private String sourceId; //封装ID(命名空间+select的ID)

        private String resultType; //select 返回类型

        private String sql; //sql语句

        }

3、通过factroy创建sqlSession;

        public SqlSession openSession(){

        return new DefaultSqlSession(config);

        }

4、通过sqlSeesion获取mapper接口动态代理;

        public <T> T getMapper(Class<T> type) {

        //动态代理:InvocationHandler和Proxy

        //参数:1、指定当前目标对象使用类加载器,获取加载器的方法是固定的

        //2、目标对象实现的接口的类型,使用泛型方式确认类型

        //3、事件处理,执行目标对象的方法时,会触发事件处理器的方法,会把当前执行目标对象的方法作为参数传入

        //生成代理对象

        MapperProxy mp = new MapperProxy(this);

        return (T) Proxy.newProxyInstance(type.getClassLoader(), new Class[]{type}, mp);

        }

5、动态代理回调sqlSession中某查询方法;

        public class MapperProxy implements InvocationHandler {

        private SqlSession session;

        public MapperProxy(SqlSession session) {

        super();

        this.session = session;

        }

@Override

        public Object invoke(Object proxy, Method method, Object[] args)

        throws Throwable {

        //三步翻译

        //判断方法的返回类型是否是集合类的子类

        //是就调用session的selectList的方法

        if(Collection.class.isAssignableFrom(method.getReturnType())){

        return session.selectList(method.getDeclaringClass().getName()+"."+method.getName(), args==null?null:args[0]);

        }else{

        return session.selectOne(method.getDeclaringClass().getName()+"."+method.getName(), args==null?null:args[0]);

        }

        }

        }

6、SQLSession将查询方法转发给Executor;

        private final Configuration config;

        private Executor executor;

        public <E> List<E> selectList(String statement, Object parameter) {

        MapperdStatement ms = config.getMapperdStatements().get(statement);

        return executor.query(ms, parameter);

        }

7、Executor基于JDBC访问数据库获取数据;

        @SuppressWarnings("unchecked")

        @Override

        public <E> List<E> query(MapperdStatement ms, Object parameter) {

        List<E> ret = new ArrayList<E>();

        try {

        Class.forName(config.getJdbcDriverClass());

        } catch (ClassNotFoundException e) {

        e.printStackTrace();

        }

        Connection conn = null;

        PreparedStatement ps = null;

        ResultSet rs = null;

        try {

        conn = DriverManager.getConnection(config.getJdbcUrl(),config.getJdbcUsername(),config.getJdbcPassword());

        ps = conn.prepareStatement(ms.getSql());

        ps.setString(1, parameter.toString());

        //处理占位符

        // parameterize(ps,parameter);

        rs = ps.executeQuery();

        User user = new User();

        while(rs.next()){

        user.setId(rs.getInt(1));

        user.setUsername(rs.getString(2));

        user.setPassword(rs.getString(3));

        ret.add((E)user);

        }

        return ret;

        //把结果集反射到对象上

        // handlerResultSet(rs,ret,ms.getResultType());

        } catch (SQLException e) {

        // TODO Auto-generated catch block

        e.printStackTrace();

        }finally{

        try {

        if(rs!=null){

        rs.close();

        }

        if(ps!=null){

        ps.close();

        }

        if(conn!=null){

        conn.close();

        }

        } catch (Exception e1) {

        e1.printStackTrace();

        }

        }

        return null;

        }

        //mybatis源码

public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {

    Statement stmt = null;

    try {

      Configuration configuration = ms.getConfiguration();

      // 创建StatementHandler对象

      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);

      // 完成Statement的创建和初始化,并使用parameterHandle对占位符进行处理

      stmt = prepareStatement(handler, ms.getStatementLog());

      // 调用StatementHandler.query方法,执行SQL语句,

      //并通过ResultSetHandler完成结果集的映射

      return handler.query(stmt, resultHandler);

    } finally {

      closeStatement(stmt);

    }

  }

private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {

    Statement stmt;

    //获取connection对象的动态代理,添加日志能力

    Connection connection = getConnection(statementLog);

    //通过不同的StatementHandler,利用connection创建(parame)Statement

    stmt = handler.prepare(connection, transaction.getTimeout());

    //调用parameterHandler处理占位符

    handler.parameterize(stmt);

    return stmt;

  }

8、Executor通过反射将数据转换成POJO并返回给sqlSession;

//mybatis源码

//处理占位符的具体业务实现

  @Override

  public void setParameters(PreparedStatement ps) {

    ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());

    //从boundSql中获取sql语句的占位符对应的参数信息

    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();

    //遍历参数列表,把参数设置到preparedStatement中

    if (parameterMappings != null) {

      for (int i = 0; i < parameterMappings.size(); i++) {

        ParameterMapping parameterMapping = parameterMappings.get(i);

        //对于存储过程中的参数不处理

        if (parameterMapping.getMode() != ParameterMode.OUT) {

          Object value;//绑定的实体

          String propertyName = parameterMapping.getProperty();//参数的名字

          if (boundSql.hasAdditionalParameter(propertyName)) { // 获取对应的实参值

            value = boundSql.getAdditionalParameter(propertyName);

          } else if (parameterObject == null) {

            value = null;

          } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {

            value = parameterObject;

          } else {

            MetaObject metaObject = configuration.newMetaObject(parameterObject);

            value = metaObject.getValue(propertyName);

          }

          TypeHandler typeHandler = parameterMapping.getTypeHandler();

          JdbcType jdbcType = parameterMapping.getJdbcType();

          if (value == null && jdbcType == null) {

            jdbcType = configuration.getJdbcTypeForNull();

          }

          try {

          //为statement中的占位符绑定参数

            typeHandler.setParameter(ps, i + 1, value, jdbcType);

          } catch (TypeException | SQLException e) {

            throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);

          }

        }

      }

    }

  }

//查询结果集在这里处理,使用反射技术将查询结果映射到实体类中

  @Override

  public List<Object> handleResultSets(Statement stmt) throws SQLException {

    ErrorContext.instance().activity("handling results").object(mappedStatement.getId());

    //用于保存结果集对象

    final List<Object> multipleResults = new ArrayList<>();

    int resultSetCount = 0;

    //statement可能返回多个结果集对象,这里先取出第一个结果集

    ResultSetWrapper rsw = getFirstResultSet(stmt);

    //获取结果集对应的resultMap

    List<ResultMap> resultMaps = mappedStatement.getResultMaps();

    int resultMapCount = resultMaps.size();

    validateResultMapsCount(rsw, resultMapCount);

    while (rsw != null && resultMapCount > resultSetCount) {

      ResultMap resultMap = resultMaps.get(resultSetCount);

      handleResultSet(rsw, resultMap, multipleResults, null);

      rsw = getNextResultSet(stmt);

      cleanUpAfterHandlingResultSet();

      resultSetCount++;

    }

    String[] resultSets = mappedStatement.getResultSets();

    if (resultSets != null) {

      while (rsw != null && resultSetCount < resultSets.length) {

        ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);

        if (parentMapping != null) {

          String nestedResultMapId = parentMapping.getNestedResultMapId();

          ResultMap resultMap = configuration.getResultMap(nestedResultMapId);

          handleResultSet(rsw, resultMap, null, parentMapping);

        }

        rsw = getNextResultSet(stmt);

        cleanUpAfterHandlingResultSet();

        resultSetCount++;

      }

    }

    return collapseSingleResultList(multipleResults);

  }

9、将数据返回给调用者

public <E> List<E> selectList(String statement, Object parameter) {

MapperdStatement ms = config.getMapperdStatements().get(statement);

return executor.query(ms, parameter);

}

mybatis的两种编程模型:

1、使用mapper接口编程,就可以访问数据库;

        //--第一阶段--

//1、读取mybatis配置文件,创建SqlSessionFactroy

String resource = "mybatis/mybatis-config.xml";

InputStream is = Resources.getResourceAsStream(resource);

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

is.close();

//--第二阶段--

//2、获取sqlSession

SqlSession sqlSession = factroy.openSession();

//3、获取对应mapper

UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

//--第三阶段--

//4、执行查询语句并返回单条数据

User user = userMapper.getUser(1);

System.out.println(user.getUsername());

2、使用sqlsession对外提供数据库的访问。

        //1、读取mybatis配置文件,创建SqlSessionFactroy

String resource = "mybatis/mybatis-config.xml";

InputStream is = Resources.getResourceAsStream(resource);

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

is.close();

//2、获取sqlSession

SqlSession sqlSession = factroy.openSession();

User user = sqlSession.selectOne("com.mybatis.mapper.UserMapper.getUser", 1);

System.out.println(user.getUsername());

为什么mapper接口没有实现类,也可调用方法查询语句:

实质上是mybatis内部都是实现的sqlSession接口开发。

mapper接口开发 内部翻译  sqlSession接口开发

配置文件解读+动态代理增强

找到session中对应的方法执行

找到命名空间和方法名

传递参数

————————————————

版权声明:本文为CSDN博主「努力材质无能」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/yunhaiwuya/article/details/93454974

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,236评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,867评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,715评论 0 340
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,899评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,895评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,733评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,085评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,722评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,025评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,696评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,816评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,447评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,057评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,009评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,254评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,204评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,561评论 2 343

推荐阅读更多精彩内容