首页 > 其他 > 详细

自定义持久层实现思想

时间:2020-05-29 09:15:02      阅读:48      评论:0      收藏:0      [点我收藏+]

首先了解传统JDBC实现逻辑:

public static void main(String[] args) {
        //Connection对象
        Connection conn = null;
        //Statement对象
        Statement stmt = null;
        //ResultSet对象
        ResultSet rs = null;
        try {
            //      1.加载驱动
            Class.forName("com.mysql.cj.jdbc.Driver");

            //      2.获取数据库连接对象(这里用的是oracle数据库)
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/sales","root","root");

            //      3.通过获得数据库连接对象获取一个statement对象
            stmt = conn.prepareStatement();

            //      4.通过statement对象执行sql语句,查询操作用executeQuery(),增删改用executeUpdate()
            rs = stmt.executeQuery("select * from user");


            while(rs.next()){                   //将读取的数据获取

                int id = rs.getInt("id");

                System.out.println(id);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally{              //最后关闭数据库连接,先打开的连接后关闭,后打开的先关闭
            try {
                if(rs!=null){
                    rs.close();
                }
                if(stmt!=null){
                    stmt.close();
                }
                if(conn!=null){
                    conn.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

 

1加载驱动

2获取connection 链接。

3.获取预编译 perpestatement

4.编写sql

5.去回传封装对象

传统JDBC缺陷:十比较严重的硬编码需要反复手动开关链接需要手动的封装数据。

对以上几个缺陷进行优化和设计:。

使用配置文件进行对相关的数据源配置好驱动加载,这样更换数据源也只需要修改相关配置文件。。

DB的链接资源的重复开销,可以使用数据库链接是池。 ↓

SQL的编码问题也同样可以使用配置文件进行编写,不会影响代码。。

使用动态代理的思想,反射实现对返回结果集的封装,避免手动封装 重复和多余的过程。。

实现思想:

配置文件部分:对数据源和SQL进行xml的格式进行配置。

读取配置:建立resource方法去读取配置文件部分。使用IO流把读取的配置信息放入流

public class Resources {
    // 根据配置文件路径,把配置文件加载为字节输入流
    public static InputStream getResourceAsSteam(String path){
        InputStream inputStream = Resources.class.getClassLoader().getResourceAsStream(path);
        return inputStream;
    }
}

  

 

定义两个封装容器: MappedStatement用于存放SQL部分解析出来的信息(mapperxm).Configuration用于核心配置,封装好数据源相关配置信息(datasouce), 同时也可以把封装好的MappedStatement 也放入,这样以后向下传递的时候只需要一个对象即可。

MappedStatement :这个容器主要存储SQL的相关信息涉及查询类型(resutTpye) 识别Statementid识别Statementid由namespace (一般可以定义大类比如user. product等)加上当id (mapper内定义的id)作为唯一标识、以及mapper内的sgl语句。

public class XMLConfigBuilder {

    private  Configuration configuration;

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

    /**
     * 解析配置,创建封装Configuration
     */
    public Configuration parseConfiguration(InputStream inputStream) throws DocumentException, PropertyVetoException {
        Document document = new SAXReader().read(inputStream);
        //<eConfiguration>
        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
            properties.setProperty(name,value);


        }
        ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
        comboPooledDataSource.setDriverClass(properties.getProperty("driverClass"));
        comboPooledDataSource.setDriverClass(properties.getProperty("jdbcUrl"));
        comboPooledDataSource.setDriverClass(properties.getProperty("userName"));
        comboPooledDataSource.setDriverClass(properties.getProperty("passWord"));

        configuration.setDataSource(comboPooledDataSource);

        //mapper.xml解析  路径

        List<Element> listMapper = rootElement.selectNodes("//mapper");
        for (Element element : listMapper) {
            String resource = element.attributeValue("resource");
            InputStream resourceAsSteam = Resources.getResourceAsSteam(resource);
            XMLMapprtBuilder xmlConfigBuilder = new XMLMapprtBuilder(configuration);
            xmlConfigBuilder.pase(resourceAsSteam);

        }


        return  configuration;
    }
}
public class XMLMapprtBuilder {
    private Configuration Configuration;


    public XMLMapprtBuilder(Configuration Configuration) {
        this.Configuration = Configuration;
    }

    public void  pase(InputStream inputStream) throws DocumentException {
        Document document = new SAXReader().read(inputStream);
        Element rootElement = document.getRootElement();
        //获取select标签里的内容
        List<Element> elements = rootElement.selectNodes("//select");
        String namespace = rootElement.attributeValue("namespace");
        for (Element element : elements) {
            //解析
            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.setParamterType(paramterType);
            mappedStatement.setResultType(resultType);
            mappedStatement.setSql(sqlText);
            String key = namespace + "." + id;
            Configuration.getMappedStatementMap().put(key,mappedStatement);

        }

    }
}

 

配置数据解析:使用Dom4j解析把解析后的数据通过反射给相关实体类(Configuration.MappedStatement)。

配置信息封装:使用两个类作为容器封装读取的配置信息向下传参。,

进入seesionFactory的建立:通过工程模式创建seesion。

public class SqlSessionFactoryBuilder {
    public  SqlSessionFactory bulid(InputStream inputStream) throws DocumentException, PropertyVetoException {
        //1.使用log4j解析配置文件,封装好configuration
        //创建解析类
        XMLConfigBuilder xmlConfigBuilder = new XMLConfigBuilder();
        Configuration configuration = xmlConfigBuilder.parseConfiguration(inputStream);

        //2.创建传递SqlSessionFactory对象
        /**
         * 创建SqlSessionFactory 产生session会话
         */
        DefaultSqlSessionFactory defaultSqlSessionFactory = new DefaultSqlSessionFactory(configuration);

        return  defaultSqlSessionFactory;
    }
}

seesion内定义实现crud操作:对于返回结果集由于返回结果不样, 不用定义准确乏型。定义查询传参建议也留下可变参数。但是传入信息定会包括我们的configuration信息

package com.lagou.sqlSession;

import com.lagou.config.BoundSql;
import com.lagou.pojo.Configuration;
import com.lagou.pojo.MappedStatement;
import com.lagou.utils.GenericTokenParser;
import com.lagou.utils.ParameterMapping;
import com.lagou.utils.ParameterMappingTokenHandler;
import sun.net.www.http.ChunkedOutputStream;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.*;
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 把相关#{user_id} 变成占位?
        String sql = mappedStatement.getSql();
        //解析sql
        BoundSql boundSql = getBoundSQL(sql);
        //获取预处理
        PreparedStatement preparedStatement = connection.prepareStatement(boundSql.getSqlText());
        //设置参数 params
        //获取类全路径获取class
        String paramterType = mappedStatement.getParamterType();

        Class<?> paramterTypeClass = getClassType(paramterType);
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
        for (int i = 0; i < parameterMappings.size(); i++) {
            ParameterMapping parameterMapping = parameterMappings.get(i);
            String content = parameterMapping.getContent();
            //反射
            Field declaredField = paramterTypeClass.getDeclaredField(content);
            //暴力访问
            declaredField.setAccessible(true);
            Object o = declaredField.get(params[0]);
            preparedStatement.setObject(i+1,o);



        }

        //执行sql
        ResultSet resultSet = preparedStatement.executeQuery();
        //获取class
        String resultType = mappedStatement.getResultType();
        Class<?> classTypeClass = getClassType(resultType);
        //返回结果集
        List<Object> objects = new ArrayList<>();
        while (resultSet.next()){
            Object o = classTypeClass.newInstance();
            //获取元数据
            ResultSetMetaData metaData = resultSet.getMetaData();
            for (int i = 0; i < metaData.getColumnCount(); i++) {
                //字段名
                String columnName = metaData.getColumnName(i);
                Object value = resultSet.getObject(columnName);
                //使用反射,根据数据库字段和实体类对应,完成封装
                PropertyDescriptor propertyDescriptor = new PropertyDescriptor(columnName, classTypeClass);
                Method writeMethod = propertyDescriptor.getWriteMethod();
                writeMethod.invoke(o,value);
            }
            objects.add(o);


        }

        return (List<E>) objects;
    }

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

    /**
     *把相关#{user_id} 变成占位?把user_id的值进行存储
     * @param sql
     * @return
     */
    private BoundSql getBoundSQL(String sql) {
        //标记处理: 解析相关占位
        ParameterMappingTokenHandler parameterMappingTokenHandler = new ParameterMappingTokenHandler();
        GenericTokenParser genericTokenParser = new GenericTokenParser("#{", "}", parameterMappingTokenHandler);
        //解析后的sql
        String paseSql = genericTokenParser.parse(sql);
        //#{}内的参数
        List<ParameterMapping> parameterMappings = parameterMappingTokenHandler.getParameterMappings();
        BoundSql boundSql = new BoundSql(paseSql, parameterMappings);
        return  boundSql;


    }

}

 

创建Excuter类进行相关的JDBC数据库链接操作:传入我们需要的MappedStatement相关sql信息,Configuration 的相关信息。创建相关的crud方法。。

例如创建query方法,使用Configuration内的datasouce数据库链接,。

从MappedStatement获取sg!.对编写的sql进行解析转换(自己mapper里sql的规进行解析使用占位符代替,再把里的值做出替换。)售

使用标记解析器.解析我们占位编译成可以执行的sgl. (使用 mybatis里的工具类包,。这个包负责把相关的占位符进行替换,以及获取我们#内的值,)中

获取实体类对象反射,暴力获取传入的实体类的相关属性值.

获取预处理对象prepareStatement把获取的值塞入执行sql.。

执行在返回结果集,对查询的结果数据set进入实体(实体类型再mapperxm指定了相关的实体类型),利用反射根据数据库(字段名cloumnName)对应数据(要注意实体类和数据库对应的表要对应)封装成实体类。

 

附带我项目的git:https://github.com/yhybadfisher/badfisherOwer/blob/master/MyBatis-easy.rar

 

自定优化

Dao层会出现大量的代码重复,解决:可以提取一个上层类进行继承减少重复代码。。部分硬编码(statmentid),.

不要Dao的实现类,使用代理模式为dao接口实现代理实现类。中

把方法名和id (mapper里的sql用的id)有相同的值,通过获取方法名加上权限类名(namespace)这样就可以解决相关获取sql和实体类。

自定义持久层实现思想

原文:https://www.cnblogs.com/badfisher/p/12985064.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!