ibatis版本号:
2.3.0
Build Date: 2006/11/30 17:16
Build Number: 677
ibatis的技术是从xml里面字符串转换成JAVA对象,对象填充JDBC的statement查询,然后从resultset取对象返回,另外利用ThreadLocal实现线程安全,JDBC保证了事务控制,cache(三方库)实现缓存的dao框架。
各大包结构和作用:
1,accessplan—
2,builder.xml
3,cache
4,datasource
5,exchange—ResultMap(sql结果类型结构)和ParameterMap(sql条件类型结果)与值的相互转换
6,execution
7,impl
8,mapping
9,scop
10,transaction
11,type—jdbc的Statement和ResultSet 与 java.lang.Object对象的相互转换。
Accessplan
uml:
Accessplan对外只提供个Factory,这种“封闭”设计可以借鉴:
对外接口调用如下:
parameterPlan = AccessPlanFactory.getAccessPlan(parameterMap.getParameterClass(), parameterPropNames);
其中parameterMap.getParameterClass(),是映射的CLASS,就是XML里parameterXXX里的类,后面那个是类的成员变量,ParameterMapping是映射元素类,如下:
// 从某个映射对象中取出所有元素
ParameterMapping[] parameterMappings = parameterMap.getParameterMappings();
String[] parameterPropNames = new String[parameterMappings.length];
for (int i = 0; i < parameterPropNames.length; i++) {
// 从元素中取出被映射对象的成员名
parameterPropNames[i] = parameterMappings[i].getPropertyName();
}
UML:
ResultGetter和ParameterSetter的设计看来是为了TypeHandlerCallback扩展的复杂数据类型所用。为什么需要在这中间加一层呢? 可能是因为数据的复杂性吧,把特例和一般分离出来,代码看上去似乎优雅些。继续深入。
元数据接口
TypeHandler接口的抽象意义——Interface for getting data into, and out of a mapped statement,主要的作用是把Object那些对象set到jdbc的statement,以及从resultset结果集中获取那些Object对象。
/**
* Interface for getting data into, and out of a mapped statement
*/
public interface TypeHandler {
// para向第i个位置填充ps.
public void setParameter(PreparedStatement ps, int i, Object parameter, String jdbcType)
throws SQLException;
// 根据rs结果集某字段名取值
public Object getResult(ResultSet rs, String columnName)
throws SQLException;
public Object getResult(ResultSet rs, int columnIndex)
throws SQLException;
/**
* Converts the String to the type that this handler deals with
*/
public Object valueOf(String s);
public boolean equals(Object object, String string);
}
TypeHandlerCallback接口抽象意义为:
A simple interface for implementing custom type handlers.
Using this interface, you can implement a type handler that
will perform customized processing before parameters are set
on a PreparedStatement and after values are retrieved from
a ResultSet.
public interface TypeHandlerCallback {
public void setParameter(ParameterSetter setter, Object parameter) // 同上
throws SQLException;
public Object getResult(ResultGetter getter) // 同上
throws SQLException;
public Object valueOf(String s);
}
StringTypeHandler——String类型帮助类
public class StringTypeHandler extends BaseTypeHandler implements TypeHandler {
public void setParameter(PreparedStatement ps, int i, Object parameter, String jdbcType)
throws SQLException {
ps.setString(i, ((String) parameter));
}
public Object getResult(ResultSet rs, String columnName)
throws SQLException {
Object s = rs.getString(columnName); // 很熟悉的jdbc编程吧
if (rs.wasNull()) {
return null;
} else {
return s;
}
}
public Object getResult(ResultSet rs, int columnIndex)
throws SQLException {
Object s = rs.getString(columnIndex);
if (rs.wasNull()) {
return null;
} else {
return s;
}
}
}
最后“元数据”这块剩下最后一个Factory管理类:TypeHandlerFactory
/**
* Not much of a suprise, this is a factory class for TypeHandler objects.
*/
public class TypeHandlerFactory {
private final Map typeHandlerMap = new HashMap(); // 用final Map来存储类型转换的帮助类
private final TypeHandler unknownTypeHandler = new UnknownTypeHandler(this);
private final HashMap typeAliases = new HashMap();// 保存type助记符,为什么呢?
/**
* Default constructor
*/
public TypeHandlerFactory() {
TypeHandler handler;
handler = new BooleanTypeHandler();
register(Boolean.class, handler);// 实际上把handler放入布尔值的map,然后再放入typeMap里。
register(boolean.class, handler);
handler = new ByteTypeHandler();
register(Byte.class, handler);
register(byte.class, handler);
register(String.class, new StringTypeHandler());
register(String.class, "CLOB", new CustomTypeHandler(new ClobTypeHandlerCallback()));
register(String.class, "LONGVARCHAR", new CustomTypeHandler(new ClobTypeHandlerCallback()));
register(byte[].class, new ByteArrayTypeHandler());
register(byte[].class, "BLOB", new CustomTypeHandler(new BlobTypeHandlerCallback()));
register(byte[].class, "LONGVARBINARY", new CustomTypeHandler(new BlobTypeHandlerCallback()));
....
putTypeAlias("string", String.class.getName());
putTypeAlias("byte", Byte.class.getName());
putTypeAlias("long", Long.class.getName());
....
}
/* Public Methods */
public TypeHandler getTypeHandler(Class type, String jdbcType) {
Map jdbcHandlerMap = (Map) typeHandlerMap.get(type);
TypeHandler handler = null;
if (jdbcHandlerMap != null) {
handler = (TypeHandler) jdbcHandlerMap.get(jdbcType);
if (handler == null) {
handler = (TypeHandler) jdbcHandlerMap.get(null);
}
}
return handler;
}
/**
* When in doubt, get the "unknown" type handler
*
* @return - if I told you, it would not be unknown, would it?
*/
public TypeHandler getUnkownTypeHandler() {
return unknownTypeHandler;
}
/**
* Tells you if a particular class has a TypeHandler
*
* @param type - the class
*
* @return - true if there is a TypeHandler
*/
public boolean hasTypeHandler(Class type) {
return getTypeHandler(type) != null;
}
/**
* Register (add) a type handler for a class and JDBC type
*
* @param type - the class
* @param jdbcType - the JDBC type
* @param handler - the handler instance
*/
public void register(Class type, String jdbcType, TypeHandler handler) {
Map map = (Map) typeHandlerMap.get(type);
if (map == null) {
map = new HashMap();
typeHandlerMap.put(type, map);
}
map.put(jdbcType, handler);
}
/**
* Lookup an aliased class and return it's REAL name
*
* @param string - the alias
*
* @return - the REAL name
*/
public String resolveAlias(String string) {
String key = null;
if(string != null)
key = string.toLowerCase();
String value = null;
if (typeAliases.containsKey(key)) {
value = (String) typeAliases.get(key);
} else {
value = string;
}
return value;
}
/**
* Adds a type alias that is case insensitive. All of the following String, string, StRiNg will equate to the same alias.
* @param alias - the alias
* @param value - the real class name
*/
public void putTypeAlias(String alias, String value) {
String key = null;
if(alias != null)
key = alias.toLowerCase();
if (typeAliases.containsKey(key) && !typeAliases.get(key).equals(value)) {
throw new SqlMapException("Error in XmlSqlMapClientBuilder. Alias name conflict occurred. The alias '" + key + "' is already mapped to the value '" + typeAliases.get(alias) + "'.");
}
typeAliases.put(key, value);
}
}
Mapping包
--parameter包意义——主要负责数据类型转换,把xml写的字符串映射成正确的类型,以上type包是解决了object到type的转换。
UML:
ParameterMap接口
public interface ParameterMap {
public String getId();
public void setParameters(RequestScope request, PreparedStatement ps, Object[] parameters)
throws SQLException;
public Object[] getParameterObjectValues(RequestScope request, Object parameterObject);
public CacheKey getCacheKey(RequestScope request, Object parameterObject);
public void refreshParameterObjectValues(RequestScope request, Object parameterObject, Object[] values);
public ParameterMapping[] getParameterMappings();
public Class getParameterClass();
}
ParameterMapping接口
public interface ParameterMapping {
public String getPropertyName();
public boolean isOutputAllowed();
}
BasicParameterMapping实现类:
public class BasicParameterMapping implements ParameterMapping {
private static final String MODE_INOUT = "INOUT";
private static final String MODE_OUT = "OUT";
private static final String MODE_IN = "IN";
private String propertyName; // 从XML文件里读取需要转换的类型名
private TypeHandler typeHandler; // 对象转换相应类型的工具map
private String typeName; // this is used for REF types or user-defined types
private int jdbcType;
private String jdbcTypeName;
private String nullValue;
private String mode;
private boolean inputAllowed;
private boolean outputAllowed;
private Class javaType; // 需要转换的类型class
private String resultMapName; // 结果map名称
private Integer numericScale;
private String errorString;
public BasicParameterMapping() {
mode = "IN";
inputAllowed = true;
outputAllowed = false;
}
public void setJavaTypeName(String javaTypeName) {
try {
if (javaTypeName == null) {
this.javaType = null;
} else {// 通过getClassLoader().loadClass(className);来获得实例
this.javaType = Resources.classForName(javaTypeName);
}
} catch (ClassNotFoundException e) {
throw new SqlMapException("Error setting javaType property of ParameterMap. Cause: " + e, e);
}
}
}
BasicParameterMap实现类
public class BasicParameterMap implements ParameterMap {
private String id;
private Class parameterClass;
private ParameterMapping[] parameterMappings;
private DataExchange dataExchange;
private String resource;
private Map parameterMappingIndex = new HashMap();
private SqlMapExecutorDelegate delegate;
public void setParameterMappingList(List parameterMappingList) {
this.parameterMappings = (BasicParameterMapping[]) parameterMappingList.toArray(new BasicParameterMapping[parameterMappingList.size()]);
for (int i = 0; i < parameterMappings.length; i++) {
parameterMappingIndex.put(parameterMappings[i].getPropertyName(), new Integer(i));
}
Map props = new HashMap();
props.put("map", this);
dataExchange = delegate.getDataExchangeFactory().getDataExchangeForClass(parameterClass);
dataExchange.initialize(props);
}
/**
* @param ps
* @param parameters
* @throws java.sql.SQLException
*/
public void setParameters(RequestScope request, PreparedStatement ps, Object[] parameters)
throws SQLException {
ErrorContext errorContext = request.getErrorContext();
errorContext.setActivity("applying a parameter map");
errorContext.setObjectId(this.getId());
errorContext.setResource(this.getResource());
errorContext.setMoreInfo("Check the parameter map.");
if (parameterMappings != null) {
for (int i = 0; i < parameterMappings.length; i++) {
BasicParameterMapping mapping = (BasicParameterMapping) parameterMappings[i];
errorContext.setMoreInfo(mapping.getErrorString());
if (mapping.isInputAllowed()) {
setParameter(ps, mapping, parameters, i);
}
}
}
}
public Object[] getParameterObjectValues(RequestScope request, Object parameterObject) {
return dataExchange.getData(request, this, parameterObject);
}
public CacheKey getCacheKey(RequestScope request, Object parameterObject) {
return dataExchange.getCacheKey(request, this, parameterObject);
}
public void refreshParameterObjectValues(RequestScope request, Object parameterObject, Object[] values) {
dataExchange.setData(request, this, parameterObject, values);
}
protected void setParameter(PreparedStatement ps, BasicParameterMapping mapping, Object[] parameters, int i) throws SQLException {
Object value = parameters[i];
// Apply Null Value
String nullValueString = mapping.getNullValue();
if (nullValueString != null) {
TypeHandler handler = mapping.getTypeHandler();
if (handler.equals(value, nullValueString)) {
value = null;
}
}
// Set Parameter
TypeHandler typeHandler = mapping.getTypeHandler();
if (value != null) {
typeHandler.setParameter(ps, i + 1, value, mapping.getJdbcTypeName());
} else if (typeHandler instanceof CustomTypeHandler) {
typeHandler.setParameter(ps, i + 1, value, mapping.getJdbcTypeName());
} else {
int jdbcType = mapping.getJdbcType();
if (jdbcType != JdbcTypeRegistry.UNKNOWN_TYPE) {
ps.setNull(i + 1, jdbcType);
} else {
ps.setNull(i + 1, Types.OTHER);
}
}
}
}