摘要:三大巨头结果集再通过反射机制映射到对象上面,便做好了数据的映射关于映射具体内容可查阅资料及源码到这我们已经完成了一个简易的框架了通过手写一个简单的框架,我们就可以看得懂源码了,学习框架设计的思路并且增强我们的内功
简化版Mybatis实现思路
1.创建SqlSessionFactory实例.
2.实例化过程中,加载配置文件创建configuration对象.
3.通过factory创建SqlSession对象,把configuaration传入SqlSession.
4.通过SqlSession获取mapper接口动态代理
5.通过代理对调sqlsession中查询方法;
6.sqlsession将查询方法转发给executor;
7.executor基于JDBC访问数据库获取数据;
8.executor通过反射将数据转换成POJO并返回给sqlsession;
9.数据返回给调用者
上节讲到快速入门mybatis的demo三大阶段
// 1.读取mybatis配置文件创SqlSessionFactory String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); inputStream.close(); //-------------第二阶段------------- // 2.获取sqlSession SqlSession sqlSession = sqlSessionFactory.openSession(); // 3.获取对应mapper TUserMapper mapper = sqlSession.getMapper(TUserMapper.class); //-------------第三阶段------------- // 4.执行查询语句并返回结果 TUser user = mapper.selectByPrimaryKey(1); System.out.println(user.toString());第一阶段:
第一阶段先把配置文件加载到内存,包括数据库信息和mapper.xml。
针对mapper.xml我们定义一个MappedStatement类来存入相应信息.
public class MappedStatement { //此处忽略getset方法 private String namespace;//xml里面的namespace即mapper接口路径 private String sourceId;//mapper接口路径+xml里面的每一个id private String sql;//sql语句 private String resultType;//返回类型 }
再定义一个全局配置信息即Configuration存放所有配置信息:
public class Configuration { //记录mapper xml文件存放的位置 public static final String MAPPER_CONFIG_LOCATION = "config"; //记录数据库连接信息文件存放位置 public static final String DB_CONFIG_FILE = "db.properties"; private String dbUrl; private String dbUserName; private String dbPassword; private String dbDriver; //mapper xml解析完以后select节点的信息存放在mappedStatements,key为MappedStatement里面 //的sourceId protected final MapmappedStatements = new HashMap (); //为mapper接口生成动态代理的方法 public T getMapper(Class type, SqlSession sqlSession) { return MapperProxyFactory.getMapperProxy(sqlSession, type); } }
SqlSessionFactory实例化,并加载configuaration对象信息,这样就把所有的配置信息加载到内存里
public class SqlSessionFactory { //配置对象全局唯一 加载数据库信息和mapper文件信息 private Configuration conf = new Configuration(); public SqlSessionFactory() { //加载数据库信息 loadDbInfo(); //加载mapper文件信息 loadMappersInfo(); } private void loadMappersInfo() { URL resources =null; resources = SqlSessionFactory.class.getClassLoader().getResource(conf.MAPPER_CONFIG_LOCATION); File mappers = new File(resources.getFile()); if(mappers.isDirectory()){ File[] listFiles = mappers.listFiles(); for (File file : listFiles) { loadMapperInfo(file); } } } private void loadMapperInfo(File file) { // 创建saxReader对象 SAXReader reader = new SAXReader(); // 通过read方法读取一个文件 转换成Document对象 Document document=null; try { document = reader.read(file); } catch (DocumentException e) { // TODO Auto-generated catch block e.printStackTrace(); } //获取根节点元素对象 Element node = document.getRootElement(); //获取命名空间 String namespace = node.attribute("namespace").getData().toString(); //获取select子节点列表 List第二阶段selects = node.elements("select"); for (Element element : selects) {//遍历select节点,将信息记录到MappedStatement对象,并登记到configuration对象中 MappedStatement mappedStatement = new MappedStatement(); String id = element.attribute("id").getData().toString(); String resultType = element.attribute("resultType").getData().toString(); String sql = element.getData().toString(); String sourceId = namespace+"."+id; mappedStatement.setSourceId(sourceId); mappedStatement.setResultType(resultType); mappedStatement.setSql(sql); mappedStatement.setNamespace(namespace); conf.getMappedStatements().put(sourceId, mappedStatement);//登记到configuration对象中 } } private void loadDbInfo() { InputStream dbIn = SqlSessionFactory.class.getClassLoader().getResourceAsStream(conf.DB_CONFIG_FILE); Properties p = new Properties(); try { p.load(dbIn); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } conf.setDbDriver(p.get("jdbc.driver").toString()); conf.setDbPassword(p.get("jdbc.password").toString()); conf.setDbUrl(p.get("jdbc.url").toString()); conf.setDbUserName(p.get("jdbc.username").toString()); } public SqlSession openSession(){ SqlSession sqlSession = new DefaultSqlSession(conf); return sqlSession; } }
第二阶段为获取Sqlsession并且从sqlsession获取mapper动态代理.
Sqlsession
mybatis暴露给外部的接口,实现增删改查的能力
1.对外提供数据访问的api
2.对内将请求转发给executor
3.executor基于JDBC访问数据库
public class DefaultSqlSession implements SqlSession { //配置对象全局唯一 加载数据库信息和mapper文件信息 private Configuration conf; //真正提供数据库访问能力的对象 private Executor executor; public DefaultSqlSession(Configuration conf) { super(); this.conf = conf; executor = new SimpleExecutor(conf); } publicT selectOne(String statement, Object parameter) { List
Executor是Mybatis核心接口定义了数据库操作的基本方法,Sqlsession都是基于它来实现的
public interface Executor {List query(MappedStatement ms, Object parameter) throws SQLException; T selectOne(String statement,Object parameter);
}
Executor实现类:
public class SimpleExecutor implements Executor { private Configuration conf; public SimpleExecutor(Configuration conf) { this.conf = conf; } publicList query(MappedStatement ms, Object parameter) throws SQLException { //获取mappedStatement对象,里面包含sql语句和目标对象等信息; MappedStatement mappedStatement = conf.getMappedStatement(ms.getSourceId()); //1.获取Connection对象 Connection conn = getConnect(); //2.实例化StatementHandler对象,准备实例化Statement StatementHandler statementHandler = new DefaultStatementHandler(mappedStatement); //3.通过statementHandler和Connection获取PreparedStatement PreparedStatement prepare = statementHandler.prepare(conn); //4.实例化ParameterHandler对象,对Statement中sql语句的占位符进行处理 ParameterHandler parameterHandler = new DefaultParameterHandler(parameter); parameterHandler.setParameters(prepare); //5.执行查询语句,获取结果集resultSet ResultSet resultSet = statementHandler.query(prepare); //6.实例化ResultSetHandler对象,对resultSet中的结果集进行处理,转化成目标对象 ResultSetHandler resultSetHandler = new DefaultResultSetHandler(mappedStatement); return resultSetHandler.handleResultSets(resultSet); } @Override public T selectOne(String statement, Object parameter) { MappedStatement mappedStatement =conf.getMappedStatements().get(statement); return null; } private Connection getConnect() { Connection conn =null; try { Class.forName(conf.getDbDriver()); conn = DriverManager.getConnection(conf.getDbUrl(), conf.getDbUserName(), conf.getDbPassword()); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } return conn; } public Configuration getConf() { return conf; } public void setConf(Configuration conf) { this.conf = conf; } }
mapper接口在我们工程里面没有实现类,是通过动态代理来执行方法的.
/** * mapper接口生成动态代理的工程类 * */ public class MapperProxyFactory{ public static T getMapperProxy(SqlSession sqlSession,Class mapperInterface){ MapperProxy mapperProxy = new MapperProxy (sqlSession, mapperInterface); return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy); }
InvocationHandler实现类:
public class MapperProxy第三阶段implements InvocationHandler { private SqlSession sqlSession; private final Class mapperInterface; public MapperProxy(SqlSession sqlSession, Class mapperInterface) { super(); this.sqlSession = sqlSession; this.mapperInterface = mapperInterface; } private boolean isCollection(Class type) { return Collection.class.isAssignableFrom(type); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (Object.class.equals(method.getDeclaringClass())) {// 如果是Object本身的方法不增强 return method.invoke(this, args); } Class> returnType = method.getReturnType();// 获取方法的返回参数class对象 Object ret = null; if (isCollection(returnType)) {// 根据不同的返回参数类型调用不同的sqlsession不同的方法 ret = sqlSession.selectList(mapperInterface.getName()+"."+ method.getName(), args); } else { ret = sqlSession.selectOne(mapperInterface.getName()+"."+ method.getName(), args); } return ret; } }
第三阶段执行查询并返回结果.刚刚讲过我们执行数据库操作实际上是executor基于jdbc执行的。
jdbc三大巨头,Connection,PreparedStatement,ResultSet,
结果集Result再通过反射机制映射到对象上面,便做好了数据的映射(关于映射具体内容可查阅资料及源码),到这我们已经完成了一个简易的Mybatis框架了.
通过手写一个简单的Mybatis框架,我们就可以看得懂源码了,学习框架设计的思路并且增强我们Java的内功.
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/74470.html
摘要:核心流程三大阶段缕清思路也就是核心流程之后,我们就开始写代码了,详见下节 Mybatis快速入门 步骤: 1.加入mybatis的依赖 2.添加Mybatis的配置文件 3.场景介绍 4.编写实体类丶mapper接口及mapper.xml文件 5.编写测试代码 demo: public class TUser { private Integer id; priva...
摘要:通过整合及可以实现数据库查询后将数据持久化。但是可能出现幻像读这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行。 所需技术:spring、mybatis、druid、flyway、logback、nodejs、html、css3 ;目标:创建一个业务框架,后端采用spring+mybatis,中间层采用node,前端html5,css3等; showImg(https:/...
摘要:基本纲要组成动态配置配置核心源码分析源码解析源码解析源码解析源码解析手写框架是什么本质是一种半自动的框架,前身是其源于和的组合,除了和映射关系之外,还需要编写语句映射三要素映射规则快速入门加入的依赖添加的配置文件场景介绍编写实体类接口以及文 showImg(https://segmentfault.com/img/bVblrnC); Mybatis基本纲要 Mybatis组成 · 动态...
摘要:前言嗨,小伙伴们,这篇博文将带大家手写,让大家对的核心原理以及工作流程有更加深刻的理解。模块顾名思义,就是框架配置类,用于解析配置文件加载相关环境。配置模块这里的对框架的配置使用了简单的,主要原因还是简单易懂然后节省时间。 前言 (。・∀・)ノ゙嗨,小伙伴们,这篇博文将带大家手写mybatis,让大家对mybaits的核心原理以及工作流程有更加深刻的理解。在上篇Spring-Mybat...
阅读 632·2021-08-17 10:15
阅读 1715·2021-07-30 14:57
阅读 1970·2019-08-30 15:55
阅读 2813·2019-08-30 15:55
阅读 2703·2019-08-30 15:44
阅读 662·2019-08-30 14:13
阅读 2380·2019-08-30 13:55
阅读 2587·2019-08-26 13:56