Mybatis源码分析05-MyBatisV2.0设计过程
前言
上节我们手写MyBatisV1.0,虽然可以简单的实现了ORM,但是存在的问题很多:
- 硬编码
- Executor耦合度过高
- 提示不友好
针对于以上三点,我们来一次改造
MyBatisV2.0实现
- 硬编码
通过ExecutorFactory改造Executor的多种实现
ExecutorFactory:
public class ExecutorFactory {
private static final String SIMPLE = "SIMPLE";
private static final String CACHING = "CACHING";
public static Executor DEFAULT(GpConfiguration configuration) {
return get(SIMPLE, configuration);
}
public static Executor get(String key, GpConfiguration configuration) {
if (SIMPLE.equalsIgnoreCase(key)) {
return new SimpleExecutor(configuration);
}
if (CACHING.equalsIgnoreCase(key)) {
return new CachingExecutor(new SimpleExecutor(configuration));
}
throw new RuntimeException("no executor found");
}
public enum ExecutorType {
/**
* 普通
*/
SIMPLE,
/**
* 缓存
*/
CACHING
}
SimpleExecutor :
public class SimpleExecutor implements Executor {
private GpConfiguration configuration;
public SimpleExecutor(GpConfiguration configuration) {
this.configuration = configuration;
}
public GpConfiguration getConfiguration() {
return configuration;
}
public void setConfiguration(GpConfiguration configuration) {
this.configuration = configuration;
}
@Override
public <E> E query(MapperRegistory.MapperData mapperData, Object parameter)
throws Exception {
//初始化StatementHandler --> ParameterHandler --> ResultSetHandler
StatementHandler handler = new StatementHandler(configuration);
return (E) handler.query(mapperData, parameter);
}
}
CachingExecutor:
public class CachingExecutor implements Executor {
private GpConfiguration configuration;
private SimpleExecutor delegate;
private Map<String,Object> localCache = new HashMap();
public CachingExecutor(SimpleExecutor delegate) {
this.delegate = delegate;
}
public CachingExecutor(GpConfiguration configuration) {
this.configuration = configuration;
}
@Override
public <E> E query(MapperRegistory.MapperData mapperData, Object parameter)
throws Exception {
//初始化StatementHandler --> ParameterHandler --> ResultSetHandler
StatementHandler handler = new StatementHandler(configuration);
Object result = localCache.get(mapperData.getSql());
if( null != result){
System.out.println("缓存命中");
return (E)result;
}
result = (E) delegate.query(mapperData,parameter);
localCache.put(mapperData.getSql(),result);
return (E)result;
}
}
- 提示不友好
@Data
public class GpConfiguration {
private String scanPath;
private MapperRegistory mapperRegistory = new MapperRegistory();
public GpConfiguration scanPath(String scanPath) {
this.scanPath = scanPath;
return this;
}
public void build() throws IOException {
if (null == scanPath || scanPath.length() < 1) {
throw new RuntimeException("scan path is required .");
}
}
public static void main(String[] args) throws IOException {
new GpConfiguration().scanPath("com/xxx/mybatis/gp/config/mappers").build();
}
}
- Executor改造
Executor主要的功能是query
V1.0我们将jdbc过程全部耦合在一起,这次我们仿照mybatis源码将其分离
- 执行拿到结果 StatementHandler
public <E> E query(MapperRegistory.MapperData mapperData, Object parameter) throws Exception {
try {
//JDBC
Connection conn = getConnection();
//TODO ParamenterHandler
PreparedStatement pstmt = conn.prepareStatement(String.format(mapperData.getSql(), Integer.parseInt(String.valueOf(parameter))));
pstmt.execute();
//ResultSetHandler
return (E)resultSetHandler.handle(pstmt.getResultSet(),mapperData.getType());
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
public Connection getConnection() throws SQLException {
String driver = "com.mysql.jdbc.Driver";
String url = "jdbc:mysql://localhost:3306/gp?useUnicode=true&characterEncoding=utf-8&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC";
String username = "root";
String password = "123456";
Connection conn = null;
try {
//classLoader,加载对应驱动
Class.forName(driver);
conn = DriverManager.getConnection(url, username, password);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
return conn;
}
- 结果映射 ResultSetHandler
public <E> E handle(ResultSet rs,Class type) throws SQLException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Object resultObj = new DefaultObjectFactory().create(type);
if (rs.next()) {
int i = 0;
for (Field field : resultObj.getClass().getDeclaredFields()) {
setValue(resultObj, field, rs ,i);
}
}
return (E) resultObj;
}
private void setValue(Object resultObj, Field field, ResultSet rs, int i) throws NoSuchMethodException, SQLException, InvocationTargetException, IllegalAccessException {
Method setMethod = resultObj.getClass().getMethod("set" + upperCapital(field.getName()), field.getType());
setMethod.invoke(resultObj, getResult(field,rs));
}
private Object getResult(Field field, ResultSet rs) throws SQLException {
//TODO type handles
//bean属性的名字必须要和数据库column的名字一样
Class<?> type = field.getType();
if(Integer.class == type){
return rs.getInt(field.getName());
}
if(String.class == type){
return rs.getString(field.getName());
}
return rs.getString(field.getName());
}
private String upperCapital(String name) {
String first = name.substring(0, 1);
String tail = name.substring(1);
return first.toUpperCase() + tail;
}
后记
完整代码github地址:MyBatisV2.0(在/gp/文件夹下)