在
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 类型 |
---|---|---|
BooleanTypeHandler | java.lang.Boolean, boolean | 任何兼容的布尔值 |
ByteTypeHandler | java.lang.Byte, byte | 任何兼容的数字或字节类型 |
ShortTypeHandler | java.lang.Short, short | 任何兼容的数字或短整型 |
IntegerTypeHandler | java.lang.Integer, int | 任何兼容的数字和整型 |
LongTypeHandler | java.lang.Long, long | 任何兼容的数字或长整型 |
FloatTypeHandler | java.lang.Float, float | 任何兼容的数字或单精度浮点型 |
DoubleTypeHandler | java.lang.Double, double | 任何兼容的数字或双精度浮点型 |
BigDecimalTypeHandler | java.math.BigDecimal | 任何兼容的数字或十进制小数类型 |
StringTypeHandler | java.lang.String | CHAR 和 VARCHAR 类型 |
ClobTypeHandler | java.lang.String | CLOB 和 LONGVARCHAR 类型 |
NStringTypeHandler | java.lang.String | NVARCHAR 和 NCHAR 类型 |
NClobTypeHandler | java.lang.String | NCLOB 类型 |
ByteArrayTypeHandler | byte[] | 任何兼容的字节流类型 |
BlobTypeHandler | byte[] | BLOB 和 LONGVARBINARY 类型 |
DateTypeHandler | java.util.Date | TIMESTAMP 类型 |
DateOnlyTypeHandler | java.util.Date | DATE 类型 |
TimeOnlyTypeHandler | java.util.Date | TIME 类型 |
SqlTimestampTypeHandler | java.sql.Timestamp | TIMESTAMP 类型 |
SqlDateTypeHandler | java.sql.Date | DATE 类型 |
SqlTimeTypeHandler | java.sql.Time | TIME 类型 |
ObjectTypeHandler | Any | 其他或未指定类型 |
EnumTypeHandler | Enumeration Type | VARCHAR-任何兼容的字符串类型, 作为代码存储(而不是索引) |
EnumOrdinalTypeHandler | Enumeration Type | Any 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的用法和作用。