您的位置:首页 > 数据库

MyBatis主流程分析之(三)-准备SQL语句和参数替换、执行

2016-05-16 13:00 946 查看
MyBatis主流程分析之(二)-打开会话和数据库操作 中我们只讲了一个主流程,没有深入了解mybatis是如何准备sql语句,如何替换参数,最后查询,新增和删除数据的。这里再补充一下,深入了解。

一、JDBC方式新增数据

首先,我们看看JDBC是如何实现的,无论mybatis内部怎么实现,肯定还是调用JDBC的。

这里可以参考JDBC-基础

//sql语句?的地方就是PreparedStatement后面要替换的地方
String sql = "insert into user values(?,?)";
PreparedStatement  pst = conn.prepareStatement(sql);
for(int i = 101;i<200;i++){
//根据位置设置值
pst.setString(1,"Tom" + i);
pst.setString(2,(100+i)*10);
pst.executeUpdate();
}


在这里PreparedStatement准备了SQL语句,根据位置设置了参数,最后调用executeUpdate。那么我们看看MyBatis是如何做到的。

二、mybatis的PreparedStatement的SQL语句准备

这个章节主要解释mybatis如何实现JDBC的PreparedStatement pst = conn.prepareStatement(sql);

在我们创建StatementHandler的实现类时候,它的基类BaseStatementHandler实现了上面的功能,见源代码

BaseStatementHandler类

//代码有删减
public Statement prepare(Connection connection) throws SQLException {
Statement statement = null;
try {
statement = instantiateStatement(connection);//实现的主要地方
return statement;
} catch (SQLException e) {
closeStatement(statement);
throw e;
} catch (Exception e) {
closeStatement(statement);
}
}

protected Statement instantiateStatement(Connection connection) throws SQLException {
//这个boundSql,在我们创建StatementHandler的实现类的时候就已经创建。这是一个比较主要的类,我们在最后将化点时间讲解这个类。
String sql = boundSql.getSql();
if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {
.....
} else if (mappedStatement.getResultSetType() != null) {
return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
} else {
//这里就是JDBC的conn.prepareStatement(sql);
return connection.prepareStatement(sql);
}
}


三、mybatis的PreparedStatement的参数设置

这个章节主要解释了JDBC的 pst.setString(1,”Tom” + i);

在DefaultParameterHandler类中(handler.parameterize(stmt)方法中调用)

public void setParameters(PreparedStatement ps) throws SQLException {
ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
//获取所有参数
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
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)) { // issue #448 ask first for additional params
value = boundSql.getAdditionalParameter(propertyName);
} else if (parameterObject == null) {
value = null;
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
//typeHandlerRegistry注册了某个类的处理
value = parameterObject;
} else {
//默认的MetaObject 的处理,根据参数获取值,这个将和boundSql一起解释
MetaObject metaObject = configuration.newMetaObject(parameterObject);
value = metaObject.getValue(propertyName);
}
//参数列的TypeHandler
TypeHandler typeHandler = parameterMapping.getTypeHandler();
//jdbcType的处理
JdbcType jdbcType = parameterMapping.getJdbcType();
if (value == null && jdbcType == null) jdbcType = configuration.getJdbcTypeForNull();
//见下面的分析,这里实现了 JDBC的 pst.setString(1,"Tom" + i);
typeHandler.setParameter(ps, i + 1, value, jdbcType);
}
}
}
}


类型处理器的作用就是

- 查询时把数据库存储的值转换成java类型

- 修改是把java类型转换成数据库类型存储,处理

- 下面这个表格描述了默认的类型处理器。

类型处理器Java 类型JDBC 类型
BooleanTypeHandlerjava.lang.Boolean, boolean任何兼容的布尔值
ByteTypeHandlerjava.lang.Byte, byte任何兼容的数字或字节类型
ShortTypeHandlerjava.lang.Short, short任何兼容的数字或短整型
IntegerTypeHandlerjava.lang.Integer, int任何兼容的数字和整型
LongTypeHandlerjava.lang.Long, long任何兼容的数字或长整型
FloatTypeHandlerjava.lang.Float, float任何兼容的数字或单精度浮点型
DoubleTypeHandlerjava.lang.Double, double任何兼容的数字或双精度浮点型
BigDecimalTypeHandlerjava.math.BigDecimal任何兼容的数字或十进制小数类型
StringTypeHandlerjava.lang.StringCHAR 和 VARCHAR 类型
ClobTypeHandlerjava.lang.StringCLOB 和 LONGVARCHAR 类型
NStringTypeHandlerjava.lang.StringNVARCHAR 和 NCHAR 类型
NClobTypeHandlerjava.lang.StringNCLOB 类型
ByteArrayTypeHandlerbyte[]任何兼容的字节流类型
BlobTypeHandlerbyte[]BLOB 和 LONGVARBINARY 类型
DateTypeHandlerjava.util.DateTIMESTAMP 类型
DateOnlyTypeHandlerjava.util.DateDATE 类型
TimeOnlyTypeHandlerjava.util.DateTIME 类型
SqlTimestampTypeHandlerjava.sql.TimestampTIMESTAMP 类型
SqlDateTypeHandlerjava.sql.DateDATE 类型
SqlTimeTypeHandlerjava.sql.TimeTIME 类型
ObjectTypeHandlerAny其他或未指定类型
EnumTypeHandlerEnumeration TypeVARCHAR-任何兼容的字符串类型, 作为代码存储(而不是索引)
EnumOrdinalTypeHandlerEnumeration TypeAny compatible NUMERIC or DOUBLE, as the position is stored (not the code itself).
-

