6

自定义持久层框架MyORMFramework(三)—框架实现

 2 years ago
source link: https://blog.51cto.com/zhanjq/5488165
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

定义数据源配置和映射语句实体

Configuration

@Data
public class Configuration {

    private DataSource dataSource;

    /**
     *  key: statementId  value:封装好的mappedStatement对象
     */
    Map<String,MappedStatement> mappedStatementMap = new HashMap<>();

}

MappedStatement

@Data
public final class MappedStatement {

    private String id;
    /**
     * sql语句
     */
    private String sql;
    /**
     * 参数值类型
     */
    private Class<?> parameterType;
    /**
     * 返回值类型
     */
    private Class<?> resultType;
}

定义配置文件访问的工具类

public class Resources {

    /**
     * 根据配置文件的路径,将配置文件加载成字节输入流,存储在内存中
     *
     * @param path
     * @return
     */
    public static InputStream getResourceAsSteam(String path) throws IOException {
        InputStream in = Resources.class.getClassLoader().getResourceAsStream(path);
        if (in == null) {
            throw new IOException("找不到资源" + path);
        }
        return in;
    }
}

定义SqlSessionFactoryBuilder

public class SqlSessionFactoryBuilder {

    private Configuration configuration;

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

    public SqlSessionFactory build(InputStream inputStream) throws
            DocumentException, PropertyVetoException, ClassNotFoundException {
        //1.dom4j解析配置⽂件,封装Configuration
        XMLConfigerBuilder xmlConfigerBuilder = new
                XMLConfigerBuilder(configuration);
        Configuration configuration =
                xmlConfigerBuilder.parseConfiguration(inputStream);
        //2.创建SqlSessionFactory对象(工厂类),生产sqlSession:会话对象
        SqlSessionFactory sqlSessionFactory = new
                DefaultSqlSessionFactory(configuration);
        return sqlSessionFactory;
    }
}

数据源配置和映射语句解析XMLConfigBuilder、XMLMapperBuilder

XMLConfigBuilder

public class XMLConfigerBuilder {

    private Configuration configuration;

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

    /**
     * 该方法就是使用dom4j对配置文件进行解析,封装Configuration
     */
    public Configuration parseConfig(InputStream inputStream) throws DocumentException, PropertyVetoException, IOException, ClassNotFoundException {

        Document document = new SAXReader().read(inputStream);
        //<configuration>
        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("username"));
        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 resourceAsSteam = Resources.getResourceAsSteam(mapperPath);
            XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(configuration);
            xmlMapperBuilder.parse(resourceAsSteam);
        }
        return configuration;
    }

}

XMLMapperBuilder

public class XMLMapperBuilder {

    private Configuration configuration;

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

    public void parse(InputStream inputStream) throws DocumentException,
            ClassNotFoundException {
        Document document = new SAXReader().read(inputStream);
        Element rootElement = document.getRootElement();
        String namespace = rootElement.attributeValue("namespace");
        List<Element> select = rootElement.selectNodes("select");
        for (Element element : select) {
            //id的值
            String id = element.attributeValue("id");
            String parameterType = element.attributeValue("parameterType");
            String resultType = element.attributeValue("resultType");
            //输⼊参数class
            Class<?> parameterTypeClass = getClassType(parameterType);
            //返回结果class
            Class<?> resultTypeClass = getClassType(resultType);
            //statementId
            String key = namespace + "." + id;
            //sql语句
            String textTrim = element.getTextTrim();
            //封装 mappedStatement
            MappedStatement mappedStatement = new MappedStatement();
            mappedStatement.setId(id);
            mappedStatement.setParameterType(parameterTypeClass);
            mappedStatement.setResultType(resultTypeClass);
            mappedStatement.setSql(textTrim);
            //填充 configuration
            configuration.getMappedStatementMap().put(key, mappedStatement);
        }
    }

    private Class<?> getClassType (String parameterType) throws
            ClassNotFoundException {
        Class<?> aClass = Class.forName(parameterType);
        return aClass;
    }

}

SqlSessionFactory 接口及DefaultSqlSessionFactory 实现类

SqlSessionFactory

public interface SqlSessionFactory {
    SqlSession openSession();
}

DefaultSqlSessionFactory

public class DefaultSqlSessionFactory implements SqlSessionFactory {
    private Configuration configuration;

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

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

SqlSession 接口及 DefaultSqlSession 实现类

SqlSession

public interface SqlSession {

