参考:https://www.jianshu.com/p/73ee8caddc68?open_source=weibo_search
Mybatis的架构图:
我们要实现的迷你Mybatis的整体框架思路:
执行器MyExecutor:
public interface MyExecutor { public <T> T query(String statement); }
这里为了方便,直接执行已经处理好的SQL语句
对执行器的实现MyBaseExecutor:
public class MyBaseExecutor implements MyExecutor{ private static final String URL = "jdbc:mysql://localhost:3306/store"; private static final String USER = "root"; private static final String PASS = ""; @Override public <T> T query(String statement) { Connection connection = null; PreparedStatement preparedStatement = null; ResultSet resultSet = null; try { Class.forName("com.mysql.jdbc.Driver"); connection = DriverManager.getConnection(URL,USER,PASS); String sql = statement; preparedStatement = connection.prepareStatement(sql); resultSet = preparedStatement.executeQuery(); Class> clazz = User.class; Object instance = null; if (resultSet.next()){ instance = clazz.newInstance(); //使用反射进行填充属性 Field[] fields = clazz.getDeclaredFields(); for (Field field : fields){ field.setAccessible(true); field.set(instance,resultSet.getObject(field.getName())); } } return (T) instance; }catch (Exception e){ e.printStackTrace(); return null; } } }
注意到,这里代码我写死了,所以写的并不好,现在只是理解Mybatis的设计思想
定义mapper接口和mapper文件(这里为了简单,使用类来代替)
public interface UserMapper { public User findUserById(String id); public void insertUser(User user); }
public class UserMapperXML{ //相当于xml的命名空间 public static final String namespace = "mybatis.UserMapper"; //相当于select标签 private static Map,String> mathodSqlMap = new HashMap<>(); static{ mathodSqlMap.put("findUserById","select * from user where uid = %s"); } public static String getMethodSql(String method){ return mathodSqlMap.get(method); } }
定义SqlSession,sqlSession可以查询结果集,也可以返回mapper代理
public interface MySqlSession { <T> T selectOne(String param); <T> T getMapper(Class<T> clazz); }
实现SqlSession
public class MyDefaultSqlSession implements MySqlSession{ private MyExecutor myExecutor = new MyBaseExecutor(); @Override public <T> T selectOne(String param) { return myExecutor.query(param); } @Override public <T> T getMapper(Class<T> clazz) { return (T) Proxy.newProxyInstance(clazz.getClassLoader(),new Class[]{clazz},new MyMapperProxy(this)); } }
可以看到,sqlSession执行sql语句是用执行器Executor执行的。
获取mapper代码,出现了JDK动态代理,接下来我们需要实现mapper
public class MyMapperProxy implements InvocationHandler{ private MySqlSession sqlSession; public MyMapperProxy(){} public MyMapperProxy(MySqlSession sqlSession){ this.sqlSession = sqlSession; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String mapperClass = method.getDeclaringClass().getName(); System.out.println("mapperClass"+mapperClass); if (UserMapperXML.namespace.equals(mapperClass)){ String methodName = method.getName(); String originsql = UserMapperXML.getMethodSql(methodName); String formatSql = String.format(originsql,String.valueOf(args[0])); return sqlSession.selectOne(formatSql); } return null; } }
首先根据命名空间,找出与mapper接口方法名相同的sql语句,然后交给sqlSession来执行。
就这样,一个迷你版的Mybatis就实现了。测试一下
public class Test { public static void main(String[] args) { MySqlSession sqlSession = new MyDefaultSqlSession(); UserMapper userMapper = sqlSession.getMapper(UserMapper.class); User user = userMapper.findUserById(1+""); System.out.println(user); } }
输出为
成功了