上一节,我们debug了mybatis的orm执行过程,这一节我们将核心实现手写成一个demo,暂且称之为MybatisV1.0
上一节分析了通过使用mybatis操作数据库的核心流程。
我们画一个图:
1.0 定义基本功能,定义scope
实际上三个核心类即可:
TestMapper --> TestMapper.xml
本质功能就是创建sqlsession会话,找到sql。
经过以上的分析,我们不难得出结论,mybatis通过sqlsession,以及configuration和Executor作为核心实现完成对于数据库的orm操作。
我们在sqlsession这里处理两件事情:
1. sqlSession.getMapper
解析Mapper.xml,获取namespace,methodName以及sql语句!
并将sql语句以map的形式存储
将当前的要执行的Mapper.class与namespace配对
成功则从map中通过methodName拿到sql!
2. sqlSession.selectList(selectOne)
委派模式,sqlSession.selectOne通过Executor.query()真正执行sql
模板模式,jdbc数据通过类加载器获取用户名密码数据库连接以及sql语句和参数
现在我们先做一个sqlsession类:
public class GPSqlSession {
private GPConfiguration configuration;
private GPExecutor executor;
public GPSqlSession(GPConfiguration configuration, GPExecutor executor) {
this.configuration = configuration;
this.executor = executor;
}
/**
*
* @param clazz 被代理的Mapper
* @param
* @return
*/
public <T> T getMapper(Class<T> clazz){
return configuration.getMapper(clazz,this);
}
/**
*
* @param statement sql 语句
* @param parameter sql 参数
* @param
* @return
*/
public <T> T selectOne(String statement,String parameter){
return executor.query(statement,parameter);
}
}
configuration类:
public class GPConfiguration {
public <T> T getMapper(Class<T> clazz,GPSqlSession sqlSession) {
return (T) Proxy.newProxyInstance(this.getClass().getClassLoader(),
new Class[]{clazz},
new GPMapperProxy(sqlSession));
}
/**
* 1、xml解析
* 2、赋值
*/
static class TestMapperXml{
public static final String namespace= "com.xxx.mybatis.v1.TestMapper";
public static final Map<String,String> methodSqlMapping = new HashMap<>();
static {
methodSqlMapping.put("selectByPrimaryKey", "select * from test where id = %d");
}
}
}
Executor:
public interface GPExecutor {
/**
* query
* @param statement sql 语句
* @param parameter sql 参数
* @param
* @return
*/
<T> T query(String statement, String parameter);
}
ExecutorImpl:
public class GPSimpleExecutor implements GPExecutor{
@Override
public <T> T query(String statement, String parameter) {
Connection connection = null;
PreparedStatement preparedStatement = null;
Test test = null;
try {
Class.forName("com.mysql.jdbc.Driver");
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/gp?useUnicode=true&characterEncoding=utf-8&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC", "root", "123456");
preparedStatement = connection.prepareStatement(String.format(statement,Integer.parseInt(parameter)));
ResultSet rs = preparedStatement.executeQuery();
while (rs.next()) {
test = new Test();
test.setId(rs.getInt(1));
test.setNums(rs.getInt(2));
test.setName(rs.getString(3));
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if (null != connection) {
connection.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
return (T) test;
}
}
MapperProxy:
public class GPMapperProxy implements InvocationHandler {
private GPSqlSession sqlSession;
public GPMapperProxy(GPSqlSession sqlSession) {
this.sqlSession = sqlSession;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getDeclaringClass().getName().equals(GPConfiguration.TestMapperXml.namespace)){
String sql = GPConfiguration.TestMapperXml.methodSqlMapping.get(method.getName());
return sqlSession.selectOne(sql,String.valueOf(args[0]));
}
return null;
}
}
我们来测试一下:
public static void main(String[] args) {
GPSqlSession gpSqlSession = new GPSqlSession(new GPConfiguration(), new GPSimpleExecutor());
TestMapper mapper = gpSqlSession.getMapper(TestMapper.class);
Test test = mapper.selectByPrimaryKey(1);
System.out.println(test);
}
源码地址:
mybatisV1.0手写版源码地址(Demo在文件夹V1里,sql脚本在resources/sql/下)