Mybatis—学习过程—自定义持久层框架设计思路
1.查看代码分析思路可以从两个方面来入手
使用端
:就是一个项目,这个项目需要调用持久层框架与数据库进行交互,既然这个项目需要调用自定义持久层框架,那么我们就需要引入自定义持久层框架的jar包- 经过分析发现
使用端
需要提供两部分配置信息(不能写死在代码中)
数据库配置信息
sql配置信息
- sql语句
- 参数类型
- 返回值类型
- 使用配置文件来提供这两部分配置信息
sqlMapConfig.xml
:存放数据库的配置信息,存放mapper.xml的全路径mapper.xml
:存放sql的配置信息
- 经过分析发现
自定义持久层框架本身
:本身就是一个工程项目,本质是对JDBC的代码进行了封装-
加载配置文件,创建一个类
Resources
,方法叫getResourceAsStream(String path),参数需要传递一个路径,这个路径就是加载配置文件的路径,这个路径是sqlMapConfig.xml的路径,但是里面可能会包含mapper.xml的路径
,目的是根据配置文件的路径,加载配置文件成字节输入流,存储在内存中 -
解析
sqlMapConfig.xml和mapper.xml两个配置文件
,把配置文件里面的内容封装成两个javabean(两个实体对象)便于存取,其实就是容器对象
,存放的就是对配置文件解析出来的内容Configuration
:核心配置类,存放sqlMapConfig.xml解析出来的东西MappedStatement
:映射配置类,存放的是mapper.xml解析出来的东西
-
解析配置文件,使用dom4j技术解析,过程如下
- 创建类
SqlSessionFactortyBuilder
,在这个类中有个build的方法,参数就是InputStreambuild(InputStream in)
,在build方法中执行两步(由第二步可知build方法的返回值是 SqlSessionFactorty)
:- 使用dom4j技术解析配置文件,将解析出来的内容封装到容器对象中
(Configuration和MappedStatement)
- 创建
SqlSessionFactorty
工厂对象,SqlSessionFactorty
主要作用是生产SqlSession
的,SqlSession
是会话对象,而当我们使用自定义持久层框架和数据库进行交互时候,crud的方法都封装到SqlSession
中了,其中用到了工厂模式
- 使用dom4j技术解析配置文件,将解析出来的内容封装到容器对象中
- 创建类
-
基于开闭原则,创建
SqlSessionFactory
接口及实现类defaultSqlSessionFactory
1. 其实SqlSessionFactory
就是一个工厂类
2. 其中有个方法叫OpenSession();
这个方法主要是用来生产SqlSession
,也就是用来创建一个sqlSession
对象的 -
创建
SqlSession
接口和实现类DefaultSession
- 定义数据库的crud操作:
- selectList( );
查询所有
- selectOne( )
- update( )
- delete( )
- selectList( );
- 定义数据库的crud操作:
-
创建
Excutor
接口及实现类SimpleExcutor
实现类, 其实就是我们将JDBC的代码封装到了SimpleExcutor
实现类中- query(Configuration,MappedStatement,Object 。。。Params);执行的就是JDBC的代码
-
使用端和自定义持久层框架本身是两个工程,所以说我们先写使用端
自定义持久层框架设计之使用端的编写
1.创建工程IPersistence_test
- 使用
idea
创建一个maven
工程 - 引入自定义持久层框架的依赖
因为目前自定义持久层框架还没写出来,所以引入依赖这一步可以先不着急
- 目前只要在使用端完成配置文件的创建即可,一般都配置在工程下的
Reaource
文件夹中,创建两个配置文件sqlMapConfig.xml
:<configuration> <!--数据源,数据库配置信息--> <dataSource name="driverClass" value="com.mysql.jdbc.Driver"></dataSource> <dataSource name="jdbcUrl" value="jdbc:mysql:///zdy_mybatis"></dataSource> <dataSource name="username" value="root"></dataSource> <dataSource name="password" value="admin123"></dataSource> <mapper resource="mapper.xml"></mapper> </configuration>
mapper.xml
:<mapper namespace="User"> <!--sql唯一的标示性,namespace.id保证唯一性,暂时先叫statementId --> <select id="selectOne" paramterType="com.lagou.pojo.User" resultType="com.lagou.pojo.User"> select * from user where id = #{id} and username =#{username} </select> <select id="selectList" resultType="com.lagou.pojo.User"> select * from user </select> </mapper>
自定义持久层框架本身的编写
1.加载配置文件:首先创建Resources类
-
持久层框架底层就是执行jdbc代码,但jdbc想要成功执行两部分代码必不可少,一部分是
数据库的配置信息
,另一部分是sql的配置信息
,这两个配置信息已经让使用端使用了两个配置文件sqlMapConfig.xml
,mapper.xml
进行了提供 -
所以说持久层框架就是使用dom4j
一种xml解析技术
解析使用端的两个配置文件,并把解析出来的东西封装到两个javabean
中Configuration
和MappedStatement
当中,这两个javabean会当做参数层层传递到SimpleExcutor
中的query
方法中 -
query方法
query(Configuration,MappedStatement,Object 。。。Params);
会再执行过程中取出Configuration
和MappedStatement
中封装的信息进行使用public class Resources { /** * 根据配置文件的路径,将配置文件加载成字节输入流,存储在内存中 */ public static InputStream getResourceAsStream(String path){ InputStream resourceAsStream = Resources.class.getClassLoader().getResourceAsStream(path); return resourceAsStream; } }
2.创建两个javabean对象Configuration
和MappedStatement
(容器对象),就是存放配置文件解析出来的内容
Configuration
:存放的是数据源的配置信息
MappedStatement
:存放的是sql语句,参数类型,返回值类型
- 创建
MappedStatement
:配置映射类/** * 映射配置类,存放的是mapper.xml解析出来的东西 * 每个select标签就会被解析成MappedStatement对象 */ public class MappedStatement { /** * id标识 */ private String id; /** *返回值 */ private String resaultType; /** *参数 */ private String paramterType; /** *sql语句 */ private String sql; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getResaultType() { return resaultType; } public void setResaultType(String resaultType) { this.resaultType = resaultType; } 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
:数据源配置信息
/** * 存放的是数据源的配置信息 */ public class Configuration { private DataSource dataSource; /** * 由于Configuration和MappedStatement都需要传递, * 按照面向对象的思想来说多参数传递我们可以将它合成一个对象进行传递, * MappedStatement对象在解析的过程中肯定是要解析成多个的 * 所以在Configuration中可以来一个Map集合, * 在Map集合中存放解析过程中封装好的一个又一个的MappedStatement的对象信息 * 这样在传递过程中只需要传递一个Configuration就好了 * 此时Configuration既包含了数据库配置信息也包含了sql的配置信息 * * 其中的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; } }
- 创建
3.解析配置文件:使用dom4j技术解析sqlMapConfig.xml
,mapper.xml
两个配置文件,并将解析出来的内容封装到Configuration
和MappedStatement
这两个javabean
对象当中
3-1.解析核心配置文件sqlMapConfig.xml和mapper.xml,将两个配置文件的信息全部封装到Configuration对象中去,然后将configuration对象进行传递
-
首先创建
SqlSessionFactoryBuilder
类/** * 在这个SqlSessionFactoryBuilder类中需要提供一个build方法 */ public class SqlSessionFactoryBuilder { /** * build方法的返回值是SqlSessionFactory * @return */ public SqlSessionFactory build(InputStream is) throws DocumentException, PropertyVetoException { //第一件事使用dom4j解析配置文件,将解析出来的配置文件的内容封装到Configuration对象中 XMLConfigBuilder xmlConfigBuilder = new XMLConfigBuilder(); /** * configuration 是第一步最终得到的返回值对象, * 这里面就是已经封装好的数据库配置信息和sql配置信息的一个对象 * 之后我们就将这个Configuration一直向下传递,一直传递到最终底层执行jdbc这段代码当中 */ Configuration configuration = xmlConfigBuilder.parseConfig(is); /** * 第二件事创建sqlSessionFactory对象:是一个工厂类,主要是作用就是生产sqlSession(会话对象) * 创建一个DefaultSqlSessionFactory对象,开始层层传递 * 当我们开始层层传递的时候,我们要把封装好的configuration进行层层传递,这样才能保证在最终执行jdbc代码的时候可以执行到数据库和sql的配置信息 * DefaultSqlSessionFactory在这个类中进行有参构造函数,将configuration参数进行层层传递 * 最终返回defaultSqlSessionFactory */ DefaultSqlSessionFactory defaultSqlSessionFactory = new DefaultSqlSessionFactory(configuration); return defaultSqlSessionFactory; } }
-
创建
XMLConfigBuilder
类:/** * 使用dom4j解析配置文件的全过程 */ public class XMLConfigBuilder { private Configuration configuration; public XMLConfigBuilder() { this.configuration = new Configuration(); } /** * 该方法就是使用dom4j将配置文件进行解析,封装Configuration * 这个流就是对sqlMapConfig.xml进行的解析 * * @param is * @return */ public Configuration parseConfig(InputStream is) throws DocumentException, PropertyVetoException { Document read = new SAXReader().read(is); /** * 拿到他的一个根对象,拿到了sqlMapConfig配置文件中的<configuration>标签中的信息 */ Element rootElement = read.getRootElement(); /** * 去查找里面的元素 */ List<Element> list = rootElement.selectNodes("//property"); /** * new 一个Properties对象用来存储 key-value键值对数据的容器 */ Properties properties = new Properties(); /** * 便利获取list中的值 */ for (Element element : list) { String name = element.attributeValue("name"); String value = element.attributeValue("value"); properties.setProperty(name,value); } /** * 配置信息都已经装到了Properties对象中,接下来就可以new一个数据源对象了 * 这里我们将使用数据库连接池,将数据库频繁连接释放这个问题解决掉 * 我们是用c3p0这个连接池 * 之后将输有数据全部存储在 comboPooledDataSource 对象中 */ 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:拿到mapper.xml的路径(在sqlMapConfig.xml中的mapper resource标签中存的)---将文件转换成字节输入流---使用dom4j进行解析 List<Element> mapperList = rootElement.selectNodes("//mapper"); for (Element element : mapperList) { String mapperPath = element.attributeValue("resource"); /** * 通过路径将文件加载成字节输入流 */ InputStream resourceAsStream = Resources.getResourceAsStream(mapperPath); /** * 调用XMLMapperBuilder这个类中的某个方法进行dom4j解析时,应该传递一个Configuration过去,这样才能将解析好的MappedStatement对象传递过去 */ XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(configuration); xmlMapperBuilder.parse(resourceAsStream); } return configuration; } }
3-2.创建SqlSessionFactory对象,生产出sqlSession对象
- 创建
SqlSessionFactory
的实现类DefaultSqlSessionFactory
,创造有参构造函数传递被封装好的configuration
public class DefaultSqlSessionFactory implements SqlSessionFactory { /** * 封装配置文件信息的对象 */ private Configuration configuration; public DefaultSqlSessionFactory(Configuration configuration) { this.configuration = configuration; } }
4.创建SqlSessionFactory对象后,完善OpenSession方法
public class DefaultSqlSessionFactory implements SqlSessionFactory {
/**
* 封装配置文件信息的对象
*/
private Configuration configuration;
public DefaultSqlSessionFactory(Configuration configuration) {
this.configuration = configuration;
}
@Override
public SqlSession openSession() {
/**
* 要创建SqlSession接口的实现类DefaultSqlSession,这个类需要实现SqlSession的接口
*/
return new DefaultSqlSession();
}
}