自定义持久层框架

DEMO自定义持久层框架的增删改查

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

使用端引入自定义持久层框架jar包,定义自定义持久层框架本身就是对JDBC代码进行封装,去解决或者规避原生JDBC在使用中的一些问题。

JDBC问题分析以及在自定义框架的解决方式

JDBC问题分析:

  1. 数据库配置信息存在硬编码问题
  2. 频繁创建和释放数据库链接
  3. SQL语句,设置参数,获取结果集参数存在硬编码问题
  4. 手动封装结果集较为繁琐

解决方式:

  1. 使用配置文件配置数据库
  2. 使用链接池
  3. 使用映射配置文件
  4. 反射,内省

整体流程

自定义持久层框架_第1张图片

导包:

<dependencies>
        <dependency>
            <groupId>mysqlgroupId>
            <artifactId>mysql-connector-javaartifactId>
            <version>5.1.17version>
        dependency>
        <dependency>
            <groupId>c3p0groupId>
            <artifactId>c3p0artifactId>
            <version>0.9.1.2version>
        dependency>
        <dependency>
            <groupId>log4jgroupId>
            <artifactId>log4jartifactId>
            <version>1.2.12version>
        dependency>
        <dependency>
            <groupId>junitgroupId>
            <artifactId>junitartifactId>
            <version>4.10version>
        dependency>
        <dependency>
            <groupId>dom4jgroupId>
            <artifactId>dom4jartifactId>
            <version>1.6.1version>
        dependency>
        <dependency>
            <groupId>jaxengroupId>
            <artifactId>jaxenartifactId>
            <version>1.1.6version>
        dependency>
    dependencies>

创建核心配置文件sqlMapConfig.xml以及映射配置文件UserMapper.xml

sqlMapConfig.xml:

<configuration>
    
    <dataSource>
        <property name="driverClass" value="com.mysql.jdbc.Driver">property>
        <property name="jdbcUrl" value="jdbc:mysql:///localbase1">property>
        <property name="user" value="root">property>
        <property name="password" value="0000">property>
    dataSource>

    
    <mapper resource="usermapper.xml">mapper>
