mybatis是一套ORM框架,也就是把字段映射为对象的属性。
大学学过,最基础要操作一个数据库步骤:
引入JDBC包——DriverManager注册驱动——创建连接——创建Statement——CRUD操作——操作结果集——关闭连接。
为了省去上面每次操作的繁琐过程,而且相比hibernate更加灵活,方便维护。mybatis出现了。
首先pom.xml要引入mybatis。
框架的大致逻辑:
package com.stu.demo;
import com.stu.demo.pojo.User;
import org.apache.ibatis.annotations.Select;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
interface UserMapper{
@Select("select * from t_user where id = #{id} and grade = #{grade}")
List<User> selectUsers(Integer id, String grade);
}
public class Application {
public static void main(String[] args) {
// JDK动态代理,反射实现
UserMapper userMapper = (UserMapper) Proxy.newProxyInstance(Application.class.getClassLoader(), new Class<?>[]{
UserMapper.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("方法名字:"+method.getName());
System.out.println("方法入参:" + Arrays.toString(args));
// sql参数和方法参数的对应键值关系
Map<String, Object> mapData = buildSQLData(method, args);
Select annotions = method.getAnnotation(Select.class);
if (annotions != null) {
String[] strs = annotions.value(); // 这个sql可以是多条
System.out.println("Mybatis种sql语句:" + Arrays.toString(strs));
// 根据键值关系赋值得到sql
String sql = bulidSQL(strs[0], mapData);
System.out.println("最终得到要在数据库执行的sql语句:" + sql);
}
return null;
}
});
userMapper.selectUsers(1, "303班");
}
private static String bulidSQL(String str, Map<String, Object> mapData) {
// 频繁字符操作
StringBuilder sb = new StringBuilder();
int length = str.length();
for (int i = 0; i < length; i++) {
char ch = str.charAt(i);
if (ch == '#') {
int nextIndex = i + 1;
char nextChar = str.charAt(nextIndex);
if ('{' != nextChar) {
throw new RuntimeException("sql异常:" + str);
}
// 找到当前}结束位置 argStr为返回的参数key,比如id
StringBuilder argStr = new StringBuilder();
i = parseArgStr(argStr,str,nextIndex);
// 根据key找到对应的值。
Object argValue = mapData.get(argStr.toString());
if (argValue == null) {
throw new RuntimeException("argValue为null");
}
sb.append(argValue);
continue;
}
sb.append(ch);
}
return sb.toString();
}
private static int parseArgStr(StringBuilder argStr, String str, int nextIndex) {
// 从nextIndex++循环
nextIndex++;
for (;nextIndex < str.length(); nextIndex++) {
char ch = str.charAt(nextIndex);
if (ch != '}') {
argStr.append(ch);
continue;
}
if (ch == '}') {
return nextIndex;
}
}
// 最后都没有} 异常处理。
throw new RuntimeException("缺少}:"+ str);
}
/**
* sql参数和方法参数的对应键值关系
* @param method
* @param args
* @return
*/
public static Map<String, Object> buildSQLData(Method method, Object[] args) {
Map<String, Object> mapData = new HashMap<>();
// 参数获得
Parameter[] parameters = method.getParameters();
// 将我们的参数赋值给sql上变量。
int index[] = {
0}; // 因为里面用的时引用类型。
// 当数据量大时,并行运行。
// Arrays.asList(parameters).parallelStream().forEach
// 与之对应Map<String, Object> mapData = new ConcurrentHashMap<>();
Arrays.asList(parameters).forEach((parameter)->{
String name = parameter.getName();
System.out.println("SQL上参数的名字:" + name);
// 得到键值关系--sql参数和方法参数的对应键值关系
mapData.put(name, args[index[0]]);
index[0]++;
});
return mapData;
}
public static String parseIntoSQL(String sql, Map<String, Object> mapData){
return "";
}
}
运行结果:(很清晰的看到这个过程)
在框架种处理更简单:通过反射直接拿到返回类型和参数类型。
UserMapper userMapper = (UserMapper) Proxy.newProxyInstance(Application.class.getClassLoader(), new Class<?>[]{
UserMapper.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(method.getReturnType());
System.out.println(method.getGenericReturnType());
}
return null;
}
});
结果:(找到了这两个,get和set进去就行了)
Mybatis架构设计:
源码包如下,ibatis包下的包:
annotations: 注解包
Arg、AutomapConstructor、CacheNamespace、CacheNamespaceRef、Case、ConstructorArgs、Delete、
DeleteProvider、Flush、Insert、InsertProvider、Lang、Many、MapKey、Mapper、One、Options、
Param、Property、Result、ResultMap、Results、ResultType、Select、SelectKey、SelectProvider、
TypeDiscriminator、Update、UpdateProvider
binding:绑定包
BindingException、MapperMethod、MapperProxy、MapperProxyFactory、MapperRegistry
builder:构建包
cache:缓冲存储包
cursor:游标包
datasource:数据源包
exceptions:异常包
executor:执行器包
io:io包
javassist:java助手
jdbc:连接包
lang:基础类型包
logging:日志包
mapping:映射包
ognl:语义解析包
parsing:解析包。用于解析mapper树结构。
plugin:依赖包
reflection:反射包
scripting:脚本包
session:session包
transaction:事务包
type:各种类型包
源码分析:
返回结果集ResultContext:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package org.apache.ibatis.session;
public interface ResultContext<T> {
T getResultObject();
int getResultCount();
boolean isStopped();
void stop();
}
返回处理类ResultHandler:
package org.apache.ibatis.session;
public interface ResultHandler<T> {
void handleResult(ResultContext<? extends T> var1);
}
SqlSessionFactoryBuilder 构建类:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package org.apache.ibatis.session;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.util.Properties;
import org.apache.ibatis.builder.xml.XMLConfigBuilder;
import org.apache.ibatis.exceptions.ExceptionFactory;
import org.apache.ibatis.executor.ErrorContext;
import org.apache.ibatis.session.defaults.DefaultSqlSessionFactory;
public class SqlSessionFactoryBuilder {
public SqlSessionFactoryBuilder() {
}
public SqlSessionFactory build(Reader reader) {
return this.build((Reader)reader, (String)null, (Properties)null);
}
public SqlSessionFactory build(Reader reader, String environment) {
return this.build((Reader)reader, environment, (Properties)null);
}
public SqlSessionFactory build(Reader reader, Properties properties) {
return this.build