MyBatis介绍
MyBatis 作为持久层框架,主要解决了传统 JDBC 操作中的繁琐和重复性工作,提高了开发效率,同时也提供了灵活的 SQL 编写和结果映射功能,使得开发人员可以更加专注于业务逻辑的实现。
这篇文章将会带着大家来实现一个简易的MyBatis,实现MyBatis中的:
- 简化数据库操作
- 灵活的SQL编写
- 参数映射
- 结果集处理
JDBC
我们知道MyBatis是JDBC的一个再封装,那么接下来我会先贴出一段JDBC的代码,在这个基础上进行封装。
实体类:
public class User {
private Integer id;
private String name;
private Integer age;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
JDBC
获取链接
public class GetJDBC {
// 数据库连接相关信息
private static final String jdbcUrl = "jdbc:mysql://localhost:3306/test";
private static final String user = "root";
private static final String password = "root";
// 获取数据库连接的方法
public static Connection getConnection() throws SQLException {
// 注册 JDBC 驱动程序
try {
Class.forName("com.mysql.cj.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
throw new SQLException("JDBC Driver not found", e);
}
// 获取连接对象
return DriverManager.getConnection(jdbcUrl, user, password);
}
}
JDBC查询代码例子
Connection connection = GetJDBC.getConnection();
PreparedStatement statement = connection.prepareStatement("select * from user where name = ?");
statement.setString(1,"张三");
ResultSet resultSet = statement.executeQuery();
List<User> list = new ArrayList<>();
while(resultSet.next()){
User user = new User();
user.setAge(resultSet.getInt("age"));
user.setId(resultSet.getInt("id"));
user.setName(resultSet.getString("name"));
list.add(user);
}
System.out.println(list);
实现
接下来需要我们要开始对以上代码进行再处理了。
首先我们知道MyBatis有一个mapper层,来进行sql映射,那么我们将创建一个UserMapper。
public interface UserMapper {
@Select(value="select * from user where name = #{name} or age = #{age}")
List<User> getAllUser(@Param("name")String name,@Param("age") Integer age);
@Select(value="select * from user where name = #{name}")
User getUser(@Param("name")String name);
}
可以看到上述代码中出现了两个注解 @Select 和 @Param,下面是两个注解的代码
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface Param {
String value();
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Select {
String value();
}
我们可以发现,UserMapper是一个接口,他没有办法帮我们干任何事,所以我们需要一个东西——代理MapperFactoryProxy来帮助UserMapper进行数据库操作与值映射。
public class MapperFactoryProxy {
//存储类型
private static Map<Class, TypeHandler> typeHandlerMap = new HashMap<>();
//初始化
static {
typeHandlerMap.put(Integer.class,new IntegerHandler());
typeHandlerMap.put(String.class,new StringHandler());
}
public static <T> T getMapper(Class<T> mapper){
Object o = Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{mapper}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Select annotation = method.getAnnotation(Select.class);
String sql = annotation.value(); //获取注解值
//需要执行的sql
ParameterMappingTokenHandler parameterMappingTokenHandler = new ParameterMappingTokenHandler();
GenericTokenParser genericTokenParser = new GenericTokenParser("#{", "}", parameterMappingTokenHandler); //解析与存储规则
String parseSql = genericTokenParser.parse(sql); //解析
//获取jdbc链接
Connection connection = GetJDBC.getConnection();
PreparedStatement statement = connection.prepareStatement(parseSql);
//存储对应的参数结构
Map<String, Object> parameterMap = new HashMap<>();
//获取参数
Parameter[] parameters = method.getParameters();
for (int i = 0; i < parameters.length; i++) {
String name = parameters[i].getName();
Param param = parameters[i].getAnnotation(Param.class);
String value = param.value();
parameterMap.put(name, args[i]); //与参数是一一对应的args
parameterMap.put(value, args[i]);
}
//接下来就是赋值的事,我需要知道第几个问号 赋值的是哪一个 总共有多少个问号
List<ParameterMapping> parameterMappings = parameterMappingTokenHandler.getParameterMappings();
for (int i = 0; i < parameterMappings.size(); i++) {//存储了问号对应的名字
Object value = parameterMap.get(parameterMappings.get(i).getProperty()); //获取值
//接下来就需要进行类型的处理 判断他是什么类型的 要怎么判断呢 根据类型
Class<?> aClass = value.getClass();
typeHandlerMap.get(aClass).setParameter(statement, i + 1, value);
}
//执行sql
statement.execute();
//获取查询结果
ResultSet resultSet = statement.getResultSet();
//封装结果
List<Object> result = new ArrayList<>();
//获取方法的返回类型
Class resultType = null;
Type genericReturnType = method.getGenericReturnType();
Boolean mark = false;
if(genericReturnType instanceof Class){
//不是泛型
resultType = (Class) genericReturnType;
}else if(genericReturnType instanceof ParameterizedType){
//是泛型 取集合中的一个
Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
resultType = (Class)actualTypeArguments[0];
mark = true;
}
//查询返回的字段信息
ResultSetMetaData metaData = resultSet.getMetaData();
List<String> columnList = new ArrayList<>();
for (int i = 0; i < metaData.getColumnCount(); i++) {
columnList.add(metaData.getColumnName(i+1));
}
//要解析set方法
Method[] methods = resultType.getMethods();
//存储set方法
Map<String,Method> setterMap = new HashMap<>();
for (int i = 0; i < methods.length; i++) {
if(methods[i].getName().contains("set")){ //如果方法名中存在set
String substring = methods[i].getName().substring(3);
String property = substring.substring(0,1).toLowerCase(Locale.ROOT) + substring.substring(1);
setterMap.put(property,methods[i]);
}
}
while (resultSet.next()) {
Object obj = resultType.newInstance();
for (int i = 0; i < columnList.size(); i++) {
String column = columnList.get(i);
Method setMethod = setterMap.get(column);
Class<?>[] parameterTypes = setMethod.getParameterTypes();
Class<?> returnType = parameterTypes[0]; //set方法只有一个参数
TypeHandler typeHandler = typeHandlerMap.get(returnType);
setMethod.invoke(obj,typeHandler.getResult(resultSet,column));
}
result.add(obj);
}
if(mark){
return result;
}else{
return result.get(0);
}
}
});
return (T) o;
}
}
SQL解析
public class GenericTokenParser {
private final String openToken;
private final String closeToken;
private final TokenHandler handler;
public GenericTokenParser(String openToken, String closeToken, TokenHandler handler) {
this.openToken = openToken;
this.closeToken = closeToken;
this.handler = handler;
}
public String parse(String text) {
if (text != null && !text.isEmpty()) {
int start = text.indexOf(this.openToken);
if (start == -1) {
return text;
} else {
char[] src = text.toCharArray();
int offset = 0;
StringBuilder builder = new StringBuilder();
StringBuilder expression = null;
do {
if (start > 0 && src[start - 1] == '\\') {
builder.append(src, offset, start - offset - 1).append(this.openToken);
offset = start + this.openToken.length();
} else {
if (expression == null) {
expression = new StringBuilder();
} else {
expression.setLength(0);
}
builder.append(src, offset, start - offset);
offset = start + this.openToken.length();
int end;
for(end = text.indexOf(this.closeToken, offset); end > -1; end = text.indexOf(this.closeToken, offset)) {
if (end <= offset || src[end - 1] != '\\') {
expression.append(src, offset, end - offset);
break;
}
expression.append(src, offset, end - offset - 1).append(this.closeToken);
offset = end + this.closeToken.length();
}
if (end == -1) {
builder.append(src, start, src.length - start);
offset = src.length;
} else {
builder.append(this.handler.handleToken(expression.toString()));
offset = end + this.closeToken.length();
}
}
start = text.indexOf(this.openToken, offset);
} while(start > -1);
if (offset < src.length) {
builder.append(src, offset, src.length - offset);
}
return builder.toString();
}
} else {
return "";
}
}
}
存储参数
public class ParameterMapping {
private String property;
public ParameterMapping(String property) {
this.property = property;
}
public String getProperty() {
return property;
}
public void setProperty(String property) {
this.property = property;
}
}
记录参数
public class ParameterMappingTokenHandler implements TokenHandler{
private List<ParameterMapping> parameterMappings = new ArrayList<>();
@Override
public String handleToken(String content) {
parameterMappings.add(new ParameterMapping(content));
return "?";
}
public List<ParameterMapping> getParameterMappings() {
return parameterMappings;
}
}
public interface TokenHandler {
String handleToken(String content);
}
参数类型解析及参数返回值解析,此处只列举Integer和String两种类型
public interface TypeHandler<T> {
void setParameter(PreparedStatement parameter,Integer index,T value) throws SQLException;
T getResult(ResultSet resultSet,String columnName) throws SQLException;
}
public class StringHandler implements TypeHandler<String>{
@Override
public void setParameter(PreparedStatement parameter, Integer index, String value) throws SQLException {
parameter.setString(index,value);
}
@Override
public String getResult(ResultSet resultSet, String columnName) throws SQLException {
return resultSet.getString(columnName);
}
}
public class IntegerHandler implements TypeHandler<Integer>{
@Override
public void setParameter(PreparedStatement parameter, Integer index, Integer value) throws SQLException {
parameter.setInt(index,value);
}
@Override
public Integer getResult(ResultSet resultSet, String columnName) throws SQLException {
return resultSet.getInt(columnName);
}
}
以上就是全部代码实现。