自定义持久层框架

自定义持久层框架

下图是JDBC引起的一系列问题以及解决办法:

 

 

自定义持久层框架设计思路:


使用端(项目):引入自定义持久层框架jar包。

提供两部分配置信息:1,数据库配置信息;2,sql配置信息--(sql语句、参数类型、返回值类型)
解决办法:使用配置文件来提供两部分配置信息:
<1>sqlMapConfig.xml:放数据库配置信息;在sqlMapConfig.xml中,其实也可以存放mapper.xml的全路径,方法getResourceAsSteam()可以一次性全部读取;
<2>mapper.xml:存放sql配置信息;

自定义持久层框架本身(工程):本质上就是对JDBC代码进行了封装。

(1)加载配置文件,根据配置文件的路径,加载配置文件成字节输入流,存储在内存中;
创建Resource类
方法:getResourceAsSteam(String path)返回 InputSteam;
(2)创建两个javaBean(容器对象):存放的就是对配置文件解析出来的内容,如下:
Configruation核心配置类:存放sqlMapConfig.xml解析出来的内容;
MappedStatement映射配置类:存放mapper.xml解析出来的内容;    
(3)解析配置文件,可以采用dom4j对配置文件进行解析;
创建类:SqlSessionFactoryBuilder,方法:build(InputSteam is)
<1>使用dom4j解析配置文件,将解析出来的内容封装到容器对象中;
<2>创建SqlSessionFactory对象;主要作用就是利用工厂模式生产sqlSession(会话对象)
(4)基于开闭原则创建SqlSessionFactory接口及实现类DefaultSqlSessionFactory
<1>生产sqlSession【openSqlSession()】
(5)创建SqlSession接口及实现类DefaultSession
定义对数据库的crud操作:selectList()selectOne()update()delete()
(6)创建Exeutor接口及实现类SimpleExeutor实现类,执行的就是JDBC代码;
query(Configruation,MappedStatement,Object ... params);

 

创建两个maven工程IPersistence和IPersistence_test

--IPersistence_test引入IPersistence依赖--

    <groupId>com.yun</groupId>
  <artifactId>IPersistence_test</artifactId>
  <version>1.0-SNAPSHOT</version>

  <!--引入自定义持久层框架依赖-->
  <dependencies>
      <dependency>
          <groupId>com.yun</groupId>
          <artifactId>IPersistence</artifactId>
          <version>1.0-SNAPSHOT</version>
      </dependency>
  </dependencies>

--IPersistence_test-->sqlMapConfig.xml--
<configuration>
    <!--数据库配置信息-->
    <dataSource>
        <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
        <property name="jdbcUrl" value="jdbc:mysql://xxx.xxx.xx.xxx:xxxx/xxxx"></property>
        <property name="username" value="xxxx"></property>
        <property name="password" value="xxxx"></property>
    </dataSource>

    <!--存放mapper.xml的全路径-->
    <mapper resource="userMapper.xml"></mapper>

</configuration>
--IPersistence_test-->userMapper.xml--
<mapper namespace="user">

    <!--sql的唯一标识应该是由 namespace.id来组成(statementId)-->
    <select id="selectList" resultType="com.yun.pojo.User">
        select * from user
    </select>

    <!--利用反射获取到user对象的参数-->
    <select id="selectOne" resultType="com.yun.pojo.User" paramterType="com.yum.pojo.User">
        select * from user where id = #{id} and username = #{username}
    </select>

</mapper>

--IPersistence--
@Data
public class MappedStatement {

    //id
    private Integer id;

    //返回值类型
    private String resultType;

    //参数值类型
    private String paramterType;

    //sql语句
    private String sql;
}
--IPersistence--
@Data
public class Configuration {

    private DataSource dataSource;

    /**
     * k:statementId
     * v:封装好的MappedStatement对象
     */
    Map<String,MappedStatement> map = new HashMap<>();
}

按照设计思路编写代码


--解析配置文件返回流
public class Resources {

    /**
     * 根据配置文件的路径,将配置文件加载成字节输入流,存储在内存中
     * @param path
     * @return
     */
    public static InputStream getResourcesAsSteam(String path){
        InputStream resourceAsStream = Resources.class.getClassLoader().getResourceAsStream(path);
        return resourceAsStream;
    }

}
@Data
public class Configuration {

    private DataSource dataSource;

    /**
     * k:statementId
     * v:封装好的MappedStatement对象
     */
    Map<String,MappedStatement> map = new HashMap<>();

}
@Data
public class MappedStatement {

    //id
    private String id;

    //返回值类型
    private String resultType;