TypeHandle的接口



BaseTypeHandler类

public void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {
if (parameter == null) {
if (jdbcType == null) {
throw new TypeException("JDBC requires that the JdbcType must be specified for all nullable parameters.");
}
try {
ps.setNull(i, jdbcType.TYPE_CODE);
} catch (SQLException e) {
throw new TypeException("Error setting null for parameter #" + i + " with JdbcType " + jdbcType + " . " +
"Try setting a different JdbcType for this parameter or a different jdbcTypeForNull configuration property. " +
"Cause: " + e, e);
}
} else {
//调用实现类
setNonNullParameter(ps, i, parameter, jdbcType);
}
}


其中StringTypeHandler的实现

//设置第i的值
@Override
public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType)
throws SQLException {
ps.setString(i, parameter);
}

//根据columnName获取值
@Override
public String getNullableResult(ResultSet rs, String columnName)
throws SQLException {
return rs.getString(columnName);
}
//根据columnIndex获取值
@Override
public String getNullableResult(ResultSet rs, int columnIndex)
throws SQLException {
return rs.getString(columnIndex);
}
//返回的是CallableStatement 获取值
@Override
public String getNullableResult(CallableStatement cs, int columnIndex)
throws SQLException {
return cs.getString(columnIndex);
}


四、mybatis的PreparedStatement的executeUpdate

这里解释了mybatis如何实现JDBC的pst.executeUpdate();

在PreparedStatementHandler的实现类中

public int update(Statement statement) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
//执行语句
ps.execute();
//获取影响的函数
int rows = ps.getUpdateCount();
Object parameterObject = boundSql.getParameterObject();
KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
keyGenerator.processAfter(executor, mappedStatement, ps, parameterObject);
return rows;
}


五、BoundSql类

BoundSql的作用

1. 存储我们的sql语

2. 存储传入参数对象

3. 利用MetaObjec对象获取或设置2(存储传入参数对象)中的值



public BoundSql(Configuration configuration, String sql, List<ParameterMapping> parameterMappings, Object parameterObject) {
this.sql = sql;
this.parameterMappings = parameterMappings;
this.parameterObject = parameterObject;
this.additionalParameters = new HashMap<String, Object>();
this.metaParameters = configuration.newMetaObject(additionalParameters);
}


主要属性

- sql

update mybatis.user set UserName=?,
UserEmail=?
where userId= ?


parameterMappings

parameterMappings内的parameterMapping分别为UserName,UserEmail和userId对象。和参数顺序一直。



parameterObject

例如一个pojo对象,User对象类。

additionalParameters

空new HashMap

metaParameters

MetaObjec的一个变量,见下文。

六、MetaObject类

先看看 MetaObject、ObjectWrapper和ObjectFactory、ObjectWrapperFactory的关系。



MetaObject的属性及其方法

private Object originalObject;//例如一个pojo对象,例如User对象类。
private ObjectWrapper objectWrapper;//根据参数对象的不同,在构造函数中配置
private ObjectFactory objectFactory;//Configuration中配置
private ObjectWrapperFactory objectWrapperFactory;//Configuration中配置

//构造函数,私有
private MetaObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory) {
this.originalObject = object;
this.objectFactory = objectFactory;
this.objectWrapperFactory = objectWrapperFactory;

if (object instanceof ObjectWrapper) {
//如果参数对象实现了ObjectWrapper
this.objectWrapper = (ObjectWrapper) object;
} else if (objectWrapperFactory.hasWrapperFor(object)) {
//如果objectWrapperFactory已经包装了对象,对用objectWrapperFactory的getWrapperFor
this.objectWrapper = objectWrapperFactory.getWrapperFor(this, object);
} else if (object instanceof Map) {
//是一个Map对象,使用mybatis的MapWrapper
this.objectWrapper = new MapWrapper(this, (Map) object);
} else if (object instanceof Collection) {
//是一个CollectionWrapper对象
this.objectWrapper = new CollectionWrapper(this, (Collection) object);
} else {
//其他默认使用BeanWrapper
this.objectWrapper = new BeanWrapper(this, object);
}
}

//对外公开的静态方法
public static MetaObject forObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory) {
if (object == null) {
return SystemMetaObject.NULL_META_OBJECT;
} else {
return new MetaObject(object, objectFactory, objectWrapperFactory);
}
}


主要的方法



通过这个MetaObject对象可以很方便获取或设置originalObject对象(传入对象参数)的值。

很有趣的是mybatis获取数据配合也调用了这个类,包装的对象是PooledDataSource。

这里解释了MyBatis主流程分析之(一)-环境准备中的ObjectFactory、ObjectWrapperFactory的用法和作用。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: