经验总结
今天跟着视频写了一个简单版的Mybatis,特此记录细节以及感悟。
实现步骤
一、.首先看main方法,实现一个Mybatis框架到底需要哪些对象。
public static void main(String[] args)throws Exception {
// 1.读取配置文件
InputStream in = Resources.getResourcesAsStream("SqlMapConfig.xml");
// 2.创建SQLSessionFaction工厂
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in);
// 3.用SQLSession工厂生产SQLSession对象0
SqlSession session = factory.openSession();
// 4.使用SQLSession创建Dao接口的代理对象
IAccountDao accountDao = (IAccountDao) session.getMapper(IAccountDao.class);
// 5.使用代理对象执行方法
List<Account> accounts = accountDao.findAllAccount();
for (Account account : accounts) {
System.out.println(account);
}
// 6.释放资源
session.close();
in.close();
}
分析:
第一行代码,需要一个Resource对象,并且在这个类中,需要一个名为getResourcesAsStream,返回值类型为InputStream的方法。
第二行代码,明显需要一个SqlSessionFactoryBuilder对象。
第三行代码,分析出需要一个SqlSessionFactory 对象,且这个对象是通过SqlSessionFactoryBuilder对象的build方法和一个InputStream参数创建出来的。
第四行代码,用到一个SqlSession对象,且这个对象是通过S qlSessionFactory的openSession方法创建出来的。
第五行代码,通过SqlSession的getMapper方法和一个对象的字节码文件,可以看出这里是使用了泛型,以此才能支持通过同一个方法,返回不同的mapper。
小结:Mybatis至少需要用到的对象:1.Resource、2.SqlSessionFactoryBuilder、3.SqlSessionFactory 、4.SqlSession。
除此之外,还需要xml文件的解析类(XMLConfigBuilder),SQL语句的执行类(Executor),数据库连接属性类(Configuration),
注册驱动获取连接类(DataSourceUtil),动态代理接口InvocationHandler接口实现类(MapperProxy),Sql语句对象封装映射类(Mapper),但是其中的Executor和XMLConfigBuilder类我还需要花点时间消化,这里相关的工具类和配置类就先不展出,择日更新。
至少用到的方法:
- Resources对象 的 public static InputStream getResourcesAsStream(String filePath){}
- SqlSessionFactoryBuilder 的 public SqlSessionFactory build(InputStream config){}
- Sqlsesson 的 SqlSession openSession();
- SqlSession 的 < T > T getMapper(Class< T > daoIntegerClass);
大致流程:Resource类读取配置文件,把配置文件内容转换为字节输入流,再使用创建者模式,利用字节流创建出一个SqlSessionFactory(工厂模式),接着使用工厂生产出SqlSession,最后通过Session获得Mapper对象。
二、实现具体类和接口
1.Resource类
public class Resources {
//getResourcesAsStream方法,返回一个字节输入流
public static InputStream getResourcesAsStream(String filePath){
//反射机制,类加载器获取方法
return Resources.class.getClassLoader().getResourceAsStream(filePath);
}
}
2.SqlSessionFactoryBuilder类
public class SqlSessionFactoryBuilder {
//创建SqlSessionFactory对象的方法
public SqlSessionFactory build(InputStream config){
//解析xml文件获取连接信息,并封装进Configuration对象中
Configuration cfg = XMLConfigBuilder.loadConfiguration(config);
//返回一个SqlSessionFactory的实现类对象
return new DefaultSqlSessionFactoryImpl(cfg);
}
}
3.SqlSessionFactory 接口及其实现类
public interface SqlSessionFactory {
SqlSession openSession();
}
public class DefaultSqlSessionFactoryImpl implements SqlSessionFactory{
private Configuration cfg;
public DefaultSqlSessionFactoryImpl(Configuration cfg) {
this.cfg = cfg;
}
@Override
public SqlSession openSession() {
//SqlSession的实现类
return new DefaultSqlSessionImpl(cfg);
}
}
4.SqlSession接口及其实现类以及动态代理接口实现类
public interface SqlSession {
<T> T getMapper(Class<T> daoIntegerClass);
void close();
}
public class DefaultSqlSessionImpl implements SqlSession {
private Configuration cfg;
private Connection connection;
public DefaultSqlSessionImpl(Configuration cfg) {
this.cfg = cfg;
connection = DataSourceUtil.getConnection(cfg);
}
@Override
public <T> T getMapper(Class<T> daoIntegerClass) {
//Proxy是动态代理技术的实现,MapperProxy类实现了InvocationHandler接口,做到了方法增强
return (T) Proxy.newProxyInstance(daoIntegerClass.getClassLoader(),new Class[]{
daoIntegerClass},
new MapperProxy(cfg.getMappers(),connection));
}
@Override
public void close() {
if (connection != null){
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
public class MapperProxy implements InvocationHandler{
private Map<String,Mapper> mappers;
private Connection conn;
public MapperProxy(Map<String, Mapper> mappers, Connection conn) {
this.mappers = mappers;
this.conn = conn;
}
public MapperProxy(Map<String, Mapper> mappers) {
this.mappers = mappers;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//获取方法名
String methodName = method.getName();
//获取方法所在的类名
String className = method.getDeclaringClass().getName();
String key = className+"."+methodName;
Mapper mapper = mappers.get(key);
if (mapper == null){
throw new IllegalArgumentException("传入参数有误");
}
return new Executor().selectList(mapper,conn);
}
}