前一版太过于简单, 这个版本依照源码格式创建config,executor,handler,mappedstatement, 整个项目结构如下:
本项目模拟了MyBatis的几个主要大类: SqlSession, Configuration, Executor, MapperProxy, MappedStatement, ResultSetHandler ,StatementHandler.
下面为项目主要源码:
1. 定义AutoSqlSession
session中包括两大主要对象AutoConfiguration, IExecutor
public class AutoSqlSession {
private AutoConfiguration configuration;
private IExecutor executor;
public AutoSqlSession(AutoConfiguration configuration) throws ClassNotFoundException {
this.configuration = configuration;
this.executor = new BaseExecutor(this);
//生成session时要初始化config配置项
configuration.initMappedStatement();
}
public AutoSqlSession(AutoConfiguration configuration, IExecutor executor) throws ClassNotFoundException {
this.configuration = configuration;
this.executor = executor;
configuration.initMappedStatement();
}
public <T> T GetMapper(Class clazz) {
configuration.addMappedStatement(clazz);
MapperProxy proxy = new MapperProxy(this);
return (T) Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[]{clazz}, proxy);
}
//stateMent 为namespace+id
public <T> T SelectOne(String stateMent, Object parameter) {
return this.executor.doQuery(stateMent, parameter);
}
}
2. 定义配置类configuration
该类包含
List< String> scanClasses,用来模拟存储Mapper接口类, 以便读取Sql注解.
Map< String, MappedStatement> mappedStatementMap, 模拟存储接口方法对应的一席配置项
public class AutoConfiguration {
//用来模拟存储Mapper接口类, 以便读取Sql注解
private List<String> scanClasses = new ArrayList<>();
//key=namespace+方法名,
private Map<String, MappedStatement> mappedStatementMap = new HashMap<>();
//扫描scanClasses中的类, 初始化 mappedStatementMap
public void initMappedStatement() throws ClassNotFoundException {
for (String clazz : scanClasses) {
if (!"".equals(clazz)) {
Class claxx = Class.forName(clazz);
if (claxx != null) {
addMappedStatement(claxx);
}
}
}
}
public void addMappedStatement(Class clazz) {
Method[] methods = clazz.getMethods();
for (Method method : methods) {
try {
if (!method.isBridge()) {
try {
String id = clazz.getName() + "." + method.getName();
if (mappedStatementMap.containsKey(id)) {
return;
}
Annotation selectA = method.getAnnotation(Select.class);
if (selectA == null) {
return;
}
//从注解中提取Sql语句, 并存在MappedStatement对象中.
final String[] strings = (String[]) selectA.getClass().getMethod("value").invoke(selectA);
MappedStatement satement = new MappedStatement(id, strings[0], method.getReturnType());
this.mappedStatementMap.put(id, satement);
} catch (Exception e) {
e.printStackTrace();
}
}
} catch (IncompleteElementException e) {
e.printStackTrace();
}
}
}
public MappedStatement getMappedStatement(String id) {
return (MappedStatement) mappedStatementMap.get(id);
}
}
3. 定义用户Mapper接口
public interface IUserMapper {
@Select("select *from User where id=?")
UserBean selectByPrimaryKey(Integer userId);
@Select("select *from User where name=?")
UserBean selectByName(String StrName);
}
public class UserBean {
private int id;
private String name;
private int age;
private boolean sex;
}
4. 定义MappedStatement
MappedStatement 用来缓存Mapper定义的方法相关的所有配置信息, 如sql, 方法返回类型, 方法参数(本次未模拟)等.
public class MappedStatement {
private String sql;
//存储nameSpace+methdNa
private String id;
private Class<?> returnType;
public MappedStatement(String id, String sql, Class<?> type) {
this.sql = sql;
this.id = id;
this.returnType = type;
}
}
5. 定义MapperProxy
MapperProxy用来生成动态代理
public class MapperProxy implements InvocationHandler {
private AutoSqlSession session;
public MapperProxy(AutoSqlSession session) {
this.session = session;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String strId = method.getDeclaringClass().getName() + '.' + method.getName();
if (session.getConfiguration().getMappedStatement(strId) != null) {
return session.SelectOne(strId,args[0]);
} else if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else {
return null;
}
}
}
6. 生成 IExecutor
IExecutor 实现类用来真正完成SqlSession提交的任务.
public interface IExecutor {
public <T> T doQuery(String stateMent, Object parameter);
}
public class BaseExecutor implements IExecutor {
private AutoSqlSession session;
public BaseExecutor(AutoSqlSession session) {
this.session = session;
}
@Override
public <T> T doQuery(String stateMent, Object parameter) {
MappedStatement mappedStatement = session.getConfiguration().getMappedStatement(stateMent);
AutoSimpleStatementHandler handler = new AutoSimpleStatementHandler(session.getConfiguration());
return (T) handler.query(stateMent, parameter);
}
}
7. 生成 StatementHandler
AutoSimpleStatementHandler 用来模拟实现 Executor提交来的任务
public class AutoSimpleStatementHandler {
private AutoConfiguration configuration;
private AutoResultSetHandler autoResultSetHandler;
public AutoSimpleStatementHandler(AutoConfiguration configuration, AutoResultSetHandler autoResultSetHandler) {
this.configuration = configuration;
this.autoResultSetHandler = autoResultSetHandler;
}
public AutoSimpleStatementHandler(AutoConfiguration configuration) {
this(configuration, new AutoResultSetHandler(configuration));
}
public <E> E query(String strStatement, Object parameter) {
try {
//JDBC
Connection conn = getConnection();
MappedStatement mappedStatement = configuration.getMappedStatement(strStatement);
//TODO ParameterHandler
String strSql = mappedStatement.getSql();
PreparedStatement pstmt = conn.prepareStatement(strSql);
if(parameter.getClass().getName().equals("java.lang.String")){
pstmt.setString(1,parameter.toString());
}
if(parameter.getClass().getName().equals("java.lang.Integer")){
pstmt.setInt(1,Integer.valueOf(parameter.toString()));
}
pstmt.execute();
//ResultSetHandler
return (E) autoResultSetHandler.handle(pstmt.getResultSet(), mappedStatement.getReturnType());
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public Connection getConnection() throws SQLException {
String driver = "com.mysql.jdbc.Driver";
String url = "jdbc:mysql://x.x.x.x:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC";
String username = "root";
String password = "xxx";
Connection conn = null;
try {
Class.forName(driver); //classLoader,加载对应驱动
conn = DriverManager.getConnection(url, username, password);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
return conn;
}
}
8. ResultSetHandler
AutoResultSetHandler 用来模拟对生成的结果进行封装.
public class AutoResultSetHandler {
private final AutoConfiguration configuration;
public AutoResultSetHandler(AutoConfiguration configuration) {
this.configuration = configuration;
}
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){
if("int".equals(type.toString())){
return rs.getInt(field.getName());
}
if(String.class == type){
return rs.getString(field.getName());
}
if("boolean".equals(type.toString())){
return rs.getBoolean(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;
}
}
9. 使用代码测试
public class TestDemo {
public static void main(String[] args) throws ClassNotFoundException {
AutoConfiguration configuration = new AutoConfiguration();
configuration.addScanClass(IUserMapper.class.getName());
AutoSqlSession session = new AutoSqlSession(configuration);
//直接使用Session.selectOne方法测试
UserBean beanByName = session.SelectOne("com.feilong.cn.mybatis.autoV2.mappers.IUserMapper.selectByName", "name1");
System.out.println(beanByName.getName());
//调用Mapper测试
IUserMapper mapper = session.GetMapper(IUserMapper.class);
UserBean bean = mapper.selectByPrimaryKey(1);
System.out.println(bean.getName());
}
}