自定义持久层框架
前言
例如:为什么连接数据库不再使用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 {
XMLConfigBuilder xmlConfigBuilder = new XMLConfigBuilder();
Configuration configuration = xmlConfigBuilder.parseConfig(in);
DefaultSqlSessionFactory defaultSqlSessionFactory = new DefaultSqlSessionFactory(configuration);
return defaultSqlSessionFactory;
}
XMLConfigBuilder.java代码如下:
public Configuration parseConfig(InputStream in) throws DocumentException, PropertyVetoException {
Document document = new SAXReader().read(in);
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);
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);
}
}
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 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 {
Connection connection = configuration.getDataSource().getConnection();
String sql = mappedStatement.getSql();
BoundSql boundSql = getBoundSql(sql);
PreparedStatement preparedStatement = connection.prepareStatement(boundSql.getSqlText());
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);
}
ResultSet resultSet = preparedStatement.executeQuery();
String resultType = mappedStatement.getResultType();
Class<?> resultTypeClass = getClassType(resultType);
ArrayList<Object> objects = new ArrayList<>();
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中自带的类。
|