018:高仿Mybatis框架
1 纯手写Mybatis框架终极版本效果演示
课程内容:
1、纯手写MappedStatement
2、纯手写BaseExecutor
3、纯手写SimpleExecutor
4、纯手写Mybatis源码分析课程总结
2 纯手写MappedStatement
Select注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Select {
String value();
}
UserMapper
public interface UserMapper {
@Select("SELECT * FROM USER WHERE id = ?")
UserEntity selectUser(Integer id);
}
UserEntity
@Data
public class UserEntity {
private Integer id;
private String name;
private Integer age;
public UserEntity(Integer id, String name, Integer age) {
this.id = id;
this.name = name;
this.age = age;
}
public UserEntity() {
}
@Override
public String toString() {
return "UserEntity{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
MappedStatement 关联sql语句
@Data
public class MappedStatement {
/**
* 数据库相关配置
*/
private Configuration configuration;
/**
* sql语句
*/
private String sqlValue;
/**
* sql语句类型
*/
private Annotation sqlType;
/**
* 返回类型
*/
private Class<?> returnType;
/**
* 传递参数
*/
private Object[] args;
public MappedStatement(Method method, Object[] args) {
this.returnType = method.getReturnType();
this.args = args;
// 问题:注解获取很麻烦 用一个集合存放当前定义所有的注解 策略模式循环判断
this.sqlType = method.getAnnotation(Select.class);
setSqlValue();
}
/**
* 从注解上获取sql语句
*/
public void setSqlValue() {
if (sqlType instanceof Select) {
Select selectAnnotation = (Select) this.sqlType;
this.sqlValue = selectAnnotation.value();
}
}
public String getMappedStatementKey() {
// 问题存在,虽然args值是相同的,但是每次args.toString() class地址不同
// return sqlValue + args == null ? "" : args.toString() + returnType;
String mappedStatementKey = sqlValue + arrayToString() + returnType.toString();
System.out.println("mappedStatementKey:" + mappedStatementKey);
return mappedStatementKey;
}
public String arrayToString() {
String str = "";
for (int i = 0; i < args.length; i++) {
Object obj = (Object) args[i];
str += obj + "";
}
return str;
}
}
3 纯手写BaseExecutor01
SqlSession
public interface SqlSession {
<T> T getMapper(Class<T> type) throws Exception;
<T> T selectOne(MappedStatement mappedStatement) throws Exception;
<E> List<E> selectList(MappedStatement mappedStatement);
}
DefaultSqlSession
public class DefaultSqlSession implements SqlSession {
private final Configuration configuration;
private BaseExecutor baseExecutor;
public DefaultSqlSession(Configuration configuration, BaseExecutor baseExecutor) {
this.configuration = configuration;
this.baseExecutor = baseExecutor;
}
@Override
public <T> T getMapper(Class<T> type) throws Exception {
// 查询configuration是否有注册mapper接口
MapperProxyFactory mapperProxyFactory = (MapperProxyFactory) configuration.getMapper(type);
if (mapperProxyFactory == null) {
throw new Exception("Type" + type + "is not known to the MapperProxyFactory.");
}
return (T) mapperProxyFactory.newInstance(this);
}
@Override
public <T> T selectOne(MappedStatement mappedStatement) throws Exception {
List<T> list = this.selectList(mappedStatement);
if (list.size() == 1) {
return list.get(0);
} else if (list.size() > 1) {
throw new Exception("Expected one result (or null) to be returned by selectOne(), but not found: " + list.size());
} else {
return null;
}
}
@Override
public <E> List<E> selectList(MappedStatement mappedStatement) {
mappedStatement.setConfiguration(configuration);
return baseExecutor.query(mappedStatement);
}
}
BaseExecutor
public abstract class BaseExecutor {
private Map<Object, Object> cache = new HashMap<Object, Object>();
public <E> List<E> query(MappedStatement ms) {
String mappedStatementKey = ms.getMappedStatementKey();
// 1.先查询一级缓存,有数据直接返回一级缓存的
List<E> list = (List<E>) cache.get(mappedStatementKey);
// 2.如果一级缓存没有查询数据库
if (list != null) {
return list;
}
return queryFromDatabase(ms);
}
/**
* 查询数据库
*
* @param ms
* @param <E>
* @return
*/
private <E> List<E> queryFromDatabase(MappedStatement ms) {
List<E> list = doQuery(ms);
// 如果数据库查询不为空 添加到一级缓存中
if (list != null) {
cache.put(ms.getMappedStatementKey(), list);
}
return list;
}
protected abstract <E> List<E> doQuery(MappedStatement ms);
}
4 纯手写BaseExecutor02
DefaultSqlSessionFactory
public class DefaultSqlSessionFactory implements SqlSessionFactory {
private final Configuration configuration;
public DefaultSqlSessionFactory(Configuration configuration) {
this.configuration = configuration;
}
@Override
public SqlSession openSqlSession() {
// 使用默认的SqlSession
return new DefaultSqlSession(configuration, new SimpleExecutor());
}
}
5 纯手写SimpleExecutor
SimpleExecutor
public class SimpleExecutor extends BaseExecutor {
@Override
protected <E> List<E> doQuery(MappedStatement ms) {
System.out.println("开始执行数据库..." + ms.toString());
// 1.创建jdbc连接
Configuration configuration = ms.getConfiguration();
// 2.预编译执行
MyBatisJDBCUtil myBatisJDBCUtil = new MyBatisJDBCUtil(configuration.getDataSource());
List<E> list = (List<E>) myBatisJDBCUtil.queryListExecute(ms.getSqlValue(), ms.getArgs(), ms.getReturnType());
// 3.封装结果集
return list;
}
}
MapperProxy
public class MapperProxy<T> implements InvocationHandler {
private SqlSession sqlSession;
public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface) {
this.sqlSession = sqlSession;
}
/**
* 执行invoke 如何知道具体要执行哪个sql语句
* 使用method对象获取UserMapper对应sql语句
*
* @param proxy
* @param method
* @param args
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 关联对应的StateMent
// // 1.判断method方法上是否有加注解
// Annotation annotation = method.getAnnotation(Select.class);
// if (annotation == null) {
// throw new Exception("该方法上没有定义sql语句,无法关联MappedStatement");
// }
// 后期可以加上缓存控制 mappedStatement 没必要每次new一个MappedStatement
MappedStatement mappedStatement = new MappedStatement(method, args);
// 2.获取注解上的sql语句
return sqlSession.selectOne(mappedStatement);
}
}
TestMyBatis
public class TestMyBatis {
public static void main(String[] args) throws Exception {
// 1.获取默认sqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build("my_config.properties");
SqlSession sqlSession = sqlSessionFactory.openSqlSession();
// 2.生成userMapper代理类
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// 3.执行MapperProxy的invoke方法
System.out.println("第一次查询>>>>");
UserEntity userEntity1 = userMapper.selectUser(2);
System.out.println("userEntity1:" + userEntity1.toString());
System.out.println("第二次查询>>>>");
UserEntity userEntity2 = userMapper.selectUser(2);
System.out.println("userEntity2:" + userEntity2.toString());
}
}
测试结果:
6 纯手写Mybatis源码分析课程总结
Mybatis中的一级缓存底层怎么实现? HashMap
存在哪些缺陷?线程安全问题; 集群不共享
实际项目中一级缓存sqlSession是每次都会初始化吗? 是
一级/二级缓存区别? 二级缓存属于第三方缓存,可以存redis或ehCache
一级/二级缓存用到哪些设计模式? 装饰者,二级缓存属于开发者扩展增强,不影响原有一级缓存代码
mybatis mapper接口如何初始化? Jdk动态代理
mybatis用到哪些设计模式?
建造者模式,例如SqlSessionFactoryBuilder、XMLConfigBuilder、XMLMapperBuilder、XMLStatementBuilder、CacheBuilder;
工厂模式,例如SqlSessionFactory、ObjectFactory、MapperProxyFactory;
单例模式,例如ErrorContext和LogFactory;
代理模式,Mybatis实现的核心,比如MapperProxy、ConnectionLogger,用的jdk的动态代理;还有executor.loader包使用了cglib或者javassist达到延迟加载的效果;
组合模式,例如SqlNode和各个子类ChooseSqlNode等;
模板方法模式,例如BaseExecutor和SimpleExecutor,还有BaseTypeHandler和所有的子类例如IntegerTypeHandler;
适配器模式,例如Log的Mybatis接口和它对jdbc、log4j等各种日志框架的适配实现;
装饰者模式,缓存设计,例如Cache包中的cache.decorators子包中等各个装饰者的实现;
迭代器模式,例如迭代器模式PropertyTokenizer;
工具类:MyBatisJDBCUtil
package com.mayikt.utils;
import com.mayikt.session.DataSource;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
public class MyBatisJDBCUtil {
public static String url = null;
public static String username = null;
public static String password = null;
public static String driver = null;
public MyBatisJDBCUtil(DataSource dataSource) {
url = dataSource.getUrl();
username = dataSource.getUsername();
password = dataSource.getPassword();
driver = dataSource.getDriver();
}
/**
* 获取数据库连接对象
*
* @return
*/
public static Connection getConnection() {
try {
Connection conn = DriverManager.getConnection(url, username, password);
return conn;
} catch (SQLException e) {
e.printStackTrace();
return null;
}
}
/**
* 添加删除修改数据
*
* @return
*/
public static boolean insertDeleteUpdateExecute(String sql, ArrayList<Object> paras) {
Connection conn = getConnection();
PreparedStatement pst = null;
boolean flag = false;
int index = 1;
try {
pst = conn.prepareStatement(sql);
if (paras != null && paras.size() > 0) {
pst.clearParameters();
for (int i = 0; i < paras.size(); i++) {
pst.setObject(index++, paras.get(i));
}
}
int result = pst.executeUpdate();
flag = result > 0 ? true : false;
} catch (SQLException e) {
flag = false;
e.printStackTrace();
} finally {
close(conn, pst);
}
return flag;
}
/**
* 获取单个对象 可用于登录注册验证
*
* @param sql
* @param paras
* @param cls
* @return
*/
public <T> T findBySingleObject(String sql, ArrayList<Object> paras, Class<T> cls) {
Connection conn = getConnection();
PreparedStatement pst = null;
ResultSet rs = null;
T singleObject = null;
int index = 1;
try {
pst = conn.prepareStatement(sql);
if (paras != null && paras.size() > 0) {
pst.clearParameters();
for (int i = 0; i < paras.size(); i++) {
pst.setObject(index++, paras.get(i));
}
}
rs = pst.executeQuery();
ResultSetMetaData rsmd = rs.getMetaData();
int columnCount = rsmd.getColumnCount();
while (rs.next()) {
singleObject = cls.newInstance();
for (int i = 0; i < columnCount; i++) {
String columnName = rsmd.getColumnName(i + 1);
Object columnValue = rs.getObject(columnName);
Field field = cls.getDeclaredField(columnName);
field.setAccessible(true);
field.set(singleObject, columnValue);
}
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
close(conn, pst);
}
return singleObject;
}
/**
* 列表查询
*/
public <T> List<T> queryListExecute(String sql, Object[] paras, Class<T> cls) {
Connection conn = getConnection();
PreparedStatement pst = null;
ResultSet rs = null;
T singleObject = null;
int index = 1;
List<T> list = new ArrayList<T>();
try {
pst = conn.prepareStatement(sql);
if (paras != null && paras.length > 0) {
pst.clearParameters();
for (int i = 0; i < paras.length; i++) {
pst.setObject(index++, paras[i]);
}
}
rs = pst.executeQuery();
ResultSetMetaData rsmd = rs.getMetaData();
int columnCount = rsmd.getColumnCount();
while (rs.next()) {
singleObject = cls.newInstance();
for (int i = 0; i < columnCount; i++) {
String columnName = rsmd.getColumnName(i + 1);
Object columdValue = rs.getObject(columnName);
Field field = cls.getDeclaredField(columnName);
field.setAccessible(true);
field.set(singleObject, columdValue);
}
list.add(singleObject);
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
close(conn, pst, rs);
}
return list;
}
/*
* 释放资源
*/
public static void close(Connection conn, PreparedStatement pst) {
if (pst != null) {
try {
pst.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
public static void close(Connection conn, PreparedStatement pst,
ResultSet rs) {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (pst != null) {
try {
pst.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}