    /**
     * 查询所有
     *
     * @param statementId
     * @param params
     * @param <E>
     * @return
     * @throws Exception
     */
    <E> List<E> selectList(String statementId, Object... params) throws Exception;

    /**
     * 根据条件查询单个
     *
     * @param statementId
     * @param params
     * @param <T>
     * @return
     * @throws Exception
     */
    <T> T selectOne(String statementId, Object... params) throws Exception;

}

DefaultSqlSession

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("查询结果为空或者返回结果过多");
        }
    }
    
}

SQL执行器接口和实现类Executor、SimpleExecutor

Executor

public interface Executor {
    /**
     * 执行查询SQL
     * @param configuration
     * @param mappedStatement
     * @param params
     * @param <E>
     * @return
     * @throws Exception
     */
    <E> List<E> query(Configuration configuration, MappedStatement mappedStatement, Object... params) throws Exception;
}

SimpleExecutor

/**
 * SQL执行器实现
 *
 * @author zjq
 * @date 2022/3/15
 */
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语句 : 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 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();

            //反射
            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);
        Class<?> resultTypeClass = mappedStatement.getResultType();

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

        // 6. 封装返回结果集
        while (resultSet.next()) {
            Object o = resultTypeClass.newInstance();
            //元数据
            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(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;

    }


    /**
     * 完成对#{}的解析工作:1.将#{}使用?进行代替,2.解析出#{}里面的值进行存储
     *
     * @param sql
     * @return
     */
    private BoundSql getBoundSql(String sql) {
        //标记处理类:配置标记解析器来完成对占位符的解析处理工作,主要是配合通⽤标记解析器GenericTokenParser类完成对配置⽂件等的解析⼯作,其中TokenHandler主要完成处理
        ParameterMappingTokenHandler parameterMappingTokenHandler = new ParameterMappingTokenHandler();
        //GenericTokenParser :通⽤的标记解析器,完成了代码⽚段中的占位符的解析,然后再根据给定的标记处理器(TokenHandler)来进⾏表达式的处理
        //三个参数:分别为openToken (开始标记)、closeToken (结束标记)、handler (标记处理器)
        GenericTokenParser genericTokenParser = new GenericTokenParser("#{", "}", parameterMappingTokenHandler);
        //解析出来的sql
        String parseSql = genericTokenParser.parse(sql);
        //#{}里面解析出来的参数名称
        List<ParameterMapping> parameterMappings = parameterMappingTokenHandler.getParameterMappings();

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

}

创建 sqlMapConfig.xml

<configuration>

    <!--数据库配置信息-->
    <dataSource>
        <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
        <property name="jdbcUrl" value="jdbc:mysql:///my-orm-framework"></property>
        <property name="username" value="root"></property>
        <property name="password" value="root"></property>
    </dataSource>

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

</configuration>

创建对应的mapper.xml

<mapper namespace="com.zjq.dao.IUserDao">

    <!--sql的唯一标识:namespace.id来组成 : statementId-->
    <select id="findAll" resultType="com.zjq.pojo.User" >
        select * from user
    </select>
    
    <!--
        User user = new User()
        user.setId(1);
        user.setUsername("zhangsan")
    -->
    <select id="findByCondition" resultType="com.zjq.pojo.User" paramterType="com.zjq.pojo.User">
        select * from user where id = #{id} and username = #{username}
    </select>
    
</mapper>

UserDao

public interface IUserDao {

    //查询所有用户
    public List<User> findAll() throws Exception;
    
    //根据条件进行用户查询
    public User findByCondition(User user) throws Exception;
    
}

User实体

@Data
public class User {

    private Long id;

    private String username;

    private String password;
}

框架查询验证

    @Test
    public void test() throws Exception {
        InputStream resourceAsSteam = Resources.getResourceAsSteam("sqlMapConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsSteam);
        SqlSession sqlSession = sqlSessionFactory.openSession();

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

        System.out.println(user2);

        List<User> users = sqlSession.selectList("user.selectList");
        for (User user1 : users) {
          System.out.println(user1);
        }
        
    }

本文内容到此结束了,
如有收获欢迎点赞👍收藏💖关注✔️,您的鼓励是我最大的动力。
如有错误❌疑问💬欢迎各位大佬指出。
主页 共饮一杯无的博客汇总👨‍💻

保持热爱,奔赴下一场山海。🏃🏃🏃

自定义持久层框架MyORMFramework(三)—框架实现_java

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK