本文主要记录了基于整个Mybatis的使用以及源码分析篇总结,为了更加清晰的看懂Mybatis原理,简化源码手写实现一个简单的Mybatis版本
1. 项目依赖以及架构
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.18</version>
</dependency>
- 目录
2.自动装配类 MapperAutoConfig
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
doScanner("com.example.demo", TMapper.class, this.configuration.getMapperInterfaceList());
doScanner("com.example.demo", Intercepts.class, this.configuration.getPluGinInterfaceList());
doScannerStatement("application.yml");
doRegister(registry);
}
private void doRegister(BeanDefinitionRegistry registry) {
for (Object object : this.configuration.getMapperInterfaceList()) {
Class<?> clazz = (Class<?>) object;
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(clazz);
AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
beanDefinition.setBeanClass(MapperFactoryBean.class);
beanDefinition.getPropertyValues().add("mapperInterface", clazz.getName());
beanDefinition.getPropertyValues().add("configuration", this.configuration);
registry.registerBeanDefinition(clazz.getName(), beanDefinition);
log.info("add mapper for " + clazz.getName());
}
}
3.加载MapperFactoryBean类会调用该类的getObject方法
@Data
public class MapperFactoryBean<T> implements FactoryBean<T> {
private Class<T> mapperInterface;
Configuration configuration;
public MapperFactoryBean() {
}
@Override
public T getObject() throws Exception {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(),
new Class[]{mapperInterface}, new MapperProxy<T>(mapperInterface, configuration));
}
@Override
public Class<T> getObjectType() {
return this.mapperInterface;
}
}
4.代理调用类MapperProxy
public class MapperProxy<T> implements InvocationHandler, Serializable {
Class<T> mapperInterface;
Configuration configuration;
public MapperProxy(Class<T> mapperInterface, Configuration configuration) {
this.mapperInterface = mapperInterface;
this.configuration = configuration;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
MapperMethod mapperMethod = new MapperMethod(configuration, method, this.mapperInterface);
return mapperMethod.execute(args);
}
}
5.sqlseesion代理生成类MapperMethod
public MapperMethod(Configuration configuration, Method method, Class<?> mapperInterface) {
this.method = method;
this.sqlCommend = new SqlCommend();
String statementId = mapperInterface.getName() + "." + method.getName();
sqlCommend.setStatementId(statementId);
sqlCommend.setResultType(method.getReturnType());
sqlCommend.setParamType(method.getParameterTypes());
sqlCommend.setSql(configuration.getMappedStatements().get(statementId));
this.sqlSessionProxy = createSqlSessionProxy(configuration);
}
private SqlSession createSqlSessionProxy(Configuration configuration) {
return (SqlSession) Proxy.newProxyInstance(this.getClass().getClassLoader(),
new Class[]{SqlSession.class}, new SqlSessionInvocation(configuration, sqlCommend));
}
public Object execute(Object[] args) {
switch (sqlCommend.getType()) {
case "SELECT":
Class<?> returnType = this.method.getReturnType();
if (List.class.equals(returnType)) {
return this.sqlSessionProxy.selectList(sqlCommend.getStatementId(), args);
} else if (!void.class.equals(returnType)) {
return this.sqlSessionProxy.selectOne(sqlCommend.getStatementId(), args);
}
case "UPDATE":
case "INSERT":
return this.sqlSessionProxy.update(sqlCommend.getStatementId(), args);
case "DELETE":
return this.sqlSessionProxy.delete(sqlCommend.getStatementId(), args);
}
throw new RuntimeException("sql类型异常," + sqlCommend.getType());
}
6.SqlSessionInvocation 新sqlSeesion和事务代理类
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
DefualtSqlSession sqlSession = getSqlSession();
Object result = method.invoke(sqlSession, args);
return result;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private DefualtSqlSession getSqlSession() {
Executor executor = new BaseExecutor(configuration, sqlCommend);
executor = (Executor) this.pluginAll(executor);
return new DefualtSqlSession(executor);
}
private Object pluginAll(Object executor) {
List<Object> interfaceList = configuration.getPluGinInterfaceList();
for (Object interceptor : interfaceList) {
Interceptor instance;
try {
instance = (Interceptor) ((Class<?>) interceptor).newInstance();
} catch (Exception e) {
continue;
}
executor = instance.plugin(executor);
}
return executor;
}
7.sqlSeesion默认实现类DefualtSqlSession
@Override
public <E> List<E> selectList(String statement, Object parameter) {
return this.executor.query(statement, parameter);
}
8.插件的执行代理类Plugin
@Slf4j
@Component
@Intercepts(method = "com.example.mybatis.executor.BaseExecutor.query")
public class PluginService implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
try {
Object target = invocation.getTarget();
log.info("Sql{}", ((BaseExecutor) target).getSqlCommend().getSql());
Object[] args = invocation.getArgs();
log.info("statementId:{}", args[0]);
log.info("参数:{}", args[1]);
} catch (Exception e) {
log.error("插件异常!");
}
return invocation.process();
}
}
public class Plugin implements InvocationHandler {
Object target;
Interceptor interceptor;
public Plugin(Object target, Interceptor interceptor) {
this.target = target;
this.interceptor = interceptor;
}
public static Object wrap(Object target, Interceptor interceptor) {
Class<?> clazz = target.getClass();
Class<?>[] interfaces = clazz.getInterfaces();
return Proxy.newProxyInstance(clazz.getClassLoader(), interfaces, new Plugin(target, interceptor));
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Intercepts annotation = interceptor.getClass().getAnnotation(Intercepts.class);
if (annotation.method().equals(target.getClass().getName()+"."+method.getName())) {
return interceptor.intercept(new Invocation(target, method, args));
}
return method.invoke(target, args);
}
}
9.BaseExecutor最终的执行类
@Getter
@Slf4j
public class BaseExecutor implements Executor {
Configuration configuration;
SqlCommend sqlCommend;
static String driverClassName = "driver-class-name";
static String url = "url";
static String username = "username";
static String password = "password";
public BaseExecutor(Configuration configuration, SqlCommend sqlCommend) {
this.configuration = configuration;
this.sqlCommend = sqlCommend;
}
@Override
public int update(String statement, Object parameter) {
Statement st = null;
try {
String sql = this.getBoundSql(parameter);
st = prepareStatement();
st.executeUpdate(sql);
return st.getUpdateCount();
} catch (Exception e) {
log.error(e.getMessage(), e);
return 0;
} finally {
closeStatement(st);
}
}
@Override
public int delete(String statement, Object parameter) {
Statement st = null;
try {
String sql = this.getBoundSql(parameter);
st = prepareStatement();
st.execute(sql);
return st.getUpdateCount();
} catch (Exception e) {
log.error(e.getMessage(), e);
return 0;
} finally {
closeStatement(st);
}
}
@Override
public <E> List<E> query(String statement, Object parameter) {
Statement st = null;
try {
String sql = this.getBoundSql(parameter);
st = prepareStatement();
ResultSet rs = st.executeQuery(sql);
return handlerResultSet(rs);
} catch (Exception e) {
log.error(e.getMessage(), e);
return null;
} finally {
closeStatement(st);
}
}
private void closeStatement(Statement st) {
if (st != null) {
try {
st.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
private String getBoundSql(Object parameter) throws Exception {
String sql = this.sqlCommend.getSql();
Object[] args = (Object[]) parameter;
if (args.length > 0) {
if (args[0] instanceof Map) {
Map<String, Object> map = (Map) args[0];
for (String str : map.keySet()) {
sql = sql.replace("#{" + str + "}",
"'" + map.get(str).toString() + "'");
}
} else {
sql = sql.replaceAll("#\\{.*?}", "%s");
sql = String.format(sql, args);
}
}
log.info("execute sql:{}", sql);
return sql;
}
private <E> List<E> handlerResultSet(ResultSet rs) throws Exception {
List<Map<String, Object>> mapList = new ArrayList<>();
Map<String, Object> map = new HashMap<>();
ResultSetMetaData metaData = rs.getMetaData();
while (rs.next()) {
for (int i = 1; i <= metaData.getColumnCount(); i++) {
map.put(metaData.getColumnName(i), rs.getObject(i));
}
mapList.add(map);
}
log.info("result:{}", mapList);
return (List<E>) mapList;
}
private Statement prepareStatement() {
try {
Properties properties = configuration.getProperties();
Class.forName(properties.getProperty(driverClassName));
Connection conn = DriverManager.getConnection(
properties.getProperty(url),
properties.getProperty(username),
properties.getProperty(password));
Statement stmt = conn.createStatement();
return stmt;
} catch (Exception e) {
throw new RuntimeException("数据库连接失败,e:" + e);
}
}
}
10.测试日志
10.1 insert测试
- http://127.0.0.1:12233/insert
@GetMapping("/insert")
public Object insert() {
Map<String, Object> map = new HashMap<>();
map.put("name", new Date().getTime());
map.put("age", 22);
map.put("status", 1);
int i = testMapper.insert(map);
return i;
}
- 日志输出
c.example.mybatis.executor.BaseExecutor : execute sql:insert into user_info(`name`,`age`,`status`) values('1647241298926','22','1')
10.2 update测试
- http://127.0.0.1:12233/update?id=1
@GetMapping("/update")
public Object update(@RequestParam Long id) {
Map<String, Object> map = new HashMap<>();
map.put("id", id);
map.put("name", "update_" + new Date().getTime());
int i = testMapper.update(map);
return i;
}
- 日志输出
BaseExecutor : execute sql:update user_info set name ='update_1647241489267' where id = '1'
10.3 select测试
- http://127.0.0.1:12233/select?id=1
@GetMapping("/select")
public Object select(@RequestParam Long id) {
Map<String, Object> map = testMapper.selectById(id);
return map;
}
com.example.demo.plugin.PluginService : Sqlselect * from user_info where id = #{id}
com.example.demo.plugin.PluginService : statementId:com.example.demo.mapper.TestMapper.selectById
com.example.demo.plugin.PluginService : 参数:[1]
c.example.mybatis.executor.BaseExecutor : execute sql:select * from user_info where id = 1
c.example.mybatis.executor.BaseExecutor : result:[{update_time=2021-09-12 18:17:16.0, create_time=2021-09-12 18:13:50.0, name=update_1647241489267, id=1, age=22, status=1}
大大的简化整个流程,方便读懂mybait整个源码。源码地址直通车 以上就是本章的全部内容了。
上一篇:mybatis第八话 - mybaits之ParameterHandler参数处理源码分析 下一篇:mybatis第十话 - mybaits事务的源码分析
立身以立学为先,立学以读书为本
|