![]()
MyBaits部分的自定义注解代码
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface Param {
String value();
}
package com.hz.mybatis.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Select {
String value() default "";
}
MyBaits部分的类型处理类代码
package com.hz.mybatis.handler;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class IntegerTypeHandler implements TypeHandler<Integer>{
@Override
public void setParameter(PreparedStatement ps, int i, Integer parameter) throws SQLException {
ps.setInt(i, parameter);
}
@Override
public Integer getResult(ResultSet rs, String columnName) throws SQLException {
return rs.getInt(columnName);
}
}
package com.hz.mybatis.handler;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class StringTypeHandler implements TypeHandler<String>{
@Override
public void setParameter(PreparedStatement ps, int i, String parameter) throws SQLException {
ps.setString(i, parameter);
}
@Override
public String getResult(ResultSet rs, String columnName) throws SQLException {
return rs.getString(columnName);
}
}
package com.hz.mybatis.handler;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public interface TypeHandler<T> {
void setParameter(PreparedStatement ps, int i, T parameter) throws SQLException;
T getResult(ResultSet rs, String columnName) throws SQLException;
}
MyBaits部分的SQL分析器类代码
/**
* Copyright 2009-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hz.mybatis.parser;
/**
* @author Clinton Begin
*/
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()) {
return "";
}
// search open token
int start = text.indexOf(openToken, 0);
if (start == -1) {
return text;
}
char[] src = text.toCharArray();
int offset = 0;
final StringBuilder builder = new StringBuilder();
StringBuilder expression = null;
while (start > -1) {
if (start > 0 && src[start - 1] == '\\') {
// this open token is escaped. remove the backslash and continue.
builder.append(src, offset, start - offset - 1).append(openToken);
offset = start + openToken.length();
} else {
// found open token. let's search close token.
if (expression == null) {
expression = new StringBuilder();
} else {
expression.setLength(0);
}
builder.append(src, offset, start - offset);
offset = start + openToken.length();
int end = text.indexOf(closeToken, offset);
while (end > -1) {
if (end > offset && src[end - 1] == '\\') {
// this close token is escaped. remove the backslash and continue.
expression.append(src, offset, end - offset - 1).append(closeToken);
offset = end + closeToken.length();
end = text.indexOf(closeToken, offset);
} else {
expression.append(src, offset, end - offset);
offset = end + closeToken.length();
break;
}
}
if (end == -1) {
// close token was not found.
builder.append(src, start, src.length - start);
offset = src.length;
} else {
builder.append(handler.handleToken(expression.toString()));
offset = end + closeToken.length();
}
}
start = text.indexOf(openToken, offset);
}
if (offset < src.length) {
builder.append(src, offset, src.length - offset);
}
return builder.toString();
}
}
package com.hz.mybatis.parser;
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;
}
}
package com.hz.mybatis.parser;
import java.util.ArrayList;
import java.util.List;
public class ParameterMappingTokenHandler implements TokenHandler{
private List<ParameterMapping> parameterMappings = new ArrayList<ParameterMapping>();
@Override
public String handleToken(String content) {
parameterMappings.add(new ParameterMapping(content));
return "?";
}
public List<ParameterMapping> getParameterMappings() {
return parameterMappings;
}
}
package com.hz.mybatis.parser;
public interface TokenHandler {
String handleToken(String content);
}
MyBaits部分的动态代理调用JDBC代码(核心代码)
package com.hz.mybatis.mapper;
import com.hz.mybatis.annotation.Param;
import com.hz.mybatis.annotation.Select;
import com.hz.mybatis.handler.IntegerTypeHandler;
import com.hz.mybatis.handler.StringTypeHandler;
import com.hz.mybatis.handler.TypeHandler;
import com.hz.mybatis.parser.GenericTokenParser;
import com.hz.mybatis.parser.ParameterMapping;
import com.hz.mybatis.parser.ParameterMappingTokenHandler;
import java.lang.reflect.*;
import java.sql.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
public class MapperProxyFactory {
private static Map<Class, TypeHandler> providers = new ConcurrentHashMap<>();
static {
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
providers.put(String.class, new StringTypeHandler());
providers.put(Integer.class, new IntegerTypeHandler());
}
public static <T> T getMapper(Class<T> mapper){
Object proxyInstance = Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{mapper}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//目前只实现了Select注解效果
boolean annotationPresent = method.isAnnotationPresent(Select.class);
if(annotationPresent){
Select annotation = method.getAnnotation(Select.class);
//mybatis中写法:select * from t_user where name = #{userName} and age = #{age}
String sqlStr = annotation.value();
if(sqlStr == null ||sqlStr.equals("")){
throw new RuntimeException("注释中没有写入SQL");
}
// 通过Parser转义 预编译SQL ...where name = ? and age = ?
ParameterMappingTokenHandler tokenHandler = new ParameterMappingTokenHandler();
GenericTokenParser genericTokenParser = new GenericTokenParser("#{","}",tokenHandler);
String parseSQL = genericTokenParser.parse(sqlStr);
//JDBC start=============================================
//获取JDBC连接
Connection conn= DriverManager.getConnection("jdbc:mysql://localhost:3306/hs?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&AllowPublicKeyRetrieval=True","root","root");
//获取被Select修饰的方法参数 封装 paramsMap<paramName,val>
Map<String,Object> paramsMap = new HashMap<>();
Parameter[] parameters = method.getParameters();
for (int i = 0; i < parameters.length; i++) {
Parameter parameter = parameters[i];
boolean annotationPresent1 = parameter.isAnnotationPresent(Param.class);
if(!annotationPresent1)throw new Exception("参数必须修饰");
Param annotation1 = parameter.getAnnotation(Param.class);
String value = annotation1.value();
String name = parameter.getName();
paramsMap.put(value == null || value.equals("") ? name : value,args[i]);
}
//预查询SQL 实现PreparedStatement.setString(index,val) or serInt.setString(index,val)
PreparedStatement stmt = conn.prepareStatement(parseSQL);
List<ParameterMapping> parameterMappings = tokenHandler.getParameterMappings();
for (int i = 0; i < parameterMappings.size(); i++) {
ParameterMapping parameterMapping = parameterMappings.get(i);
String property = parameterMapping.getProperty();
Object value = paramsMap.get(property);
Class<?> aClass = value.getClass();
providers.get(aClass).setParameter(stmt,i+1,value);
}
//执行,得到结果集
stmt.execute();
ResultSet rs = stmt.getResultSet();
List<Object> userList = new ArrayList<>();
Object object = null;
// 获取返回值
Class resultType = null;
Type genericReturnType = method.getGenericReturnType();
if(genericReturnType instanceof Class){
resultType = (Class)genericReturnType;
}else if(genericReturnType instanceof ParameterizedType){
Type actualTypeArgument = ((ParameterizedType) genericReturnType).getActualTypeArguments()[0];
resultType = (Class)actualTypeArgument;
}
//获取返回实体 <属性名,方法>
Map<String , Method> methodMap = new HashMap<>();
Method[] declaredMethods = resultType.getDeclaredMethods();
for (int i = 0; i < declaredMethods.length; i++) {
Method declaredMethod = declaredMethods[i];
String name = declaredMethod.getName();
if (name.startsWith("set")) {
String propertyName = declaredMethod.getName().substring(3);
propertyName = propertyName.substring(0, 1).toLowerCase(Locale.ROOT) + propertyName.substring(1);
methodMap.put(propertyName,declaredMethod);
}
}
//获取返回的字段名
ResultSetMetaData metaData = rs.getMetaData();
List<String> columnNames = new ArrayList<>();
for (int i = 0; i < metaData.getColumnCount(); i++) {
columnNames.add(metaData.getColumnName(i+1));
}
/**
*1.第一种方法
*/
while (rs.next()){
Object o = resultType.newInstance();
// int id = rs.getInt(1);
// String name = rs.getString(2);
// int password =rs.getInt(3);
// userList.add(new User(id,name,password));
//上方注释代码的实现
for (int i = 0; i < columnNames.size(); i++) {
String columnName = columnNames.get(i);
Method method1 = methodMap.get(columnName);
Class<?> parameterType = method1.getParameterTypes()[0];
TypeHandler typeHandler = providers.get(parameterType);
Object result = typeHandler.getResult(rs, columnName);
method1.invoke(o,result);
}
userList.add(o);
if(method.getReturnType().equals(List.class)){
object = userList;
}else{
object = userList.get(0);
}
}
conn.close();
//JDBC end=============================================
return object;
}else{
throw new RuntimeException("未被注解修饰。。");
}
//return method.invoke(age, args);
}
});
return (T)proxyInstance;
}
}
业务调用部分的代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.hz</groupId>
<artifactId>HzMyBatis</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>HzMyBatis</name>
<description>HzMyBatis</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.35</version>
</dependency>
<!--数据库驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
package com.hz.mybatis.controller;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.hz.mybatis.pojo.User;
import com.hz.mybatis.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
@RequestMapping("user")
public class UserController {
@Autowired
private UserService userService;
@RequestMapping("listAll")
public String listAll(@RequestParam("username") String username,@RequestParam("age") Integer age) {
List<User> list = userService.listAll(username, age);
String result = JSONArray.toJSONString(list);
return result;
}
@RequestMapping("getById/{id}")
public String getById(@PathVariable("id") Integer id) {
User user = userService.getById(id);
String result = JSONObject.toJSONString(user);
return result;
}
}
package com.hz.mybatis.pojo;
public class User {
private Integer id;
private String name;
private Integer age;
public User() {
}
public User(Integer id, String name, Integer age) {
this.id = id;
this.name = name;
this.age = 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;
}
}
package com.hz.mybatis.service;
import com.hz.mybatis.pojo.User;
import java.util.List;
public interface UserService {
List<User> listAll(String name, Integer age);
User getById(Integer id);
}
package com.hz.mybatis.service.impl;
import com.hz.mybatis.mapper.MapperProxyFactory;
import com.hz.mybatis.mapper.UserMapper;
import com.hz.mybatis.pojo.User;
import com.hz.mybatis.service.UserService;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class UserServiceImpl implements UserService {
@Override
public List<User> listAll(String name, Integer age) {
UserMapper mapper = MapperProxyFactory.getMapper(UserMapper.class);
Object o = mapper.selectAll(name, age);
return (List<User>)o;
}
@Override
public User getById(Integer id) {
UserMapper mapper = MapperProxyFactory.getMapper(UserMapper.class);
Object o = mapper.getById(id);
return (User)o;
}
}
package com.hz.mybatis.mapper;
import com.hz.mybatis.annotation.Param;
import com.hz.mybatis.annotation.Select;
import com.hz.mybatis.pojo.User;
import org.springframework.stereotype.Component;
import java.util.List;
public interface UserMapper {
@Select("select * from t_user where name = #{userName} and age = #{age}")
List<User> selectAll(@Param("userName") String name, @Param("age") Integer age);
@Select("select * from t_user where id = #{id}")
User getById(@Param("id") Integer id);
}