龙空技术网

自定义持久层框架

编程识堂 136

前言:

此刻你们对“dom4j方法”大体比较珍视,小伙伴们都需要分析一些“dom4j方法”的相关知识。那么小编在网络上网罗了一些有关“dom4j方法””的相关文章,希望各位老铁们能喜欢,朋友们一起来学习一下吧!

小伙伴儿们,如果觉得文章干货满满,欢迎加入公众号【编程识堂】,更多干货等你们来哦!

背景

阅读源码,是程序员越不过去的坎儿,只有掌握到相应方法,跟着小堂坚持、坚持再坚持,抓住主干流程,一步一步梳理,相信大家一定会有收获。

开源框架有很多,我们先从持久性框架Mybatis开始,小堂将从Mybatis概念、应用、架构原理、源码及设计模式等等方面为大家讲解,跟着小堂一起卷起来。

介绍

持久层是我们JavaEE三层结构中与数据库交互的一层,我们经常称为Dao层,当然作为持久层的技术选型有很多,不仅仅只有mybatis一种,在早期,我们持久层直接会选用JDBC去完成与数据库的交互,完成CRUD操作。既然JDBC已经能完成与数据库的操作,为什么后期还会出现Mybatis,是不是意味着JDBC与数据库交互时还会出现很多问题,正是这些问题,后期才会出现像Mybatis这种持久型框架,这些持久型框架其实都是对JDBC的封装,只是在封装的过程中,将JDBC的一些问题进行了规避和解决。

我们接下来,就会对JDBC的一些回顾,并分析JDBC与数据库交互时究竟存在哪些问题,然后会给出一个解决思路,并在此基础之上,完成一个自定义一个持久型框架。小伙伴儿们会疑问,为什么要自定义呢?其实自定义持久型框架就是Mybatis框架的一个雏形,对后续阅读Mybatis框架源码是有帮助的。

JDBC回顾及问题分析回顾