    //参数值类型
    private String paramterType;

    //sql语句
    private String sql;
}
--将解析的流封装到SqlSessionFactoryBuilder中
public class SqlSessionFactoryBuilder {

    public SqlSessionFactory build(InputStream is) throws Exception {
        //1,使用dom4j解析配置文件,将解析出来的内容封装到Configuration中
        XmlConfigBuilder xmlConfigBuilder = new XmlConfigBuilder();
        Configuration configuration = xmlConfigBuilder.parseConfig(is);

        //2,创建sqlSessionFactory对象,工厂类:生产sqlSession绘画对象
        DefaultSqlSessionFactory defaultSqlSessionFactory = new DefaultSqlSessionFactory(configuration);

        return defaultSqlSessionFactory;
    }
}
--将sqlMapConfig.xml和userMapper.xml流放入configuration中
public class XmlConfigBuilder {

    private Configuration configuration;

    public XmlConfigBuilder() {
        this.configuration = new Configuration();
    }

    /**
     * 该方法就是使用dom4j将配置文件解析,封装Configuration
     * @param is
     * @return
     */
    public Configuration parseConfig(InputStream is) throws Exception {

        Document document = new SAXReader().read(is);
        //获取Configuration根对象<Configuration>
        Element rootElement = document.getRootElement();
        //获取sqlMapConfig.xml里面的配置信息并且遍历
        List<Element> list = rootElement.selectNodes("//property");
        Properties properties = new Properties();
        for (Element element : list) {
            String name = element.attributeValue("name");
            String value = element.attributeValue("value");
            properties.setProperty(name,value);
        }

        //创建 c3p0 连接池
        ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
        comboPooledDataSource.setDriverClass(properties.getProperty("driverClass"));
        comboPooledDataSource.setJdbcUrl(properties.getProperty("jdbcUrl"));
        comboPooledDataSource.setUser(properties.getProperty("username"));
        comboPooledDataSource.setUser(properties.getProperty("password"));

        configuration.setDataSource(comboPooledDataSource);

        //mapper.xml解析 步骤:拿到路径-->加载成字节输入流-->dom4j进行解析
        List<Element> mapperList = rootElement.selectNodes("//mapper");

        for (Element element : mapperList) {
            String mapperPath = element.attributeValue("resource");
            InputStream resourcesAsSteam = Resources.getResourcesAsSteam(mapperPath);
            XmlMApperBuilder xmlMApperBuilder = new XmlMApperBuilder(configuration);
            xmlMApperBuilder.prase(resourcesAsSteam);
        }

        return configuration;
    }
}
public class XmlMApperBuilder {

    private Configuration configuration;

    public XmlMApperBuilder(Configuration configuration) {
        this.configuration = configuration;
    }

    public void prase(InputStream is) throws Exception {
        Document document = new SAXReader().read(is);
        Element rootElement = document.getRootElement();
        String namespace = rootElement.attributeValue("namespace");

        List<Element> list = rootElement.selectNodes("//select");
        for (Element element : list) {
            String id = element.attributeValue("id");
            String resultType = element.attributeValue("resultType");
            String paramterType = element.attributeValue("paramterType");
            String sqlText = element.getTextTrim();

            MappedStatement mappedStatement = new MappedStatement();
            mappedStatement.setId(id);
            mappedStatement.setResultType(resultType);
            mappedStatement.setParamterType(paramterType);
            mappedStatement.setSql(sqlText);

            //key值是由 namespace.id来组成
            String key = namespace +"."+id;
            configuration.getMap().put(key,mappedStatement);
        }
    }
}
--利用工厂模式生产sqlSession
public class DefaultSqlSessionFactory implements SqlSessionFactory{

    private Configuration configuration;

    public DefaultSqlSessionFactory(Configuration configuration) {
        this.configuration = configuration;
    }

    @Override
    public SqlSession openSession() {
        return new DefaultSqlSession(configuration);
    }
}
@AllArgsConstructor
public class DefaultSqlSession implements SqlSession {

    private Configuration configuration;

    @Override
    public <E> List<E> selectList(String statementId, Object... params) throws Exception {

        //将要去完成对 SimpleExecutor 里的query方法的调用
        SimpleExecutor simpleExecutor = new SimpleExecutor();
        MappedStatement mappedStatement = configuration.getMap().get(statementId);
        List<Object> list = simpleExecutor.query(configuration, mappedStatement, params);

        return (List<E>) list;
    }

