Mybatis源码(二)mybatis核心组件
Configuration:用于描述mybatis的主配置信息,其他组件需要获取配置信息时,直接通过Configuration对象获取
MappedStatement:用于描述Mapper中的SQL的配置信息,是对MapperXML配置文件中select|update|delete|insert语句的封装
SqlSession:SqlSession是Mybatis提供的面向用户的API,表示和数据库交互时的会话对象。用于完成数据库的增删改查功能。SqlSession是Executor组件的外观。目的是对外提供易于理解和使用的操作数据库的接口
Executor:Executor是操作数据库的执行器。Mybatis中所有的增删改查操作都是由该执行器完成的
StatementHandler:StatementHandler是对Statement对象的封装。用于为Statement对象设置参数。调用statement接口提供的方法和数据库进行交互
parameterHandler: 当mybatis框架使用的statement类型为CallableStatement和prepareStatement时,为他们参数占位符设置值
ResultHandler:ResultHandler用于对JDBC的操作中ResultSet对象操作。执行SQL类型为SELECT语句时,ResultSetHandler用于将查询结果转换为Java对象
TypeHandler:Mybatis中的类型处理器。用于处理java类型和jdbc类型之间的映射
1.Configuration
1.Mybatis启动后,会对所有的配置信息进行解析,然后将解析后的内容注册到Configuration对象中。
2.Configuration对象还将作为Executor和StatementHandler和ResultSetHandler和ParameterHandler组件的工厂类
3.Configuration类中提供了用于控制Mybatis的属性行为配置和作为容器存放TypeHandler和TypeAlias和Mapper接口和MapperSQL的配置信息
2.Executor
SqlSession是mybatis提供的操作数据库的api,但是真正执行的还是Executor
Executor执行器分类
SimpleExecutor:基础的执行器,能完成增删改查操作
BatchExecutor:用于批量操作
ResueExecutor:对JDBC的statement对象做了缓存。当执行相同的sql语句时,直接从缓存中取出statement对象进行复用避免频繁的创建和销毁对象。享元模式的思想
代码使用案例
public class ExecutorExample { @Before public void initData() { DbUtils.initData(); } @Test public void testExecutor() throws IOException, SQLException { // 获取配置文件输入流 InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml"); // 通过SqlSessionFactoryBuilder的build()方法创建SqlSessionFactory实例 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); // 调用openSession()方法创建SqlSession实例 SqlSession sqlSession = sqlSessionFactory.openSession(); Configuration configuration = sqlSession.getConfiguration(); // 从Configuration对象中获取描述SQL配置的MappedStatement对象 MappedStatement listAllUserStmt = configuration.getMappedStatement( "com.blog4java.mybatis.com.blog4java.mybatis.example.mapper.UserMapper.listAllUser"); //创建ReuseExecutor实例 Executor reuseExecutor = configuration.newExecutor( new JdbcTransaction(sqlSession.getConnection()), ExecutorType.REUSE ); // 调用query()方法执行查询操作 List<UserEntity> userList = reuseExecutor.query(listAllUserStmt, null, RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER); System.out.println(JSON.toJSON(userList)); } }
3.MapperStatement
用于描述Mapper中的SQL的配置信息,是对MapperXML配置文件中select|update|delete|insert语句的封装
4.StatementHandler
StatementHandler是对Statement对象的封装。用于为Statement对象设置参数。调用statement接口提供的方法和数据库进行交互
StatementHandler实现类有SimpleStatementHandler、RoutingStatementHandler、CallableStatementHandler、PreparedStatementHandler
5.ParameterHandler
作用:在preparedStatementHandler和CallableStatementHandler操作数据数据库之前为参数占位符设置值
6.ResultSetHandler
ResultSetHandler用于在StatementHandler对象执行完查询操作后或者存储过程后,对结果集或者存储过程执行后续处理
ResultSetHandler是一个接口,只有一个默认的实现类 DefaultResultSetHandler
ResultSetHandler接口中有三个方法:
<E> List<E> handleResultSets(Statement stmt) throws SQLException; 获取statement对象的resultSet对象。对ResultSet对象进行处理。返回包含结果实体的List对象
@Override public List<Object> handleResultSets(Statement stmt) throws SQLException { ErrorContext.instance().activity("handling results").object(mappedStatement.getId()); final List<Object> multipleResults = new ArrayList<Object>(); int resultSetCount = 0; // 1、获取ResultSet对象,將ResultSet对象包装为ResultSetWrapper ResultSetWrapper rsw = getFirstResultSet(stmt); // 2、获取ResultMap信息,一般只有一个ResultMap List<ResultMap> resultMaps = mappedStatement.getResultMaps(); int resultMapCount = resultMaps.size(); // 校验ResultMap,如果该ResultMap名称没有配置,则抛出异常 validateResultMapsCount(rsw, resultMapCount); // 如果指定了多个ResultMap,则对每个ResultMap进行处理 while (rsw != null && resultMapCount > resultSetCount) { ResultMap resultMap = resultMaps.get(resultSetCount); // 3、调用handleResultSet方法处理结果集 handleResultSet(rsw, resultMap, multipleResults, null); // 获取下一个结果集对象,需要JDBC驱动支持多结果集 rsw = getNextResultSet(stmt); cleanUpAfterHandlingResultSet(); resultSetCount++; } // 如果JDBC驱动支持多结果集,可以通过<select>标签resultSets属性指定多个ResultMap // 处理<select>标签resultSets属性,该属性一般情况不会指定 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方法处理结果集 handleResultSet(rsw, resultMap, null, parentMapping); } rsw = getNextResultSet(stmt); cleanUpAfterHandlingResultSet(); resultSetCount++; } } // 对multipleResults进行处理,如果只有一个结果集,则返回结果集中的元素,否则返回多个结果集 return collapseSingleResultList(multipleResults); }
<E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException;将ResultSet对象包装成Cursor对象。对Cursor对象进行遍历时,能够动态的从数据库查询数据,避免一次性将所有的数据加载到内存中
@Override public <E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException { ErrorContext.instance().activity("handling cursor results").object(mappedStatement.getId()); ResultSetWrapper rsw = getFirstResultSet(stmt); List<ResultMap> resultMaps = mappedStatement.getResultMaps(); int resultMapCount = resultMaps.size(); validateResultMapsCount(rsw, resultMapCount); if (resultMapCount != 1) { throw new ExecutorException("Cursor results cannot be mapped to multiple resultMaps"); } ResultMap resultMap = resultMaps.get(0); return new DefaultCursor<E>(this, resultMap, rsw, rowBounds); }
void handleOutputParameters(CallableStatement cs) throws SQLException; 处理存储过程调用结果
// 处理存储过程Output参数 @Override public void handleOutputParameters(CallableStatement cs) throws SQLException { final Object parameterObject = parameterHandler.getParameterObject(); final MetaObject metaParam = configuration.newMetaObject(parameterObject); final List<ParameterMapping> parameterMappings = boundSql.getParameterMappings(); for (int i = 0; i < parameterMappings.size(); i++) { final ParameterMapping parameterMapping = parameterMappings.get(i); if (parameterMapping.getMode() == ParameterMode.OUT || parameterMapping.getMode() == ParameterMode.INOUT) { if (ResultSet.class.equals(parameterMapping.getJavaType())) { handleRefCursorOutputParameter((ResultSet) cs.getObject(i + 1), parameterMapping, metaParam); } else { final TypeHandler<?> typeHandler = parameterMapping.getTypeHandler(); metaParam.setValue(parameterMapping.getProperty(), typeHandler.getResult(cs, i + 1)); } } } }
7.TypeHanadler
Mybatis通过TypeHandlerRegistry建立JDBC类型、java类型与TypeHandler之间的转换关系
TypeHandlerRegister通过map对象保存JDBC类型、java类型和handler之间的关系
如果自定义了类型转换器,也可以通过TypeHandlerRegistry类额register()方法进行注册
部分代码片段如下:
public final class TypeHandlerRegistry { private final Map<JdbcType, TypeHandler<?>> JDBC_TYPE_HANDLER_MAP = new EnumMap<JdbcType, TypeHandler<?>>(JdbcType.class); private final Map<Type, Map<JdbcType, TypeHandler<?>>> TYPE_HANDLER_MAP = new ConcurrentHashMap<Type, Map<JdbcType, TypeHandler<?>>>(); private final TypeHandler<Object> UNKNOWN_TYPE_HANDLER = new UnknownTypeHandler(this); private final Map<Class<?>, TypeHandler<?>> ALL_TYPE_HANDLERS_MAP = new HashMap<Class<?>, TypeHandler<?>>(); private static final Map<JdbcType, TypeHandler<?>> NULL_TYPE_HANDLER_MAP = Collections.emptyMap(); private Class<? extends TypeHandler> defaultEnumTypeHandler = EnumTypeHandler.class; public TypeHandlerRegistry() { register(Boolean.class, new BooleanTypeHandler()); register(boolean.class, new BooleanTypeHandler()); register(JdbcType.BOOLEAN, new BooleanTypeHandler()); register(JdbcType.BIT, new BooleanTypeHandler()); register(Byte.class, new ByteTypeHandler()); register(byte.class, new ByteTypeHandler()); register(JdbcType.TINYINT, new ByteTypeHandler()); register(Short.class, new ShortTypeHandler()); register(short.class, new ShortTypeHandler()); register(JdbcType.SMALLINT, new ShortTypeHandler()); register(Integer.class, new IntegerTypeHandler()); register(int.class, new IntegerTypeHandler()); register(JdbcType.INTEGER, new IntegerTypeHandler()); register(Long.class, new LongTypeHandler()); register(long.class, new LongTypeHandler()); register(Float.class, new FloatTypeHandler()); register(float.class, new FloatTypeHandler()); register(JdbcType.FLOAT, new FloatTypeHandler()); register(Double.class, new DoubleTypeHandler()); register(double.class, new DoubleTypeHandler()); register(JdbcType.DOUBLE, new DoubleTypeHandler()); register(Reader.class, new ClobReaderTypeHandler()); register(String.class, new StringTypeHandler()); register(String.class, JdbcType.CHAR, new StringTypeHandler()); register(String.class, JdbcType.CLOB, new ClobTypeHandler()); register(String.class, JdbcType.VARCHAR, new StringTypeHandler()); register(String.class, JdbcType.LONGVARCHAR, new ClobTypeHandler()); register(String.class, JdbcType.NVARCHAR, new NStringTypeHandler()); register(String.class, JdbcType.NCHAR, new NStringTypeHandler()); register(String.class, JdbcType.NCLOB, new NClobTypeHandler()); register(JdbcType.CHAR, new StringTypeHandler()); register(JdbcType.VARCHAR, new StringTypeHandler()); register(JdbcType.CLOB, new ClobTypeHandler()); register(JdbcType.LONGVARCHAR, new ClobTypeHandler()); register(JdbcType.NVARCHAR, new NStringTypeHandler()); register(JdbcType.NCHAR, new NStringTypeHandler()); register(JdbcType.NCLOB, new NClobTypeHandler()); register(Object.class, JdbcType.ARRAY, new ArrayTypeHandler()); register(JdbcType.ARRAY, new ArrayTypeHandler()); register(BigInteger.class, new BigIntegerTypeHandler()); register(JdbcType.BIGINT, new LongTypeHandler()); register(BigDecimal.class, new BigDecimalTypeHandler()); register(JdbcType.REAL, new BigDecimalTypeHandler()); register(JdbcType.DECIMAL, new BigDecimalTypeHandler()); register(JdbcType.NUMERIC, new BigDecimalTypeHandler()); register(InputStream.class, new BlobInputStreamTypeHandler()); register(Byte[].class, new ByteObjectArrayTypeHandler()); register(Byte[].class, JdbcType.BLOB, new BlobByteObjectArrayTypeHandler()); register(Byte[].class, JdbcType.LONGVARBINARY, new BlobByteObjectArrayTypeHandler()); register(byte[].class, new ByteArrayTypeHandler()); register(byte[].class, JdbcType.BLOB, new BlobTypeHandler()); register(byte[].class, JdbcType.LONGVARBINARY, new BlobTypeHandler()); register(JdbcType.LONGVARBINARY, new BlobTypeHandler()); register(JdbcType.BLOB, new BlobTypeHandler()); register(Object.class, UNKNOWN_TYPE_HANDLER); register(Object.class, JdbcType.OTHER, UNKNOWN_TYPE_HANDLER); register(JdbcType.OTHER, UNKNOWN_TYPE_HANDLER); register(Date.class, new DateTypeHandler()); register(Date.class, JdbcType.DATE, new DateOnlyTypeHandler()); register(Date.class, JdbcType.TIME, new TimeOnlyTypeHandler()); register(JdbcType.TIMESTAMP, new DateTypeHandler()); register(JdbcType.DATE, new DateOnlyTypeHandler()); register(JdbcType.TIME, new TimeOnlyTypeHandler()); register(java.sql.Date.class, new SqlDateTypeHandler()); register(java.sql.Time.class, new SqlTimeTypeHandler()); register(java.sql.Timestamp.class, new SqlTimestampTypeHandler()); register(String.class, JdbcType.SQLXML, new SqlxmlTypeHandler()); // mybatis-typehandlers-jsr310 if (Jdk.dateAndTimeApiExists) { this.register(Instant.class, InstantTypeHandler.class); this.register(LocalDateTime.class, LocalDateTimeTypeHandler.class); this.register(LocalDate.class, LocalDateTypeHandler.class); this.register(LocalTime.class, LocalTimeTypeHandler.class); this.register(OffsetDateTime.class, OffsetDateTimeTypeHandler.class); this.register(OffsetTime.class, OffsetTimeTypeHandler.class); this.register(ZonedDateTime.class, ZonedDateTimeTypeHandler.class); this.register(Month.class, MonthTypeHandler.class); this.register(Year.class, YearTypeHandler.class); this.register(YearMonth.class, YearMonthTypeHandler.class); this.register(JapaneseDate.class, JapaneseDateTypeHandler.class); } // issue #273 register(Character.class, new CharacterTypeHandler()); register(char.class, new CharacterTypeHandler()); }
8.SqlSession
SqlSession是Mybatis提供的面向用户的API,表示和数据库交互时的会话对象。用于完成数据库的增删改查功能。SqlSession是Executor组件的外观。目的是对外提供易于理解和使用的操作数据库的接口