自定义持久层框架
文章目录
-
- 自定义持久层框架
- 前言
- 一、JDBC问题分析
- 二、解决策略分析
- 二、框架设计
-
- 1.使用端
- 2. 框架端
- 三、实现
-
- 1.使用端
- 2.框架端
- 总结
前言
例如:为什么连接数据库不再使用JDBC,而改用诸如Mybatis的持久层框架,此篇通过自定义持久层框架分析对比了二者的区别,及持久层的实现原理
提示:以下是本篇文章正文内容,下面案例可供参考
一、JDBC问题分析
- 数据库连接创建、释放频繁,造成系统资源浪费,从而影响系统性能;
- sql语句存在硬编码,不易扩展和维护;
- 查询结果集的封装存在硬编码,sql变化会导致解析变化,不易维护;
二、解决策略分析
- 连接频繁---->使用连接池
- sql语句硬编码----->使用配置文件
- 封装结果集硬编码 ------>使用反射、内省封装pojo对象
二、框架设计
1.使用端
提供核心配置文件
sqlMapConfig.xml 提供数据库配置信息,引入mapper.xml
mapper.xml 提供sql语句配置信息
2. 框架端
-
加载配置文件,以字节流读取到内存中
创建Resources类,getResourceAsStaream方法,返回InputStream
-
创建Bean对象,存放解析出来的内容
Configuration:核心配置类,存放sqlMapConfig.xml 解析出来的内容;
MappedStatement:映射配置类,存放mapper.xml解析春来的内容;
Configuration 中引入MappedStatement,便于传递执行。
-
解析配置文件
创建类SqlSessionFactoryBuilder
创建方法build
该方法做了两件事:
第一,dom4j解析字节输入流,封装成Configuration对象中;
第二,创建工厂类SqlSessionFactory对象。
-
创建SQLSessionFactory接口及实现类DefaultSqlSessionFactory
方法openSession,该方法获取SqlSession接口的实例类对象
-
创建SqlSession接口及实现类DefaultSession
创建CRUD方法:selectList()、selectOne()、insert()、update()、delete()等,方法中调用Executor接口的实力类对象的方法
优化:在SqlSession中添加getMapper方法,通过代理实现(代理对象在调用任何方法,都会执行getMapper中的invoke方法)
-
创建Executor接口及实现类SimpleExecutor
该类的crud方法中执行的就是jdbc的代码:注册驱动获连接、获取sql、获取预处理对象、设置参数、执行sql、封装结果集
三、实现
1.使用端
sqlMapConfig.xml 代码如下:
<configuration>
<!-- 数据库配置信息 -->
<dataSource>
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/lagou?useUnicode=true&characterEncoding=utf8"></property>
<property name="username" value="root"></property>
<property name="password" value="123456"></property>
</dataSource>
<!--存放mapper.xml全路径-->
<mapper resource="UserMapper.xml"></mapper>
</configuration>
sqlMapConfig.xml 代码如下:
<mapper namespace="com.lagou.dao.IUserDao">
<!-- sql唯一标识:namespace.id组成:statementId-->
<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})
</insert>
<update id="updateUser" paramterType="com.lagou.pojo.User">
update user set username = #{username} where id = #{id}
</update>
<delete id="deleteUser" paramterType="com.lagou.pojo.User">
delete from user where id = #{id}
</delete>
</mapper>
2.框架端
Resources.java代码如下:
public static InputStream getResourceAsStaream(String path){
InputStream resourceAsStream = Resources.class.getClassLoader().getResourceAsStream(path);
return resourceAsStream;
SqlSessionFactoryBuilder.java代码如下:
public SqlSessionFactory build(InputStream in) throws PropertyVetoException, DocumentException {
//第一:使用dom4j解析配合文件,将解析出来的配置文件封装到Configuration中
XMLConfigBuilder xmlConfigBuilder = new XMLConfigBuilder();
Configuration configuration = xmlConfigBuilder.parseConfig(in);
//第二:创建SqlsessionFactory对象:工厂类:生产sqlSession:会话对象
DefaultSqlSessionFactory defaultSqlSessionFactory = new DefaultSqlSessionFactory(configuration);
return defaultSqlSessionFactory;
}
XMLConfigBuilder.java代码如下:
/**
*
* 该方法就是将配置文件进行解析,封装到Configuration
*/
public Configuration parseConfig(InputStream in) throws DocumentException, PropertyVetoException {
Document document = new SAXReader().read(in);
//根标签<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解析:拿到路径--字节输入流---解析
List<Element> mapperList = rootElement.selectNodes("//mapper");
for (Element element : mapperList) {
String mapperPath = element.attributeValue("resource");
InputStream resourceAsStaream = Resources.getResourceAsStaream(mapperPath);
XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(configuration);
xmlMapperBuilder.parse(resourceAsStaream);
}
return configuration;
}
xmlMapperBuilder.java代码如下:
public void parse(InputStream inputStream) throws DocumentException {
Document document = new SAXReader().read(inputStream);
Element rootElement = document.getRootElement();
String namesapce = rootElement.attributeValue("namespace");
List<Element> selectlist = rootElement.selectNodes("//select");
List<Element> insertlist = rootElement.selectNodes("//insert");
List<Element> updatelist = rootElement.selectNodes("//update");
List<Element> deletelist = rootElement.selectNodes("//delete");
//组装方法类型与标签集合的对应关系
Map map = new HashMap<String,Object>();
map.put("select",selectlist);
map.put("insert",insertlist);
map.put("update",updatelist);
map.put("delete",deletelist);
Iterator<Map.Entry<String, Object>> it = map.entrySet().iterator();
while(it.hasNext()){
Map.Entry<String, Object> entry = it.next();
dealConfiguration(configuration,(List<Element>)entry.getValue(),(String)entry.getKey(),namesapce);
}
}
/**
把配置文件增删改查类型的标签配置到MappedStatement,从而封装到Configuration
*/
private void dealConfiguration(Configuration configuration,List<Element> list,String methodType,String namesapce){
for (Element element : list) {
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.setResultType(resultType);
mappedStatement.setParamterType(paramterType);
mappedStatement.setSql(sqlText);
mappedStatement.setMethodType(methodType);
String key = namesapce + "." + id;
configuration.getMappedStatementMap().put(key,mappedStatement);
}
}
DefaultSqlSession.java代码如下:
@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;
}
DefaultSqlSession.java代码如下:
@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}
String sql = mappedStatement.getSql();
//解析sql语句: select * from user where id=? and username=? 转换过程中,对#{}里面的值进行存储
BoundSql boundSql = getBoundSql(sql);
//3. 获取预处理对象:paramterStatement
PreparedStatement preparedStatement = connection.prepareStatement(boundSql.getSqlText());
//4. 设置参数
//获取参数全路径
String paramterType = mappedStatement.getParamterType();
Class<?> paramterClass = 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 = paramterClass.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);
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;
}
总结
以上就是自定义持久层框架的思路及内容,其中对sql语句解析的类时mybatis中自带的类。