认识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

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容