    @Override
    public <T> T selectOne(String statementId, Object... params) throws Exception {
        List<Object> objects = selectList(statementId, params);
        if (objects.size() == 1) {
            return (T) objects.get(0);
        } else {
            throw new RuntimeException("查询结果为空或者返回结果过多");
        }
    }
}
--注册驱动,查询数据信息 并且封装返回
public class SimpleExecutor implements Executor {
    @Override                                                                                 //user
    public <E> List<E> query(Configuration configuration, MappedStatement mappedStatement, Object... params) throws Exception{
        //1,注册驱动,获取连接
        Connection connection = configuration.getDataSource().getConnection();

        //2,获取sql    select * from user where id = #{id} and username = #{username}
          //转换sql    select * from user where id = ? and username = ?,转换过程中,还需要对#{}里面的值进行存储解析
        String sql = mappedStatement.getSql();

        BoundSql boundSql = getBoundSql(sql);

        //3,获取预处理对象
        PreparedStatement preparedStatement = connection.prepareStatement(boundSql.getSqlText());

        //4,设置参数
        //获取到参数的全路径
        String paramterType = mappedStatement.getParamterType();
        Class<?> paramterTypeClass =  getClassType(paramterType);

        List<ParameterMapping> parameterMappingList = boundSql.getParameterMappingList();
        for (int i = 0; i < parameterMappingList.size(); i++) {
            ParameterMapping parameterMapping = parameterMappingList.get(i);
            String content = parameterMapping.getContent();

            //反射根据content获取到实体对象中的属性值,再根据属性值获取到当前传过来的参数对象
            Field declaredField = paramterTypeClass.getDeclaredField(content);
            //暴力访问
            declaredField.setAccessible(true);
            Object o = declaredField.get(params[0]);

            preparedStatement.setObject(i+1,o);
        }

        //5,执行sql
        ResultSet resultSet = preparedStatement.executeQuery();
        //获取实体对象
        String resultType = mappedStatement.getResultType();
        Class<?> resultTypeClass = getClassType(resultType);
        //获取实体对象的具体实现
        Object instance = resultTypeClass.newInstance();

        List<Object> objects = new ArrayList<>();

        //6,封装返回结果集
        while (resultSet.next()) {
            //1,取出元数据
            ResultSetMetaData metaData = resultSet.getMetaData();
            for (int i = 1; i <= metaData.getColumnCount(); i++) {
                //获取字段名
                String columnName = metaData.getColumnName(i);
                //获取字段值
                Object value = resultSet.getObject(columnName);
                //使用反射或者内省根据数据库表和实体的对应关系,完成封装
                PropertyDescriptor propertyDescriptor = new PropertyDescriptor(columnName, resultTypeClass);
                Method writeMethod = propertyDescriptor.getWriteMethod();
                writeMethod.invoke(instance,value);
            }
            objects.add(instance);
        }

        return (List<E>) objects;
    }

    /**
     * 反射获取实体
     * @param paramterType
     * @return
     * @throws Exception
     */
    private Class<?> getClassType(String paramterType) throws Exception {
        if (StringUtils.isNullOrEmpty(paramterType)) {
            Class<?> aClass = Class.forName(paramterType);
            return aClass;
        }
        return null;
    }

    /**
     * 完成对#{}的解析工作:1,将#{}使用?进行代替;2,解析出#{}里面的值进行存储
     * @param sql
     * @return
     */
    private BoundSql getBoundSql(String sql) {
        //标记处理类:配置标记解析器来完成对占位符的解析处理工作
        ParameterMappingTokenHandler tokenHandler = new ParameterMappingTokenHandler();
        //标记解析器,对占位符的转换
        GenericTokenParser tokenParser = new GenericTokenParser("#{", "}", tokenHandler);
        //解析出来的sql
        String parseSql = tokenParser.parse(sql);
        //#{}里面的解析出来的参数名称
        List<ParameterMapping> parameterMappings = tokenHandler.getParameterMappings();

        BoundSql boundSql = new BoundSql(parseSql,parameterMappings);

        return boundSql;

    }
}

开始测试

public class IPersistenceTest {

    @Test
    public void test() throws Exception {
        //获取配置文件流
        InputStream resourcesAsSteam = Resources.getResourcesAsSteam("sqlMapConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourcesAsSteam);
        SqlSession sqlSession = sqlSessionFactory.openSession();

        //调用
        User user = new User();
        user.setId(1);
        user.setUsername("张三");
        User user2 = sqlSession.selectOne("user.selectOne", user);
        System.out.println(user2);
    }
}

结果:

posted @ 2022-04-29 16:02  前度刘郎  阅读(27)  评论(0编辑  收藏  举报
欢迎来到戴建伟的博客!