mybatis 简单理解

MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射,
MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。
MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。

mybatis核心流程三大阶段:
1、初始化阶段
读取xml配置文件和注解中的配置信息,创建配置对象,并完成各个模块的初始化的工作。

//1、读取mybatis配置文件,创建SqlSessionFactroy
String resource = "mybatis/mybatis-config.xml";
InputStream is = Resources.getResourceAsStream(resource);
SqlSessionFactory factroy = new SqlSessionFactoryBuilder().build(is);
is.close();


2、代理阶段
封装iBatis的编程模型。使用mapper接口开发的初始化工作

//2、获取sqlSession
SqlSession sqlSession = factroy.openSession();
//3、获取对应mapper
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);


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

//4、执行查询语句并返回单条数据
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中对应的方法执行
找到命名空间和方法名
传递参数

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值