configuration>`

UserMapper.xml:

<mapper namespace="com.lagou.dao.IUserDao">
    <select id="findAll" resultType="com.lagou.pojo.User" >
        select * from user
    select>
    <select id="findByCondition" resultType="com.lagou.pojo.User" paramterType="com.lagou.pojo.User">
        select * from user where id = #{id} and username = #{username}
    select>
    <insert id="addUser" paramterType="com.lagou.pojo.User">
        insert into user  values (#{id},#{username},#{password},#{birthday})
    insert>
    <update id="updateUser" paramterType="com.lagou.pojo.User">
        update user set username = #{username} ,password = #{password}, birthday = #{birthday}
        where id = #{id}
    update>
    <delete id="deleteUser" paramterType="java.lang.Integer">
        delete from user where id = #{id}
    delete>
mapper>

创建Resources类,添加getResourcesAsStream(String path)方法,根据配置文件路径将配置文件加载成字节输入流

public class Resources {
     
    //根据配置文件的路径,将配置文件加载成字节流
    public static InputStream getResourceAsStream(String path){
     
        InputStream resourceAsStream = Resources.class.getClassLoader().getResourceAsStream(path);
        return resourceAsStream;
    }
}

创建两个容器对象类,用来存放配置文件解析出来的内容。

核心配置类Configuration

存放sqlMapConfig.xml解析出来的内容,具体如下

public class Configuration {
     

    private DataSource dataSource;

    /*
    * key:statementId
    * value:mappedStatement对象
    * */
    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;
    }
}

映射配置类MappedStatement

存放mapper.xml解析出来的内容

public class MappedStatement {
     
    //id标识
    private String id;
    //返回值类型
    private String resultType;
    //参数值类型
    private String paramterType;
    //sql语句
    private String sql;

    public String getId() {
     
        return id;
    }

    public void setId(String id) {
     
        this.id = id;
    }


    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;
    }

    public String getResultType() {
     
        return resultType;
    }

    public void setResultType(String resultType) {
     
        this.resultType = resultType;
    }
}

创建SqlSessionFactoryBuilder解析配置文件:dom4j

创建SqlSessionFactoryBuilder类以及build(InputStream in)

  1. 使用dom4j解析配置文件,将配置文件内容封装到容器上面的两个对象中
  2. 创建SqlSessionFactory对象;生产SqlSession

SqlSessionFactoryBuilder:

public class SqlSessionFactoryBuilder {
     

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

        //创建SqlSessionFactory对象
        DefaultSqlSessionFactory defaultSqlSessionFactory = new DefaultSqlSessionFactory(configuration);
        return defaultSqlSessionFactory;
    }
}

XMLConfigBuilder 解析核心配置文件

public class XMLConfigBuilder {
     

    private Configuration configuration;

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

    /**使用dom4j将配置文件解析封装Configuration的方法
     *
     * @param inputStream
     * @return
     */
    public Configuration parseConfig(InputStream inputStream) throws DocumentException, PropertyVetoException {
     

        Document document = new SAXReader().read(inputStream);
        //
        Element rootElement = document.getRootElement();
        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);
        }

        ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
        comboPooledDataSource.setDriverClass(properties.getProperty("driverClass"));
        comboPooledDataSource.setJdbcUrl(properties.getProperty("jdbcUrl"));
        comboPooledDataSource.setUser(properties.getProperty("user"));
        comboPooledDataSource.setPassword(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 resourceAsStream = Resources.getResourceAsStream(mapperPath);
            XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(configuration);
            xmlMapperBuilder.parse(resourceAsStream);
        }
        return configuration;
    }
}

XMLMapperBuilder解析映射配置文件

public class XMLMapperBuilder {
     
    private 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 = rootElement.selectNodes("//select|//delete|//update|//insert");
        for (Element element : list) {
     
            String id = element.attributeValue("id");
            String resultType = element.attributeValue("resultType");
            String paramterType = element.attributeValue("paramterType");
            String sqlTest = element.getTextTrim();
            MappedStatement mappedStatement = new MappedStatement();
            mappedStatement.setId(id);
            mappedStatement.setResultType(resultType);
            mappedStatement.setParamterType(paramterType);
            mappedStatement.setSql(sqlTest);
            String key = namespace+"."+id;
            configuration.getMappedStatementMap().put(key,mappedStatement);
        }
    }
}

创建SqlSessionFactory接口及实现类DefaultSqlSessionFactory

通过openSession()方法生产SqlSession

public interface SqlSessionFactory {
     
    public SqlSession openSession();
}
public class DefaultSqlSessionFactory implements SqlSessionFactory{
     

    private Configuration configuration;

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

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

创建SqlSession接口及实现类DefaultSession

	定义对数据库的Crud操作,以及代理方法getMapper()	
public interface SqlSession {
     
    //查询所有
    public <E> List<E> selectList(String statementid, Object... params) throws Exception;

    //查询单个
    public <T> T selectOne(String statementid, Object... params) throws Exception;

    //为DAO接口生成代理实现类
    public <T> T getMapper(Class<?> mapperClass);

    public Boolean except(String statementId, Object[] args) throws Exception;
}

public class DefaultSqlSession implements SqlSession {
     

    private Configuration configuration;

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

    @Override
    public <E> List<E> selectList(String statementid,Object... params) throws Exception {
     
        //完成对simpleExecutor里的query方法的调用
        SimpleExecutor simpleExecutor = new SimpleExecutor();
        MappedStatement mappedStatement = configuration.getMappedStatementMap().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("查询结果为空或者返回结果过多");
        }
    }

    @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 {
     
                //获取方法名
                String methodName = method.getName();
                String className = method.getDeclaringClass().getName();
                //获取statementId
                String statementId = className+"."+methodName;

                Type genericReturnType = method.getGenericReturnType();
                Class<?> returnType = method.getReturnType();
                if(returnType == List.class){
     
                    System.out.println("返回类型为:list");
                }
                //判断是否为增删改方法
                if(methodName.contains("delete")||methodName.contains("update")||methodName.contains("insert")||methodName.contains("add")){
     
                    return except(statementId,args);
                }
                //泛型类型参数化
                if(genericReturnType instanceof ParameterizedType){
     
                    List<Object> objects = selectList(statementId, args);
                    return objects;
                }
                return selectOne(statementId,args);
            }
        });
        return (T) proxyInstance;
    }

    @Override
    public Boolean except(String statementId, Object[] args) throws Exception {
     
        //将要去完成对simpleExecutor里的executeUpdate方法的调用
        SimpleExecutor simpleExecutor = new SimpleExecutor();
        MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementId);
        return  simpleExecutor.except(configuration, mappedStatement, args);
    }
}

创建Executor执行器接口及实现类SimpleExecutor

public interface Executor {
     

    public <E> List<E>
    query(Configuration configuration, MappedStatement mappedStatement,Object... params) throws SQLException, Exception;

    public boolean except(Configuration configuration,MappedStatement mappedStatement,Object... params)throws Exception;
}

public class SimpleExecutor implements Executor {
     
    @Override
    public <E> List<E> query(Configuration configuration, MappedStatement mappedStatement, Object... params) throws Exception {
     
        //1.注册驱动,获取链接
        Connection connection = configuration.getDataSource().getConnection();
        //2.获取sql语句,转换sql语句,转换的过程中还需要对#{}中的值进行解析存储
        String sql = mappedStatement.getSql();
        BoundSql boundSql = getBoundSql(sql);
        //3.获取预处理对象
        PreparedStatement preparedStatement = connection.prepareStatement(boundSql.getSqlText());
        //4.设置参数
        List<ParameterMapping> parameterMappingList = boundSql.getParameterMappingList();
        String paramterType = mappedStatement.getParamterType();
        Class<?> parameterTypeClass = getClassType(paramterType);
        for (int i = 0; i < parameterMappingList.size(); i++) {
     
            ParameterMapping parameterMapping = parameterMappingList.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);
        }
        //5.执行SQL
        ResultSet resultSet = preparedStatement.executeQuery();
        String resultType = mappedStatement.getResultType();
        Class<?> resultTypeClass = getClassType(resultType);
       // = resultTypeClass.newInstance();
        ArrayList<Object> results = new ArrayList<>();
        //6.封装返回结果集
        while(resultSet.next()){
     
            Object o1 = resultTypeClass.newInstance();
            //去除resultSet中的元数据 ResultSetMetaData元数据
           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 propertyDescriptor = new PropertyDescriptor(columnName, resultTypeClass);
               Method readMethod = propertyDescriptor.getWriteMethod();
               readMethod.invoke(o1,value);
           }
            results.add(o1);
        }

        return (List<E>) results;
        //return new ArrayList<>();
    }

    /**增删改
     *
     * @param configuration
     * @param mappedStatement
     * @param params
     * @return
     * @throws Exception
     */
    @Override
    public boolean except(Configuration configuration, MappedStatement mappedStatement, Object... params) throws Exception {
     
        //1.注册驱动,获取链接
        Connection connection = configuration.getDataSource().getConnection();
        //2.获取sql语句,转换sql语句,转换的过程中还需要对#{}中的值进行解析存储
        String sql = mappedStatement.getSql();
        BoundSql boundSql = getBoundSql(sql);
        //3.获取预处理对象
        PreparedStatement preparedStatement = connection.prepareStatement(boundSql.getSqlText());
        //获取参数类型
        String parameterType = mappedStatement.getParamterType();
        Class<?> paramClassType = getClassType(parameterType);

        List<ParameterMapping> parameterMappingList = boundSql.getParameterMappingList();
        if (parameterMappingList.size()==1){
     
            preparedStatement.setObject(1,params[0]);
        }else{
     
            for (int i = 0; i < parameterMappingList.size(); i++) {
     
                ParameterMapping parameterMapping = parameterMappingList.get(i);
                String content = parameterMapping.getContent();
                Field declaredField = paramClassType.getDeclaredField(content);
                declaredField.setAccessible(true);
                Object o = declaredField.get(params[0]);
                preparedStatement.setObject(i+1,o);
            }
        }

        return preparedStatement.executeUpdate()>0;
    }

    private Class<?> getClassType(String paramterType) throws ClassNotFoundException {
     
        if(paramterType!=null){
     
            Class<?> aClass = Class.forName(paramterType);
            return aClass;
        }
        return null;
    }

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

        BoundSql boundSql = new BoundSql(genericsql,parameterMappings);
        return boundSql;
    }
}

代码地址:自定义持久层框架DEMO

思维导图,验证及讲解视频地址:思维导图及验证讲解视频

你可能感兴趣的:(java,后端,mybatis)