Mybatis输入输出映射及动态SQL Review
Mybatis是一个基于Java的持久化框架,支持定制化SQL、存储过程以及高级映射。在Mybatis中,输入输出映射是指将Java对象与SQL语句的参数或结果集进行转换的机制,而动态SQL则可根据需要构建不同的SQL语句。
输入输出映射
输入输出映射主要涉及Mybatis中的ParameterHandler和ResultSetHandler。ParameterHandler将Java对象转换为SQL参数,而ResultSetHandler则将SQL查询结果转换为Java对象。
ParameterHandler
ParameterHandler位于org.apache.ibatis.executor.parameter包中,它是实现参数设置的接口。Mybatis提供了DefaultParameterHandler和MapParameterHandler两种默认的实现,如果需要自定义ParameterHandler,则需要实现ParameterHandler接口。下面以DefaultParameterHandler为例进行说明:
public class DefaultParameterHandler implements ParameterHandler {
private TypeHandlerRegistry typeHandlerRegistry;
...
@Override
public void setParameters(PreparedStatement ps) throws SQLException {
Object[] args = parameterObject == null ? new Object[0] : (Object[]) parameterObject;
for (int i = 0; i < args.length; i++) {
TypeHandler typeHandler = typeHandlerRegistry.getTypeHandler(args[i].getClass());
typeHandler.setParameter(ps, i + 1, args[i], parameterMapping.getJdbcType());
}
}
}
DefaultParameterHandler将Java对象数组中的每个对象,使用TypeHandler转换成对应的SQL参数,并通过PreparedStatement进行设置。setParameters方法即为由Mybatis负责调用的接口,其中包含PreparedStatement对象,因此可以在自定义ParameterHandler中使用setParameters方法进行参数设置。
ResultSetHandler
ResultSetHandler位于org.apache.ibatis.executor.resultset包中,是ResultMap映射实现的核心。Mybatis提供了DefaultResultSetHandler、FastResultSetHandler、MapResultSetHandler和XMLResultSetHandler等默认的实现,如果需要自定义ResultSetHandler,则需要实现ResultSetHandler接口。下面以DefaultResultSetHandler为例进行说明:
public class DefaultResultSetHandler implements ResultSetHandler {
private final Executor executor;
private final Configuration configuration;
private final MappedStatement mappedStatement;
private final RowBounds rowBounds;
private final ResultHandler resultHandler;
private final BoundSql boundSql;
private final TypeHandlerRegistry typeHandlerRegistry;
...
@Override
public <E> List<E> handleResultSets(Statement stmt) throws SQLException {
ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
final List<E> list = new ArrayList<>();
ResultSetWrapper rsw = getResultSetWrapper(stmt);
try {
handleResultSet(rsw, list);
} finally {
closeResultSet(rsw.getResultSet());
}
return list;
}
private ResultSetWrapper getResultSetWrapper(Statement stmt) throws SQLException {
ResultSet rs = stmt.getResultSet();
while (rs == null) {
if (stmt.getMoreResults()) {
rs = stmt.getResultSet();
} else {
if (stmt.getUpdateCount() == -1) {
break;
}
}
}
return rs != null ? new ResultSetWrapper(rs, configuration) : null;
}
private void handleResultSet(ResultSetWrapper rsw, List<Object> list) throws SQLException {
if (rsw == null) {
return;
}
try {
// 将ResultSet结果集使用TypeHandler转换成Java对象
final ResultSetMetaData metaData = rsw.getMetaData();
final TypeHandler<Object> typeHandler = typeHandlerRegistry.getTypeHandler(Object.class);
while (rsw.getResultSet().next()) {
// 创建映射的结果集对象
final Object resultObject = createResultObject(rsw, mappedStatement.getResultMap());
if (resultObject != null && !typeHandlerRegistry.hasTypeHandler(resultObject.getClass())) {
// 查找该结果集对象对应的TypeHandler
final MetaObject metaObject = configuration.newMetaObject(resultObject);
final List<ResultMapping> unmappedResultList = new ArrayList<ResultMapping>();
final List<String> mappedColumnNames = rsw.getMappedColumnNames(resultMap, null);
for (ResultMapping resultMapping : resultMap.getResultMappings()) {
// 根据列名和Java属性名等信息,查找对应的TypeHandler进行对象转换
String column = resultMapping.getColumn();
// ResultMapping对应的Java属性名称
String property = resultMapping.getProperty();
TypeHandler<?> typeHandlerImpl = null;
if (resultMapping.getNestedResultMapId() != null) {
// 这里没有进行更深一层次的映射处理
typeHandlerImpl = new NestedResultSetHandler(resultMapping.getNestedResultMap(), rsw, rowBounds, resultHandler, typeHandlerRegistry, mappedColumnNames, configuration).getTypeHandler(property);
} else if (resultMapping.getResultSet() != null) {
// 这里没有进行更深一层次的映射处理
typeHandlerImpl = new ResultSetTypeHandler(resultMapping.getResultSet(), rsw, rowBounds, resultHandler, typeHandlerRegistry, mappedColumnNames, configuration).getTypeHandler(property);
} else if (column != null && !"".equals(column.trim())) {
JdbcType jdbcType = resultMapping.getJdbcType();
if (jdbcType == null) {
jdbcType = configuration.getJdbcTypeForNull();
}
// 根据列名和Java属性名等信息,查找对应的TypeHandler进行对象转换
typeHandlerImpl = rsw.getTypeHandler(jdbcType, column);
} else {
typeHandlerImpl = typeHandlerRegistry.getTypeHandler(resultMapping.getJavaType());
}
if (typeHandlerImpl == null) {
// 如果没有对应的TypeHandler,则将结果映射设置为null
unmappedResultList.add(resultMapping);
} else {
// 进行结果集对象到Java对象的转换
Object value = typeHandlerImpl.getResult(rsw.getResultSet(), property);
// 判断目标转换类型是否是Java基本类型,如果是,则将结果集中null值转换为默认值
if (value == null && configuration.isCallSettersOnNulls()) {
Class<?> javaType = resultMapping.getJavaType();
if (javaType == int.class || javaType == short.class || javaType == long.class || javaType == byte.class) {
value = 0;
} else if (javaType == float.class || javaType == double.class) {
value = 0.0;
} else if (javaType == boolean.class) {
value = false;
}
}
metaObject.setValue(property, value);
}
}
if (unmappedResultList.size() > 0) {
// 将未映射的结果集设置为null
handleUnmappedProperties(metaObject, unmappedResultList, rsw.getResultSet());
}
list.add(resultObject);
} else {
Object value = typeHandler.getResult(rsw.getResultSet(), 1);
list.add(value);
}
}
} finally {
// 将ResultSet关闭
closeResultSet(rsw.getResultSet());
}
}
}
DefaultResultSetHandler中的handleResultSets方法是Mybatis使用ResultSet构建Java对象的核心方法。handleResultSets方法包含ResultSetResultSetWrapper和RowBounds等参数,通过调用createResultObject、getTypeHandler、getResult等方法,将ResultSet结果集中的每一行数据映射为Java对象,最终返回Java对象的List集合。
动态SQL
动态SQL指的是运行时根据条件构建SQL语句,主要包括if、choose、when、otherwise、foreach、bind等元素。Mybatis支持通过XML方式或注解方式实现动态SQL。
动态SQL示例1:if元素
if元素可以根据条件进行SQL语句的拼接,例如根据搜索条件进行模糊查询。下面是一个根据用户名模糊查询用户列表的示例代码:
<select id="selectUsersByNameLike" resultMap="UserResultMap">
SELECT * FROM users
<where>
<if test="name != null and name != ''">
AND name LIKE CONCAT('%', #{name}, '%')
</if>
</where>
ORDER BY id
</select>
在XML配置文件中,使用if元素进行参数判断,根据判断结果来拼接SQL语句。
动态SQL示例2:foreach元素
foreach元素可以进行迭代集合,对集合中的元素进行操作。下面是一个根据ID列表批量查询用户信息的示例代码:
<select id="selectUsersByIdList" resultMap="UserResultMap">
SELECT * FROM users
WHERE id IN
<foreach item="item" index="index" collection="idList" open="(" separator="," close=")">
#{item}
</foreach>
</select>
在XML配置文件中,使用foreach元素遍历idList列表,将每个元素拼接到SQL语句中,并执行查询。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Mybatis输入输出映射及动态SQL Review - Python技术站