笔记1:简易的持久层框架

笔记1:简易的持久层框架

思维逻辑:
在这里插入图片描述

  1. 前提为创建好的xml配置文件:sqlMapConfig.xml 以及IUserDao.java

    <configuration>
        <dataSource>
            <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
            <property name="jdbcUrl" value="jdbc:mysql:///lg"></property>
            <property name="username" value="root"></property>
            <property name="password" value="123456"></property>
        </dataSource>
    	
    	<!-- 指定Mapper路径,方便读取 --> 
        <mapper resource="UserMapper.xml"></mapper>
    </configuration>
    
    public interface IUserDao {
          
          
    
        List<User> findAll();
    
        User findByCondition(User user);
    
        void insert(User user);
    
        void update(User user);
    
        void deleteById(Integer id);
    }
    

    pom依赖:

    	<dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.17</version>
        </dependency>
        <dependency>
            <groupId>c3p0</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.1.2</version>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.12</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.10</version>
        </dependency>
        <dependency>
            <groupId>dom4j</groupId>
            <artifactId>dom4j</artifactId>
            <version>1.6.1</version>
        </dependency>
        <dependency>
            <groupId>jaxen</groupId>
            <artifactId>jaxen</artifactId>
            <version>1.1.6</version>
        </dependency>
    
  2. 开始编写简易的Mybatis, 首先创建SqlSessionFactoryBuilder.java 用于解析xml配置和jdbc操作

    public class Resources {
          
          
    
        public static InputStream getResourceAsStream(String path) {
          
          
            InputStream resourceAsStream = Resources.class.getClassLoader().getResourceAsStream(path);
            return resourceAsStream;
        }
    }
    
    	public class SqlSessionFactoryBuilder {
          
          
    
        public SqlSessionFactory build(InputStream in) throws DocumentException, PropertyVetoException {
          
          
            XmlConfigBuilder xmlConfigBuilder = new XmlConfigBuilder();
            Configuration configuration = xmlConfigBuilder.parseConfig(in);
    
            return new DefaultSqlSessionFactory(configuration);
        }
    
    }
    
  3. XmlConfigBuilder.java解析xml配置,将解析出来的参数存入实体类Configuration.java及MappedStatement.java

    	public class Configuration {
          
          
    
        private DataSource dataSource;
    
        /**
         * key: statementId -> namespace.id
         */
        Map<String, MappedStatement> mappedStatementMap = new HashMap<>(16);
    
        public DataSource getDataSource() {
          
          
            return dataSource;
        }
    
        public void setDataSource(DataSource dataSource) {
          
          
            this.dataSource = dataSource;
        }
    
        public Map<String, MappedStatement> getMappedStatementMap() {
          
          
            return mappedStatementMap;
        }
    
        public void setMappedStatementMap(Map<String, MappedStatement> mappedStatementMap) {
          
          
            this.mappedStatementMap = mappedStatementMap;
        }
    }
    
    public class MappedStatement {
          
          
        private String id;
        private String resultType;
        private String parameterType;
        private String sql;
    
        public String getId() {
          
          
            return id;
        }
    
        public void setId(String id) {
          
          
            this.id = id;
        }
    
        public String getResultType() {
          
          
            return resultType;
        }
    
        public void setResultType(String resultType) {
          
          
            this.resultType = resultType;
        }
    
        public String getParameterType() {
          
          
            return parameterType;
        }
    
        public void setParameterType(String parameterType) {
          
          
            this.parameterType = parameterType;
        }
    
        public String getSql() {
          
          
            return sql;
        }
    
        public void setSql(String sql) {
          
          
            this.sql = sql;
        }
    }
    
    public class XmlConfigBuilder {
          
          
    
        private final Configuration configuration;
    
        public XmlConfigBuilder() {
          
          
            this.configuration = new Configuration();
        }
    
        public Configuration parseConfig(InputStream in) throws DocumentException, PropertyVetoException {
          
          
            Document document = new SAXReader().read(in);
            Element rootElement = document.getRootElement();
            List<Element> list = rootElement.selectNodes("//property");
            Properties properties = new Properties();
            list.forEach(element -> {
          
          
                String name = element.attributeValue("name");
                String value = element.attributeValue("value");
                properties.setProperty(name, value);
            });
    
            ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
            comboPooledDataSource.setDriverClass(properties.getProperty("driverClass"));
            comboPooledDataSource.setJdbcUrl(properties.getProperty("jdbcUrl"));
            comboPooledDataSource.setUser(properties.getProperty("username"));
            comboPooledDataSource.setPassword(properties.getProperty("password"));
    
            configuration.setDataSource(comboPooledDataSource);
    
            List<Element> mapperList = rootElement.selectNodes("//mapper");
            for (Element element : mapperList) {
          
          
                String mapperPath = element.attributeValue("resource");
                InputStream mapperIn = Resources.getResourceAsStream(mapperPath);
                XmlMapperBuilder xmlMapperBuilder = new XmlMapperBuilder(configuration);
                xmlMapperBuilder.parse(mapperIn);
            }
    
            return configuration;
        }
    }
    
    public class XmlMapperBuilder {
          
          
    
        private final Configuration configuration;
    
        public XmlMapperBuilder(Configuration configuration) {
          
          
            this.configuration = configuration;
        }
    
        public void parse(InputStream in) throws DocumentException {
          
          
            Document document = new SAXReader().read(in);
            Element rootElement = document.getRootElement();
            String namespace = rootElement.attributeValue("namespace");
            List<Element> list = new ArrayList<>();
            List<Element> selectList = rootElement.selectNodes("//select");
            List<Element> insertList = rootElement.selectNodes("//insert");
            List<Element> updateList = rootElement.selectNodes("//update");
            List<Element> deleteList = rootElement.selectNodes("//delete");
            if(selectList != null) {
          
          
                list.addAll(selectList);
            }
            if(insertList != null) {
          
          
                list.addAll(insertList);
            }
            if(updateList != null) {
          
          
                list.addAll(updateList);
            }
            if(deleteList != null) {
          
          
                list.addAll(deleteList);
            }
            list.forEach(element -> {
          
          
                String id = element.attributeValue("id");
                String resultType = element.attributeValue("resultType");
                String parameterType = element.attributeValue("parameterType");
                String sql = element.getTextTrim();
                MappedStatement mappedStatement = new MappedStatement();
                mappedStatement.setId(id);
                mappedStatement.setParameterType(parameterType);
                mappedStatement.setResultType(resultType);
                mappedStatement.setSql(sql);
                configuration.getMappedStatementMap().put(namespace+"."+id, mappedStatement);
            });
        }
    }
    
  4. 解析好xml配置后,将参数传递并构造sql

    public interface SqlSessionFactory {
          
          
    
        public SqlSession openSession();
    }
    
    public class DefaultSqlSessionFactory implements SqlSessionFactory {
          
          
    
        private final Configuration configuration;
    
        public DefaultSqlSessionFactory(Configuration configuration) {
          
          
            this.configuration = configuration;
        }
    
    
        @Override
        public SqlSession openSession() {
          
          
            return new DefaultSqlSession(configuration);
        }
    
    	public interface SqlSession {
          
          
    
        <E> List<E> selectList(String statementId, Object... params) throws Exception;
    
        <T> T selectOne(String statementId, Object... params) throws Exception;
    
        <T> T getMapper(Class<?> mapperClass);
    
        Object insert(String statementId, Object... params) throws Exception;
    
        Object update(String statementId, Object... params) throws Exception;
    
        Object delete(String statementId, Object... params) throws Exception;
    }
    
  5. 然后在DefaultSqlSession里进行jdbc方法的实现,这里的getMapper为动态代理,用于IUserDao.java的调用

    public class DefaultSqlSession implements SqlSession {
          
          
    
        private final Configuration configuration;
    
        public DefaultSqlSession(Configuration configuration) {
          
          
            this.configuration = configuration;
        }
    
        @Override
        public <E> List<E> selectList(String statementId, Object... params) throws Exception {
          
          
            SimpleExecutor simpleExecutor = new SimpleExecutor();
            MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementId);
            List<Object> queryList = simpleExecutor.query(configuration, mappedStatement, params);
            return (List<E>) queryList;
        }
    
        @Override
        public <T> T selectOne(String statementId, Object... params) throws Exception {
          
          
            List<Object> objects = selectList(statementId, params);
            if(objects == null || objects.size() > 1) {
          
          
                throw new RuntimeException("结果为空或结果过多");
            } else {
          
          
                return (T) objects.get(0);
            }
        }
    
        @Override
        public Object insert(String statementId, Object... params) throws Exception {
          
          
            return update(statementId, params);
        }
    
        @Override
        public Object update(String statementId, Object... params) throws Exception {
          
          
            SimpleExecutor simpleExecutor = new SimpleExecutor();
            MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementId);
            return simpleExecutor.update(configuration, mappedStatement, params);
        }
    
        @Override
        public Object delete(String statementId, Object... params) throws Exception {
          
          
            SimpleExecutor simpleExecutor = new SimpleExecutor();
            MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementId);
            return simpleExecutor.delete(configuration, mappedStatement, params);
        }
    
        @Override
        public <T> T getMapper(Class<?> mapperClass) {
          
          
            //使用JDK动态代理来为Dao接口生成代理对象,并返回
            Object proxyInstance = Proxy.newProxyInstance(DefaultSqlSession.class.getClassLoader(), new Class[]{
          
          mapperClass}, new InvocationHandler() {
          
          
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
          
          
                    // 底层都还是去执行JDBC代码 //根据不同情况,来调用selctList或者selectOne
                    // 准备参数 1:statmentid :sql语句的唯一标识:namespace.id= 接口全限定名.方法名
                    // 方法名:findAll
                    String methodName = method.getName();
                    String className = method.getDeclaringClass().getName();
    
                    String statementId = className + "." + methodName;
    
                    // 准备参数2:params:args
                    // 获取被调用方法的返回值类型
                    Type genericReturnType = method.getGenericReturnType();
                    MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementId);
                    String sql = mappedStatement.getSql();
                    if (genericReturnType instanceof ParameterizedType) {
          
          
                        return selectList(statementId, args);
                    } else if(sql.startsWith("update")) {
          
          
                        return update(statementId, args);
                    } else if(sql.startsWith("insert")) {
          
          
                        return insert(statementId, args);
                    } else if(sql.startsWith("delete")) {
          
          
                        return delete(statementId, args);
                    } else {
          
          
                        return selectOne(statementId, args);
                    }
                }
            });
            return (T) proxyInstance;
        }
    }
    
  6. 对应数据库操作的实现

    public interface Executor {
          
          
        <E> List<E> query(Configuration configuration, MappedStatement mappedStatement, Object... params) throws Exception;
    
        Integer update(Configuration configuration, MappedStatement mappedStatement, Object[] params) throws Exception;
    
        Integer delete(Configuration configuration, MappedStatement mappedStatement, Object[] params) throws Exception;
    }
    
    public class BoundSql {
          
          
        private String sqlText;
        private List<ParameterMapping> parameterMappings = new ArrayList<>();
    
        public BoundSql(String sqlText, List<ParameterMapping> parameterMappings) {
          
          
            this.sqlText = sqlText;
            this.parameterMappings = parameterMappings;
        }
    
        public String getSqlText() {
          
          
            return sqlText;
        }
    
        public void setSqlText(String sqlText) {
          
          
            this.sqlText = sqlText;
        }
    
        public List<ParameterMapping> getParameterMappings() {
          
          
            return parameterMappings;
        }
    
        public void setParameterMappings(List<ParameterMapping> parameterMappings) {
          
          
            this.parameterMappings = parameterMappings;
        }
    }
    
    public class SimpleExecutor implements Executor {
          
          
        @Override
        public <E> List<E> query(Configuration configuration, MappedStatement mappedStatement, Object... params) throws Exception {
          
          
            //创建链接
            Connection connection = configuration.getDataSource().getConnection();
    
            String sql = mappedStatement.getSql();
            BoundSql boundSql = getBoundSql(sql);
    
            PreparedStatement preparedStatement = connection.prepareStatement(boundSql.getSqlText());
    
            //设置参数
            String parameterType = mappedStatement.getParameterType();
            Class<?> parameterTypeClass = getClassType(parameterType);
            if(parameterTypeClass != null) {
          
          
                List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
                for (int i = 0; i < parameterMappings.size(); i++) {
          
          
                    ParameterMapping parameterMapping = parameterMappings.get(i);
                    String content = parameterMapping.getContent();
    
                    Field declaredField = parameterTypeClass.getDeclaredField(content);
                    declaredField.setAccessible(true);
                    Object o = declaredField.get(params[0]);
    
                    preparedStatement.setObject(i + 1, o);
                }
            }
    
            //执行
            ResultSet resultSet = preparedStatement.executeQuery();
    
            //封装返回结果
            List<Object> resultList = new ArrayList<>();
            String resultType = mappedStatement.getResultType();
            Class<?> resultTypeClass = getClassType(resultType);
            Object o;
            ResultSetMetaData metaData = resultSet.getMetaData();
            if(resultTypeClass != null) {
          
          
                while (resultSet.next()) {
          
          
                    o = resultTypeClass.newInstance();
                    for (int i = 1; i < metaData.getColumnCount(); i++) {
          
          
                        String columnName = metaData.getColumnName(i);
                        PropertyDescriptor propertyDescriptor = new PropertyDescriptor(columnName, resultTypeClass);
                        Method writeMethod = propertyDescriptor.getWriteMethod();
                        Object value = resultSet.getObject(columnName);
                        writeMethod.invoke(o, value);
                    }
                    resultList.add(o);
                }
            }
    
            return (List<E>) resultList;
        }
    
        @Override
        public Integer update(Configuration configuration, MappedStatement mappedStatement, Object[] params) throws Exception {
          
          
            //创建链接
            Connection connection = configuration.getDataSource().getConnection();
    
            String sql = mappedStatement.getSql();
            BoundSql boundSql = getBoundSql(sql);
    
            PreparedStatement preparedStatement = connection.prepareStatement(boundSql.getSqlText());
    
            //设置参数
            String parameterType = mappedStatement.getParameterType();
            Class<?> parameterTypeClass = getClassType(parameterType);
            if(parameterTypeClass != null) {
          
          
                List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
                ParameterMapping parameterMapping;
                for (int i = 0; i < parameterMappings.size(); i++) {
          
          
                    parameterMapping = parameterMappings.get(i);
                    String content = parameterMapping.getContent();
    
                    Field declaredField = parameterTypeClass.getDeclaredField(content);
                    declaredField.setAccessible(true);
                    Object value = declaredField.get(params[0]);
    
                    preparedStatement.setObject(i + 1, value);
                }
    
                return preparedStatement.executeUpdate();
    
            } else {
          
          
                throw new RuntimeException(mappedStatement.getId() + ": parameterType未定义");
            }
        }
    
        @Override
        public Integer delete(Configuration configuration, MappedStatement mappedStatement, Object[] params) throws Exception {
          
          
            //创建链接
            Connection connection = configuration.getDataSource().getConnection();
    
            String sql = mappedStatement.getSql();
            BoundSql boundSql = getBoundSql(sql);
    
            PreparedStatement preparedStatement = connection.prepareStatement(boundSql.getSqlText());
    
            preparedStatement.setObject(1, params[0]);
    
            return preparedStatement.executeUpdate();
        }
    
        private Class<?> getClassType(String parameterType) throws ClassNotFoundException {
          
          
            if(!StringUtils.isNullOrEmpty(parameterType)) {
          
          
                return Class.forName(parameterType);
            }
    
            return null;
        }
    
        private BoundSql getBoundSql(String sql) {
          
          
            ParameterMappingTokenHandler handler = new ParameterMappingTokenHandler();
            GenericTokenParser genericTokenParser = new GenericTokenParser("#{", "}", handler);
            String sqlOut = genericTokenParser.parse(sql);
            List<ParameterMapping> parameterMappings = handler.getParameterMappings();
    
            return new BoundSql(sqlOut, parameterMappings);
        }
    }
    

    其中GenericTokenParser为Mybatis的源码里的方法。

  7. 测试用例

    public class IPersistenceTest {
          
          
    
        @Test
        public void test() throws Exception {
          
          
            InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
            SqlSession sqlSession = sqlSessionFactory.openSession();
            
            User user = new User();
            user.setId(3);
            user.setUsername("Jack Ma");
            user.setPassword("123");
            user.setBirthday("2020-12-11");
            IUserDao userDao = sqlSession.getMapper(IUserDao.class);
    
            //insert
            userDao.insert(user);
    
            //update
            user.setPassword("1234");
            userDao.update(user);
    
            //delete
            userDao.deleteById(3);
        }
    }
    

P.S. GenericTokenParser工具:
链接:https://pan.baidu.com/s/1Drec7juYHD1-Uka-v5vQWw
提取码:oukl

猜你喜欢

转载自blog.csdn.net/Lossdate/article/details/111145275
今日推荐