public static void main(String[] args) {    Connection connection = null;    PreparedStatement preparedStatement = null;    ResultSet resultSet = null;    try {        // 加载数据库驱动        Class.forName("com.mysql.jdbc.Driver");        // 通过驱动管理类获取数据库链接        connection =        DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis?        characterEncoding=utf-8", "root", "root");        // 定义sql语句?表示占位符        String sql = "select * from user where username = ?";        // 获取预处理statement        preparedStatement = connection.prepareStatement(sql);        // 设置参数,第⼀个参数为sql语句中参数的序号(从1开始),第⼆个参数为设置的参数值        preparedStatement.setString(1, "tom");        // 向数据库发出sql执⾏查询,查询出结果集        resultSet = preparedStatement.executeQuery();        // 遍历查询结果集        while (resultSet.next()) {            int id = resultSet.getInt("id");            String username = resultSet.getString("username");            // 封装User            user.setId(id);            user.setUsername(username);        }        System.out.println(user);    }} catch (Exception e) {    e.printStackTrace();} finally {    // 释放资源    if (resultSet != null) {        try {            resultSet.close();        } catch (SQLException e) {            e.printStackTrace();             }} if (preparedStatement != null) { try { preparedStatement.close(); } catch (SQLException e) { e.printStackTrace(); }} if (connection != null) { try { connection.close(); } catch (SQLException e) { e.printStackTrace(); } }}
问题分析JDBC问题总结

原始jdbc开发存在的问题如下:

数据库连接创建、释放频繁造成系统资源浪费,从⽽影响系统性能。Sql语句在代码中硬编码,造成代码不易维护,实际应⽤中sql变化的可能较⼤,sql变动需要改变 java代码。使⽤preparedStatement向占有位符号传参数存在硬编码,因为sql语句的where条件不⼀定,可能 多也可能少,修改sql还要修改代码,系统不易维护。对结果集解析存在硬编码(查询列名),sql变化导致解析代码变化,系统不易维护,如果能将数据 库 记录封装成pojo对象解析⽐较⽅便问题解决思路使⽤数据库连接池初始化连接资源将sql语句抽取到xml配置⽂件中使⽤反射、内省等底层技术,⾃动将实体与表进⾏属性与字段的⾃动映射自定义持久层框架设计思路使用端

是一个项目,需引入自定义持久层框架的jar包。

提供两部分配置信息: 数据库配置信息、sql配置信息:sql语句、参数类型、返回值类型。使用配置文件来提供这两部分配置信息:sqlMapConfig.xml:存放数据库配置信息,存放mapper.xml的全路径mapper.xml:存放sql配置信息自定义持久层框架本身

是一个工程,本质就是对JDBC代码进行了封装

加载配置文件

根据配置文件的路径,加载配置文件成字节输入流,存储在内存中

创建Resources类,对应方法为:InputStream getResourceAsStream(String path);

创建两个javaBean(容器对象)

存放的就是对配置文件解析出来的内容: 我们不能将读取到的配置信息以流的形式存放在内存中,不好操作,可 以创建javaBean来存储

Configuration:核心配置类:存放sqlMapconfig.xml解析出来的内容; Map<唯⼀标识,Mapper> 唯⼀标识:namespace + "." + id 。

MappedStatement:映射配置类:存放mapper.xml解析出来的内容; :sql语句、statement类型、输⼊参数java类型、输出参数java类型 。

解析配置文件

通过dom4j技术xml配置文件。

创建SqlSessionFactoryBuilder类,方法 SqlSessionFactory build(inputStream in)

使⽤dom4j解析配置⽂件,将解析出来的内容封装到Configuration和MappedStatement中 ;创建SqlSessionFactory对象:用来生产sqlSession会话对象(工厂模式)创建SqlSessionFactory接口及实现类DefaultSqlSessionFactory

创建方法openSession():生产sqlSession

创建SqlSession接口及实现类DefaultSqlSession

定义对数据库的CRUD操作:selectList() selectOne() update() delete()

创建Executor接口及实现类SimpleExecutor实现类

query(Configuration configuration,MappedStatement mappedStatement,Object...params):执行的就是JDBC代码。

自定义框架实现使用端创建User实体

package com.xl.jdbc.pojo;public class User {    private Integer id;    private String name;    private Integer age;    public Integer getId() {        return id;    }    public void setId(Integer id) {        this.id = id;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public Integer getAge() {        return age;    }    public void setAge(Integer age) {        this.age = age;    }    @Override    public String toString() {        return "User{" +                "id=" + id +                ", name='" + name + '\'' +                ", age=" + age +                '}';    }}
使用端创建sqlMapConfig.xml文件
<configuration>    <!--数据库配置信息-->    <dataSource>        <property name="driver" value="com.mysql.jdbc.Driver"></property>        <property name="jdbcUrl" value="jdbc:mysql://192.168.150.131:3306/bcst?characterEncoding=utf-8"></property>        <property name="user" value="root"></property>        <property name="password" value="123456"></property>    </dataSource>    <!-- 存放mapper.xml的全路径-->    <mapper resource="UserMapper.xml"></mapper></configuration>
使用端创建UserMapper.xml文件
<Mapper namespace= "user">    <!--sql唯一标识:namespace.id来组成:statementId -->    <select id="selectList" resultType="com.xl.jdbc.pojo.User">        select id,name from user    </select>    <!--     User user = new User();     user.setId(1);     user.setName("张三");     -->    <select id="selectOne" resultType="com.xl.jdbc.pojo.User" paramterType="com.xl.jdbc.pojo.User">        select id,name from user where id = #{id} and name = #{name}    </select></Mapper>
自定义持久层框架Resources类定义
package com.xl;import java.io.InputStream;public class Resources {    /**     * 根据配置文件路径加载配置文件成为字节输入流,存放在内存中     * @param path     * @return     */    public static InputStream getResourcesAsStream(String path){       return Resources.class.getClassLoader().getResourceAsStream(path);    }}
使用端引入自定义持久层框架的依赖
<dependency>  <groupId>com.xl</groupId>  <artifactId>persistence</artifactId>  <version>1.0.0-SNAPSHOT</version></dependency>
使用端调用自定义持久层框架加载配置配置文件
public class IPersistenceTest {    public static void main(String[] args) {        InputStream inputStream = Resources.getResourcesAsStream("sqlConfigMap.xml");    }}
自定义持久层框架容器对象定义MappedStatement文件
package com.xl.com.xl.pojo;public class MappedStatement {    /**     * 标识id     */    private Integer id;    /**     * 返回值类型     */    private String resultType;    /**     * 参数值类型     */    private String paramterType;    /**     * sql语句     */    private String sql;    public Integer getId() {        return id;    }    public void setId(Integer id) {        this.id = id;    }    public String getResultType() {        return resultType;    }    public void setResultType(String resultType) {        this.resultType = resultType;    }    public String getParamterType() {        return paramterType;    }    public void setParamterType(String paramterType) {        this.paramterType = paramterType;    }    public String getSql() {        return sql;    }    public void setSql(String sql) {        this.sql = sql;    }}
Configuration文件
package com.xl.com.xl.pojo;import javax.sql.DataSource;import java.util.HashMap;import java.util.Map;public class Configuration {    /**     * 数据源     */    private DataSource dataSource;    /**     * key: statementId     * value: 封装好的MappedStatement对象     */    private Map<String,MappedStatement> mappedStatementMap = new HashMap<>();    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;    }}
自定义持久层框架解析配置文件XmlConfigBuilder
package com.xl.config;import com.mchange.v2.c3p0.ComboPooledDataSource;import com.xl.pojo.Configuration;import org.dom4j.Document;import org.dom4j.DocumentException;import org.dom4j.Element;import org.dom4j.io.SAXReader;import java.beans.PropertyVetoException;import java.io.InputStream;import java.util.List;import java.util.Properties;public class XmlConfigBuilder {    private Configuration configuration;    public XmlConfigBuilder() {        this.configuration = new Configuration();    }    /**     * 该方法就是使用dom4j对配置文件解析,封装Configuration     * @param inputStream     * @return     */    public Configuration parseConfig(InputStream inputStream) {        try {            Document document = new SAXReader().read(inputStream);            //根节点 <configuration>            Element rootElement = document.getRootElement();            //使用xpath 获取property节点            List<Element> propertyList = rootElement.selectNodes("//property");            Properties properties = new Properties();            for (Element element : propertyList) {                properties.setProperty(element.attributeValue("name"),element.attributeValue("value"));            }            //使用c3p0数据库连接池            ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();            comboPooledDataSource.setDriverClass(properties.getProperty("driver"));            comboPooledDataSource.setJdbcUrl(properties.getProperty("jdbcUrl"));            comboPooledDataSource.setUser(properties.getProperty("user"));            comboPooledDataSource.setPassword(properties.getProperty("password"));            this.configuration.setDataSource(comboPooledDataSource);            //mapper.xml解析:拿到mapper.xml的全路径--字节输入流--dom4j进行解析            List<Element> mapperList = rootElement.selectNodes("//mapper");            for (Element element : mapperList) {                String mapperPath = element.attributeValue("resource");                XmlMapperBuilder xmlMapperBuilder = new XmlMapperBuilder(this.configuration);                xmlMapperBuilder.parse(mapperPath);            }        } catch (DocumentException | PropertyVetoException e) {            e.printStackTrace();        }        return configuration;    }}
XmlMapperBuilder
package com.xl.config;import com.xl.Resources;import com.xl.pojo.Configuration;import com.xl.pojo.MappedStatement;import org.dom4j.Document;import org.dom4j.DocumentException;import org.dom4j.Element;import org.dom4j.io.SAXReader;import java.io.InputStream;import java.util.List;public class XmlMapperBuilder {    private Configuration configuration;    public XmlMapperBuilder(Configuration configuration) {        this.configuration = configuration;    }    public void parse(String mapperPath) {        try {            InputStream inputStream = Resources.getResourcesAsStream(mapperPath);            Document document = new SAXReader().read(inputStream);            //Mapper            Element rootElement = document.getRootElement();            String namespace = rootElement.attributeValue("namespace");            List<Element> selectList = rootElement.selectNodes("//select");            for (Element element : selectList) {                String id = element.attributeValue("id");                String paramterType = element.attributeValue("paramterType");                String resultType = element.attributeValue("resultType");                String sql = element.getTextTrim();                MappedStatement mappedStatement = new MappedStatement();                mappedStatement.setId(id);                mappedStatement.setParamterType(paramterType);                mappedStatement.setResultType(resultType);                mappedStatement.setSql(sql);                String statementId = namespace+"."+id;                configuration.getMappedStatementMap().put(statementId,mappedStatement);            }        } catch (DocumentException e) {            e.printStackTrace();        }    }}
SqlSessionFactoryBuilder
package com.xl.sqlSession;import com.xl.config.XmlConfigBuilder;import com.xl.pojo.Configuration;import java.io.InputStream;public class SqlSessionFactoryBuilder {    public SqlSessionFactory build(InputStream inputStream){        //1、使用dom4j解析配置文件,将解析出来的内容封装到Configuration中        XmlConfigBuilder xmlConfigBuilder = new XmlConfigBuilder();        Configuration configuration = xmlConfigBuilder.parseConfig(inputStream);        //2.创建SqlSessionFactory对象        return null;    }}
SqlSessionFactory
package com.xl.sqlSession;public interface SqlSessionFactory {}
IPersistenceTest
package com.xl.jdbc.test;import com.xl.Resources;import com.xl.sqlSession.SqlSessionFactoryBuilder;import java.io.InputStream;/** * 使用端测试 */public class IPersistenceTest {    public static void main(String[] args) {        InputStream inputStream = Resources.getResourcesAsStream("sqlConfigMap.xml");        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();        sqlSessionFactoryBuilder.build(inputStream);    }}
测试自定义持久层框架会话对象sqlSession创建DefaultSqlSessionFactory对象实现SqlSessionFactory接口
public class DefaultSqlSessionFactory implements SqlSessionFactory {    private Configuration configuration;    public DefaultSqlSessionFactory(Configuration configuration) {        this.configuration = configuration;    }}
SqlSessionFactoryBuilder
package com.xl.sqlSession;import com.xl.config.XmlConfigBuilder;import com.xl.pojo.Configuration;import java.io.InputStream;public class SqlSessionFactoryBuilder {    public SqlSessionFactory build(InputStream inputStream){        //1、使用dom4j解析配置文件,将解析出来的内容封装到Configuration中        XmlConfigBuilder xmlConfigBuilder = new XmlConfigBuilder();        Configuration configuration = xmlConfigBuilder.parseConfig(inputStream);        System.out.println(configuration);        //2.创建SqlSessionFactory对象 : 工厂类:生产sqlSession会话对象        SqlSessionFactory sqlSessionFactory = new DefaultSqlSessionFactory(configuration);        return sqlSessionFactory;    }}
创建SqlSession对象
package com.xl.sqlSession;public interface SqlSession {}
sqlSession会话对象类定义
package com.xl.sqlSession;public interface SqlSessionFactory {    SqlSession openSession();}
package com.xl.jdbc.test;import com.xl.Resources;import com.xl.sqlSession.SqlSession;import com.xl.sqlSession.SqlSessionFactory;import com.xl.sqlSession.SqlSessionFactoryBuilder;import java.io.InputStream;/** * 使用端测试 */public class IPersistenceTest {    public static void main(String[] args) {        InputStream inputStream = Resources.getResourcesAsStream("sqlConfigMap.xml");        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);        //SqlSessionFactory 工厂类:生产sqlSession会话对象        SqlSession sqlSession = sqlSessionFactory.openSession();    }}
package com.xl.sqlSession;import com.xl.pojo.Configuration;public class DefaultSqlSessionFactory implements SqlSessionFactory {    private Configuration configuration;    public DefaultSqlSessionFactory(Configuration configuration) {        this.configuration = configuration;    }    /**     * 创建会话对象     * @return     */    @Override    public SqlSession openSession() {        return new DefaultSession(configuration);    }}
package com.xl.sqlSession;public class DefaultSession implements SqlSession {}
sqlSession会话对象方法定义

定义对数据库的CRUD操作的方法,下面小堂只列举部分方法。

package com.xl.sqlSession;import com.xl.pojo.Configuration;import com.xl.pojo.MappedStatement;import java.util.List;public class DefaultSession implements SqlSession {    private Configuration configuration;    public DefaultSession(Configuration configuration) {        this.configuration = configuration;    }    @Override    public <E> List<E> selectList(String statementId, Object... params) throws Exception {        MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementId);        SimpleExecutor simpleExecutor = new SimpleExecutor();        return simpleExecutor.query(configuration, mappedStatement, params);    }    @Override    public <T> T selectOne(String statementId, Object... params) throws Exception {        List<Object> list = this.selectList(statementId, params);        if (list.size()==1){           return (T) list.get(0);        }else {            throw new RuntimeException("selectOne error return more...");        }    }}
package com.xl.sqlSession;import com.xl.pojo.Configuration;import com.xl.pojo.MappedStatement;import java.sql.SQLException;import java.util.List;public interface Executor {    <E> List<E> query(Configuration configuration, MappedStatement mappedStatement,Object ... params) throws SQLException, Exception;}
package com.xl.sqlSession;import com.xl.config.BoundSql;import com.xl.pojo.Configuration;import com.xl.pojo.MappedStatement;import com.xl.utils.GenericTokenParser;import com.xl.utils.ParameterMapping;import com.xl.utils.ParameterMappingTokenHandler;import java.beans.PropertyDescriptor;import java.lang.reflect.Field;import java.lang.reflect.Method;import java.sql.Connection;import java.sql.PreparedStatement;import java.sql.ResultSet;import java.sql.ResultSetMetaData;import java.util.ArrayList;import java.util.List;public class SimpleExecutor implements Executor {    @Override    public <E> List<E> query(Configuration configuration, MappedStatement mappedStatement, Object... params) throws Exception {        //注册驱动,获取连接        Connection connection = configuration.getDataSource().getConnection();        //获取sql select * from user where id = #{id} and name = #{name}        String sql = mappedStatement.getSql();        //转换sql select * from user where id = ? and name = ? 因为jdbc 只识别?占位符,转换的过程中,还需要对#{}        //里面的值进行解析存储        BoundSql boundSql = getBoundSql(sql);        //获取预处理对象PreparedStatement        PreparedStatement preparedStatement = connection.prepareStatement(boundSql.getParseSql());        //设置参数        //获取入参类型 com.xl.jdbc.pojo.User        String paramterType = mappedStatement.getParamterType();        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();        for (int i = 0; i < parameterMappings.size(); i++) {            Class<?> paramterTypeClass = Class.forName(paramterType);            ParameterMapping parameterMapping = parameterMappings.get(i);            //获取参数名称 比如id name            String filed = parameterMapping.getContent();            //利用反射获取参数值            Field declaredField = paramterTypeClass.getDeclaredField(filed);            declaredField.setAccessible(true);            Object value = declaredField.get(params[0]);            preparedStatement.setObject(i+1,value);        }        //执行sql        ResultSet resultSet = preparedStatement.executeQuery();        //获取输出参数类型 com.xl.jdbc.pojo.User        String resultTypeStr = mappedStatement.getResultType();        Class<?> resultTypeClass = Class.forName(resultTypeStr);        List<Object> relsutList = new ArrayList<>();        //封装返回结果集        while (resultSet.next()){            //返回实体            Object obj = resultTypeClass.newInstance();            //元数据            ResultSetMetaData metaData = resultSet.getMetaData();            //列数            int columnCount = metaData.getColumnCount();            for (int i = 1; i <= columnCount; i++) {                //字段名                String columnName = metaData.getColumnName(i);                //字段值                Object value = resultSet.getObject(columnName);                //使用反射或者内省,根据数据库表和实体的对应关系,完成封装                PropertyDescriptor propertyDescriptor = new PropertyDescriptor(columnName, resultTypeClass);                Method writeMethod = propertyDescriptor.getWriteMethod();                writeMethod.invoke(obj,value);            }            relsutList.add(obj);        }        return (List<E>) relsutList;    }    /**     * 完成对#{}的解析工作:1.将#{}使用?代替  2.解析出#{}里面的值进行存储 如id name     * @param sql     * @return     */    private BoundSql getBoundSql(String sql) {        //使用mybatis工具类        ParameterMappingTokenHandler parameterMappingTokenHandler = new ParameterMappingTokenHandler();        GenericTokenParser genericTokenParser = new GenericTokenParser("#{","}",parameterMappingTokenHandler);        String parseSql = genericTokenParser.parse(sql);        List<ParameterMapping> parameterMappings = parameterMappingTokenHandler.getParameterMappings();        return new BoundSql(parseSql,parameterMappings);    }}
package com.xl.jdbc.test;import com.xl.Resources;import com.xl.jdbc.pojo.User;import com.xl.sqlSession.SqlSession;import com.xl.sqlSession.SqlSessionFactory;import com.xl.sqlSession.SqlSessionFactoryBuilder;import java.io.InputStream;import java.util.List;/** * 使用端测试 */public class IPersistenceTest {    public static void main(String[] args) throws Exception {        InputStream inputStream = Resources.getResourcesAsStream("sqlConfigMap.xml");        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);        //SqlSessionFactory 工厂类:生产sqlSession会话对象        SqlSession sqlSession = sqlSessionFactory.openSession();        User user = new User();        user.setId(11239737);        user.setName("小王-1");        User user1 = sqlSession.selectOne("user.selectOne", user);        System.out.println(user1);        /*List<Object> objects = sqlSession.selectList("user.selectList");        System.out.println(objects.size());*/    }}
自定义框架优化优化1

我们在使用自定义持久化框架与数据库交互还是有些问题的,在使用端测试类IPersistenceTest,我们在main方法中,我们写的这段代码,在实际项目中,我们一般编写在持久层方法中,因为这些都是与数据库交互的代码,应该编写在持久层方法中,接下来我们先建立持久层。

public interface IUserDao {    List<User> selectList() throws Exception;    User selectOne(User user) throws Exception;}
package com.xl.jdbc.dao;import com.xl.Resources;import com.xl.jdbc.pojo.User;import com.xl.sqlSession.SqlSession;import com.xl.sqlSession.SqlSessionFactory;import com.xl.sqlSession.SqlSessionFactoryBuilder;import java.io.InputStream;import java.util.List;public class UserDaoImpl implements IUserDao {    @Override    public List<User> selectList() throws Exception {        InputStream inputStream = Resources.getResourcesAsStream("sqlConfigMap.xml");        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);        //SqlSessionFactory 工厂类:生产sqlSession会话对象        SqlSession sqlSession = sqlSessionFactory.openSession();        List<User> objects = sqlSession.selectList("user.selectList");        return  objects;    }    @Override    public User selectOne(User user) throws Exception {        InputStream inputStream = Resources.getResourcesAsStream("sqlConfigMap.xml");        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);        //SqlSessionFactory 工厂类:生产sqlSession会话对象        SqlSession sqlSession = sqlSessionFactory.openSession();        User user1 = sqlSession.selectOne("user.selectOne", user);        return user1;    }}
测试

在测试类IPersistenceTest增加测试方法

   UserDaoImpl userDao = new UserDaoImpl();   User user1 = userDao.selectOne(user);   System.out.println(user1);
分析

通过上述我们的⾃定义框架,我们解决了JDBC操作数据库带来的⼀些问题:例如频繁创建释放数据库连 接,硬编码,⼿动封装返回结果集等问题,但是现在我们继续来分析刚刚完成的⾃定义框架代码,有没 有什么问题?

问题如下:

Dao层使用自定义持久层框架,存在代码重复,整个操作过程模板重复(加载配置文件、创建sqlSessionFactory、生产sqlSession)statementId存在硬编码问题

解决思路:

使用代理模式生产Dao层接口的代理实现类

优化2在sqlSession中添加方法

public interface SqlSession {   <T> T getMapper(Class<?> mapperClass);}
实现类
public class DefaultSession implements SqlSession {      @Override    public <T> T getMapper(Class<?> mapperClass) {        /**         * 1.使用JDK动态代理来为Dao层接口生成动态代理,并返回         * 2.代理对象调用接口中任意方法,都会执行invoke方法         */        Object proxyInstance = Proxy.newProxyInstance(DefaultSession.class.getClassLoader(), new Class<?>[]{mapperClass}, new InvocationHandler() {            @Override            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {                /**                 * proxy:当前代理对象的应用                 * method:当前被调用方法的引用                 * args:传递的参数                 *                 * 底层都还是执行JDBC代码,根据不同情况,来调用selectList或者selectOne                 * 准备参数 1.statementId:sql语句唯一标识:namespace.id = 接口全限定名.方法                 * 接口全限定名 com.xl.jdbc.dao.IUserDao                 * 方法名 selectOne                 */                String methodName = method.getName();                String namespace = method.getDeclaringClass().getName();                String statementId = namespace + "." + methodName;                Type genericReturnType = method.getGenericReturnType();                if (genericReturnType instanceof ParameterizedType) {                    //如果返回参数类型后面带有泛型认为返回的是集合                    List<Object> objects = selectList(statementId, args);                    return objects;                } else {                    return selectOne(statementId, args);                }            }        });        return (T) proxyInstance;    }}
测试类
package com.xl.jdbc.test;import com.xl.Resources;import com.xl.jdbc.dao.IUserDao;import com.xl.jdbc.pojo.User;import com.xl.sqlSession.SqlSession;import com.xl.sqlSession.SqlSessionFactory;import com.xl.sqlSession.SqlSessionFactoryBuilder;import java.io.InputStream;import java.util.List;/** * 使用端测试 */public class IPersistenceTest {    public static void main(String[] args) throws Exception {        InputStream inputStream = Resources.getResourcesAsStream("sqlConfigMap.xml");        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);        //SqlSessionFactory 工厂类:生产sqlSession会话对象        SqlSession sqlSession = sqlSessionFactory.openSession();        User user = new User();        user.setId(11239737);        user.setName("小王-1");        IUserDao userDao = sqlSession.getMapper(IUserDao.class);        User user1 = userDao.selectOne(user);        System.out.println(user1);    }}
时序图

标签: #dom4j方法