标签:new 测试 enc mon query ref dom4j width conf
最近心血来潮,想加深学习框架实现的思想,所以抽空研究研究Mybatis,Mybatis的核心工作流程实现其实并不是很难。核心都是对反射,动态代理、设计模式以及JDBC的运用。
当然想要很完善的实现,包括各种特性、各种严谨的代码规范,那可能需要花费更多时间去研究源码。这里更多的是通过手写简单版本的mybatis加深理解mybatis的核心工作流程,核心原理。
上图是mybatis的核心工作流程,通过理解其工作流程后,对于自己去实现就会有了一个很好的思路。
mybatis的核心工作流程就是(分为mybatis读取配置的初始阶段和执行jdbc操作的运行阶段):
初始阶段
1、通过SqlSessionFactoryBuilder加载配置,包括了核心配置文件,里面保存与mybatis运行环境有关的全局配置(JDBC配置,Mapper文件位置等)
和Mapper映射文件,里面都是CRUD标签的内容。加载的时候通过相应的解析器,最终将所有的内容以面向对象的思想封装成对象,
最终保存到Configuration对象中。最后生成SqlSessionFactory对象,session工厂对象。
2、另外对于<select/insert/update/delete>的标签解析都会生成MappedStatement对象,里面都封装每条SQL的参数类型,结果类型,statement类型,sql文本等
执行阶段
1、每次进行JDBC操作的时候,会通过SqlsessionFactory对象生成一个SqlSession接口的对象,作为session访问处理。真正的CRUD操作,
它会交给Executor接口的实现类去真正实现。
2、Executor也会将相应的操作交给具体handler去处理,包括statementhandler、parameterhandler、resultsethandler处理。
当然里面无非就是对于jdbc的statement处理输入参数的映射,最后对结果集的映射封装结果而已。
所以在这里也看得出来mybatis算是半ORM框架,具体体现在输入参数和输出结果集的映射处理。
上面的是具体实现的工程目录,里面实现也是基于执行流程的思路去实现的。
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.hjj.mybatis</groupId> <artifactId>simplemybatis</artifactId> <version>0.0.1-SNAPSHOT</version> <dependencies> <dependency> <groupId>dom4j</groupId> <artifactId>dom4j</artifactId> <version>1.6</version> </dependency> <dependency> <groupId>commons-dbcp</groupId> <artifactId>commons-dbcp</artifactId> <version>1.4</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.20</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.0</version> <configuration> <source>1.8</source> <target>1.8</target> <encoding>UTF-8</encoding> </configuration> </plugin> </plugins> </build> </project>
public class SqlSessionFactoryBuilder { public SqlSessionFactory build(InputStream inputStream) { //解析配置,封装Configuration对象, XmlConfigBuilder parser = new XmlConfigBuilder(inputStream); return build(parser.parse()); } public SqlSessionFactory build(Reader reader) { //TO DO return null; } public SqlSessionFactory build(Configuration configuration) { return new DefaultSqlSessionFactory(configuration); } }
public class DefaultSqlSessionFactory implements SqlSessionFactory { private Configuration configuration; public DefaultSqlSessionFactory(Configuration configuration) { super(); this.configuration = configuration; } @Override public SqlSession openSession() { // TODO Auto-generated method stub return new DefaultSqlSession(configuration, null); } }
public class DefaultSqlSession implements SqlSession { private Configuration configuration; private Executor executor; public DefaultSqlSession(Configuration configuration, String executorType) { this.configuration = configuration; if(executorType == null) { this.executor = new SimpleExecutor(); }else { //// } } @Override public <T> T selectOne(String statementId, Object params) throws SQLException { // TODO Auto-generated method stub List<T> list = this.selectList(statementId, params); if (list.size() == 1) { return list.get(0); } else if (list.size() > 1) { throw new RuntimeException( "通过selectOne()方法,期待返回一个结果, 但实际返回结果数为: " + list.size()); } else { return null; } } @Override public <T> List<T> selectList(String statementId, Object params) throws SQLException { // TODO Auto-generated method stub MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementId); return executor.selectList(configuration, mappedStatement, params); } }
public class XmlConfigBuilder { private InputStream inputStream; private Configuration configuration; public XmlConfigBuilder(InputStream inputStream) { super(); this.inputStream = inputStream; this.configuration = new Configuration(); } public Configuration parse(InputStream inputStream) { Document document = DocumentReader.createDocument(inputStream); parseConfiguration(document.getRootElement()); return configuration; } public Configuration parse() { return parse(inputStream); } private void parseConfiguration(Element element) { // TODO Auto-generated method stub //解析<environments> parseEnvironments(element.element("environments")); //解析<mappers> parseMappers(element.element("mappers")); } private void parseEnvironments(Element element) { // TODO Auto-generated method stub String attr = element.attributeValue("default"); List<Element> elements = element.elements("environment"); if(attr != null) { for (Element ele : elements) { String eleId = ele.attributeValue("id"); if(eleId != null && eleId.equals(attr)) { parseDataSource(ele.element("dataSource")); break; } } }else { throw new RuntimeException("environments标签的default属性不能为空"); } } private void parseDataSource(Element element) { // TODO Auto-generated method stub String type = element.attributeValue("type"); if(type == null || type.trim().equals("")) type = "DBCP"; Properties prop = new Properties(); /* for (Element ele : element.elements("property")) { }*/ List<Element> elements = element.elements("property"); for (Element ele : elements) { String name = ele.attributeValue("name"); String value = ele.attributeValue("value"); prop.setProperty(name, value); } //封装数据源对象 BasicDataSource dataSource = null; if (type.equals("DBCP")) { dataSource = new BasicDataSource(); dataSource.setDriverClassName(prop.getProperty("driver")); dataSource.setUrl(prop.getProperty("url")); dataSource.setUsername(prop.getProperty("username")); dataSource.setPassword(prop.getProperty("password")); } configuration.setDataSource(dataSource); } private void parseMappers(Element element) { // TODO Auto-generated method stub List<Element> elements = element.elements("mapper"); for(Element ele : elements) { parseMapper(ele); } } private void parseMapper(Element element) { String resource = element.attributeValue("resource"); InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(resource); XmlMapperBuilder parser = new XmlMapperBuilder(inputStream, configuration); parser.parse(); } }
public class XmlMapperBuilder { private InputStream inputStream; private Configuration configuration; private String namespace; public XmlMapperBuilder(InputStream inputStream, Configuration configuration) { // TODO Auto-generated constructor stub this.inputStream = inputStream; this.configuration = configuration; } public void parse() { // TODO Auto-generated method stub Document document = DocumentReader.createDocument(inputStream); Element element = document.getRootElement(); this.namespace = element.attributeValue("namespace"); if(this.namespace == null || this.namespace.equals("")) { throw new RuntimeException("Mapper的namespace值不能为空"); } //解析所有select标签 parseSelectStatements(element.elements("select")); //解析所有insert标签 parseInsertStatements(element.elements("insert")); } private void parseSelectStatements(List<Element> elements) { for (Element element : elements) { parseSelectStatement(element); } } private void parseSelectStatement(Element element) { // TODO Auto-generated method stub String id = this.namespace + "." + element.attributeValue("id"); Class<?> parameterTypeClass = ClassUtil.getClazz(element.attributeValue("parameterType")); Class<?> resultTypeClass = ClassUtil.getClazz(element.attributeValue("resultType")); String statementType = element.attributeValue("statementType"); SqlSource sqlSource = new SqlSource(element.getTextTrim()); MappedStatementBuilder msBuilder = new MappedStatementBuilder(); MappedStatement mappedStatement = msBuilder.id(id) .parameterTypeClass(parameterTypeClass) .resultTypeClass(resultTypeClass) .statementType(statementType) .sqlSource(sqlSource) .builder(); configuration.addMappedStatement(id, mappedStatement); } private void parseInsertStatements(List<Element> elements) { // TODO Auto-generated method stub } }
public class Configuration { private BasicDataSource dataSource; private Map<String, MappedStatement> mappedStatementMap = new HashMap<>(); public BasicDataSource getDataSource() { return dataSource; } public void setDataSource(BasicDataSource dataSource) { this.dataSource = dataSource; } public Map<String, MappedStatement> getMappedStatementMap() { return mappedStatementMap; } public void addMappedStatement(String statementId, MappedStatement mappedStatement) { this.mappedStatementMap.put(statementId, mappedStatement); } }
public class MappedStatement { private String id; private Class<?> parameterTypeClass; private Class<?> resultTypeClass; private String statementType; private SqlSource sqlSource; public MappedStatement() { } public MappedStatement(String id, Class<?> parameterTypeClass, Class<?> resultTypeClass, String statementType, SqlSource sqlSource) { super(); this.id = id; this.parameterTypeClass = parameterTypeClass; this.resultTypeClass = resultTypeClass; this.statementType = statementType; this.sqlSource = sqlSource; } public String getId() { return id; } public void setId(String id) { this.id = id; } public Class<?> getParameterTypeClass() { return parameterTypeClass; } public void setParameterTypeClass(Class<?> parameterTypeClass) { this.parameterTypeClass = parameterTypeClass; } public Class<?> getResultTypeClass() { return resultTypeClass; } public void setResultTypeClass(Class<?> resultTypeClass) { this.resultTypeClass = resultTypeClass; } public String getStatementType() { return statementType; } public void setStatementType(String statementType) { this.statementType = statementType; } public SqlSource getSqlSource() { return sqlSource; } public void setSqlSource(SqlSource sqlSource) { this.sqlSource = sqlSource; } }
ublic class MappedStatementBuilder { private MappedStatement mappedStatement = new MappedStatement(); public MappedStatementBuilder id(String id) { mappedStatement.setId(id); return this; } public MappedStatementBuilder parameterTypeClass(Class<?> parameterTypeClass) { mappedStatement.setParameterTypeClass(parameterTypeClass); return this; } public MappedStatementBuilder resultTypeClass(Class<?> resultTypeClass) { mappedStatement.setResultTypeClass(resultTypeClass); return this; } public MappedStatementBuilder statementType(String statementType) { mappedStatement.setStatementType(statementType); return this; } public MappedStatementBuilder sqlSource(SqlSource sqlSource) { mappedStatement.setSqlSource(sqlSource); return this; } public MappedStatement builder() { return mappedStatement; } }
public class SqlSource { private String sqlText; public SqlSource(String sqlText) { super(); this.sqlText = sqlText; } public String getSqlText() { return sqlText; } public void setSqlText(String sqlText) { this.sqlText = sqlText; } public BoundSql getBoundSql() { //这是从mybatis源码中直接获得的工具类,用于解析sql获得原始的sql语句 ParameterMappingTokenHandler tokenHandler = new ParameterMappingTokenHandler(); GenericTokenParser parser = new GenericTokenParser("#{", "}", tokenHandler); //封装原始的sql以及参数列表名称 return new BoundSql(parser.parse(sqlText), tokenHandler.getParameterMappings()); } }
public class BoundSql { private String originalSql; private List<ParameterMapping> parameterMappings = new ArrayList<>(); public BoundSql(String originalSql, List<ParameterMapping> parameterMappings) { super(); this.originalSql = originalSql; this.parameterMappings = parameterMappings; } public String getOriginalSql() { return originalSql; } public void setOriginalSql(String originalSql) { this.originalSql = originalSql; } public List<ParameterMapping> getParameterMappings() { return parameterMappings; } public void addParameterMapping(ParameterMapping parameterMapping) { this.parameterMappings.add(parameterMapping); } }
public class SimpleExecutor implements Executor { @Override public <T> List<T> selectList(Configuration configuration, MappedStatement mappedStatement, Object params) throws SQLException { // TODO Auto-generated method stub Connection connection = getConnection(configuration); return handleStatementSelect(connection, mappedStatement, params); } private Connection getConnection(Configuration configuration) throws SQLException { if(configuration == null || configuration.getDataSource() == null) { throw new RuntimeException("Configuration对象或者DataSource对象不能为null"); } return configuration.getDataSource().getConnection(); } private <E> List<E> handleStatementSelect(Connection connection, MappedStatement mappedStatement, Object params) throws SQLException { String statementType = mappedStatement.getStatementType(); List<Object> result = null; //不同statementType会有不同操作 if("prepared".equals(statementType)) { result = handlePreparedStatementSelect(connection, mappedStatement, params); }else { //other .... } return (List<E>) result; } /** * JDBC操作 * @param connection * @param mappedStatement * @param params * @throws SQLException */ private <E> List<E> handlePreparedStatementSelect(Connection connection, MappedStatement mappedStatement, Object params) throws SQLException { // TODO Auto-generated method stub List<Object> result = new ArrayList<Object>(); try { BoundSql boundSql = mappedStatement.getSqlSource().getBoundSql(); String sql = boundSql.getOriginalSql(); PreparedStatement preparedStatement = connection.prepareStatement(sql); //获得参数类型 Class<?> parameterTypeClass = mappedStatement.getParameterTypeClass(); //获得参数名称 List<ParameterMapping> parameterMappings = boundSql.getParameterMappings(); if("BASIC".equals(getParamenteTypeString(parameterTypeClass))) { preparedStatement.setObject(1, params); }else { //Object for (int i = 0; i < parameterMappings.size(); i++) { ParameterMapping parameterMapping = parameterMappings.get(i); String name = parameterMapping.getName(); Object fieldValue = ClassUtil.getPrivateFieldValue(parameterTypeClass, name, params); preparedStatement.setObject(i+1, fieldValue); } Class<?> resultTypeClass = mappedStatement.getResultTypeClass(); ResultSet resultSet = preparedStatement.executeQuery(); while(resultSet.next()) { //遍历封装Result Object newInstance = resultTypeClass.newInstance(); int columnCount = resultSet.getMetaData().getColumnCount(); for(int i = 1; i <= columnCount; i++) { String columnName = resultSet.getMetaData().getColumnName(i); Field field = resultTypeClass.getDeclaredField(columnName); field.setAccessible(true); field.set(newInstance, resultSet.getObject(columnName)); } result.add(newInstance); } } } catch (Exception e) { // TODO: handle exception } return (List<E>) result; } private String getParamenteTypeString(Class<?> parameterTypeClass) { //八种基本类型以及String类型 if(parameterTypeClass == Integer.class || parameterTypeClass == Double.class || parameterTypeClass == String.class) { return "BASIC"; }else { //.....Map和List以及Object的处理,这里先处理Object,有待扩展 return "OBJECT"; } } }
<configuration> <environments default="dev"> <environment id="dev"> <dataSource type="DBCP"> <property name="driver" value="com.mysql.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://ali_server01:3306/mybatis_study"></property> <property name="username" value="root"></property> <property name="password" value="root"></property> </dataSource> </environment> </environments> <mappers> <mapper resource="mapper/UserMapper.xml"></mapper> </mappers> </configuration>
<mapper namespace="basic"> <select id="selectOne" parameterType="com.hjj.mybatis.framework.pojo.User" resultType="com.hjj.mybatis.framework.pojo.User" statementType="prepared"> SELECT * FROM user WHERE id = #{id} </select> </mapper>
public class UserDaoImpl implements UserDao { private SqlSessionFactory sqlSessionFactory; public UserDaoImpl(SqlSessionFactory sqlSessionFactory) { super(); this.sqlSessionFactory = sqlSessionFactory; } public User selectOne(User user) { // TODO Auto-generated method stub try { return sqlSessionFactory.openSession().selectOne("basic.selectOne", user); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } return null; } }
public class SimpleTest { private SqlSessionFactory sqlSessionFactory; @Before public void init() { String xmlConfig = "sqlConfig.xml"; InputStream is = this.getClass().getClassLoader().getResourceAsStream(xmlConfig); sqlSessionFactory = new SqlSessionFactoryBuilder().build(is); } @Test public void testSelectOne() { User param = new User(); param.setId(1); User user = new UserDaoImpl(sqlSessionFactory).selectOne(param); System.out.println("user: " + user.toString()); } }
本文作者:JaySpace
原文出处:https://www.cnblogs.com/jayhou/p/12686986.html
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。
标签:new 测试 enc mon query ref dom4j width conf
原文地址:https://www.cnblogs.com/jayhou/p/12686986.html