长文干货 | 手写自定义持久层框架!
Baoxing
读完需要
速读仅需 38 分钟
为何要手写自定义持久层框架?
JDBC 编码的弊端
会造成硬编码问题(无法灵活切换数据库驱动) 频繁创建和释放数据库连接造成系统资源浪费 影响系统性能 sql 语句存在硬编码,造成代码不易维护,实际应用中 sql 变化可能较大,变动 sql 需要改 Java 代码 使用 preparedStatement 向占有位符号传参数存在硬编码, 因 sql 语句的 where 条件不确定甚至没有where条件,修改 sql 还要修改代码 系统不易维护 对结果集解析也存在硬编码, sql变化导致解析代码变化
更有助于读 mybatis 持久层框架源码
JDBC代码
public class jdbcConnection {
private static Connection connection = null;
private static PreparedStatement preparedStatement = null;
private static ResultSet resultSet = null;
public static void main(String[] args) {
try {
// 加载数据库驱动
Class.forName("com.mysql.jdbc.Driver");
// 通过驱动管理类获取数据库连接
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/huodd", "root", "1234");
// 定义sql语句 ? 表示占位符
String sql = "select id,username from user where id = ?";
// 获取预处理对象 statement
PreparedStatement preparedStatement = (PreparedStatement) connection.prepareStatement(sql);
// 设置参数 第一个参数为 sql 语句中参数的序号(从1开始) 第二个参数为 设置的参数值
preparedStatement.setInt(1, 1);
// 向数据库发出sql执行查询 查询出结果集
resultSet = preparedStatement.executeQuery();
// 遍历查询结果集
while (resultSet.next()) {
int id = resultSet.getInt("id");
String username = resultSet.getString("username");
// 封装对象
User user = new User();
user.setId(id);
user.setUsername(username);
System.out.println(user);
}
} catch (Exception ex) {
ex.printStackTrace();
} finally {
try {
// 释放资源
if (resultSet != null) {
resultSet.close();
}
if (preparedStatement != null) {
preparedStatement.close();
}
if (connection != null) {
connection.close();
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
}
解决问题的思路
数据库频繁创建连接、释放资源 -> 连接池 sql语句及参数硬编码 -> 配置文件 手动解析封装结果集 -> 反射、内省
编码前思路整理
创建、读取配置文件
sqlMapConfig.xml 存放数据库配置信息 userMapper.xml :存放sql配置信息 根据配置文件的路径,加载配置文件成字节输入流,存储在内存中Resources#getResourceAsStream(String path) 创建两个JavaBean存储配置文件解析出来的内容 Configuration :核心配置类 ,存放 sqlMapConfig.xml解析出来的内容 MappedStatement:映射配置类:存放mapper.xml解析出来的内容
解析配置文件(使用dom4j)
创建类:SqlSessionFactoryBuilder#build(InputStream in) -> 设计模式之构建者模式 使用dom4j解析配置文件,将解析出来的内容封装到容器对象(JavaBean)中
创建 SqlSessionFactory 接口及实现类DefaultSqlSessionFactory
SqlSessionFactory对象,生产sqlSession会话对象 -> 设计模式之工厂模式
创建 SqlSession接口及实现类DefaultSqlSession
定义对数据库的CRUD操作 selectList() selectOne() update() delete()
创建Executor接口及实现类SimpleExecutor实现类
query(Configuration configuration, MappedStatement mapStatement, Object... orgs) 执行的就是JDBC代码
测试代码
用到的设计模式
构建者模式 工厂模式 代理模式
进入编码
1.创建、读取配置文件
sqlMapConfig.xml 存放数据库配置信息
<configuration>
<dataSource>
<property name="driverClass" value="com.mysql.jdbc.Driver">property>
<property name="jdbcUrl" value="jdbc:mysql:///huodd">property>
<property name="user" value="root">property>
<property name="password" value="1234">property>
dataSource>
<mapper resource="userMapper.xml">mapper>
configuration>
userMapper.xml 存放sql配置信息
<mapper namespace="user">
<select id="selectList" resultType="com.huodd.pojo.User" paramterType="com.huodd.pojo.User">
select * from user
select>
<select id="selectOne" paramterType="com.huodd.pojo.User" resultType="com.huodd.pojo.User">
select * from user where id = #{id} and username =#{username}
select>
mapper>
User.java
public class User {
private Integer id;
private String username;
... 省略getter setter 方法
... 省略 toString 方法
}
pom.xml 中引入依赖
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.17version>
dependency>
<dependency>
<groupId>c3p0groupId>
<artifactId>c3p0artifactId>
<version>0.9.1.2version>
dependency>
<dependency>
<groupId>log4jgroupId>
<artifactId>log4jartifactId>
<version>1.2.12version>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.10version>
dependency>
<dependency>
<groupId>dom4jgroupId>
<artifactId>dom4jartifactId>
<version>1.6.1version>
dependency>
<dependency>
<groupId>jaxengroupId>
<artifactId>jaxenartifactId>
<version>1.1.6version>
dependency>
创建两个JavaBean对象 用于存储解析的配置文件的内容(Configuration.java、MappedStatement.java)
public class Configuration {
// 数据源
private DataSource dataSource;
//map集合 key:statementId value:MappedStatement
private Map mappedStatementMap = new HashMap<>();
... 省略getter setter 方法
}
public class MappedStatement {
// id
private String id;
// sql 语句
private String sql;
// 参数值类型
private Class paramterType;
// 返回值类型
private Class resultType;
... 省略getter setter 方法
}
创建Resources工具类 并编写静态方法getResourceAsSteam(String path)
public class Resources {
/**
* 根据配置文件的路径 将配置文件加载成字节输入流 存储在内存中
* @param path
* @return InputStream
*/
public static InputStream getResourceAsStream(String path) {
InputStream resourceAsStream = Resources.class.getClassLoader().getResourceAsStream(path);
return resourceAsStream;
}
}
2.解析配置文件(使用dom4j)
创建 SqlSessionFactoryBuilder类 并添加 build 方法
public class SqlSessionFactoryBuilder {
public SqlSessionFactory build (InputStream in) throws DocumentException, PropertyVetoException, ClassNotFoundException {
// 1. 使用 dom4j 解析配置文件 将解析出来的内容封装到Configuration中
XMLConfigerBuilder xmlConfigerBuilder = new XMLConfigerBuilder();
// configuration 是已经封装好了sql信息和数据库信息的对象
Configuration configuration = xmlConfigerBuilder.parseConfig(in);
// 2. 创建 SqlSessionFactory 对象 工厂类 主要是生产sqlSession会话对象
DefaultSqlSessionFactory defaultSqlSessionFactory = new DefaultSqlSessionFactory(configuration);
return defaultSqlSessionFactory;
}
}
public class XMLConfigerBuilder {
private Configuration configuration;
public XMLConfigerBuilder() {
this.configuration = new Configuration();
}
/**
* 该方法 使用dom4j对配置文件进行解析 封装Configuration
* @param in
* @return
*/
public Configuration parseConfig (InputStream in) throws DocumentException, PropertyVetoException, ClassNotFoundException {
Document document = new SAXReader().read(in);
//
Element rootElement = document.getRootElement();
List propertyElements = rootElement.selectNodes("//property");
Properties properties = new Properties();
for (Element propertyElement : propertyElements) {
properties.setProperty(propertyElement.attributeValue("name"), propertyElement.attributeValue("value"));
}
// 连接池
ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
comboPooledDataSource.setDriverClass(properties.getProperty("driverClass"));
comboPooledDataSource.setJdbcUrl(properties.getProperty("jdbcUrl"));
comboPooledDataSource.setUser(properties.getProperty("user"));
comboPooledDataSource.setPassword(properties.getProperty("password"));
// 填充 configuration
configuration.setDataSource(comboPooledDataSource);
// mapper 部分 拿到路径 -> 字节输入流 -> dom4j进行解析
List mapperElements = rootElement.selectNodes("//mapper");
XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(configuration);
for (Element mapperElement : mapperElements) {
String mapperPath = mapperElement.attributeValue("resource");
InputStream resourceAsStream = Resources.getResourceAsStream(mapperPath);
xmlMapperBuilder.parse(resourceAsStream);
}
return configuration;
}
}
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 select = rootElement.selectNodes("//select");
for (Element element : select) {
// 获取 id 的值
String id = element.attributeValue("id");
String paramterType = element.attributeValue("paramterType");
String resultType = element.attributeValue("resultType");
// 输入参数 class
Class paramterTypeClass = getClassType(paramterType);
// 返回结果 class
Class resultTypeClass = getClassType(resultType);
// sql 语句
String sqlStr = element.getTextTrim();
// 封装 mappedStatement
MappedStatement mappedStatement = new MappedStatement();
mappedStatement.setId(id);
mappedStatement.setParamterType(paramterTypeClass);
mappedStatement.setResultType(resultTypeClass);
mappedStatement.setSql(sqlStr);
// statementId
String key = namespace + "." + id;
// 填充 configuration
configuration.getMappedStatementMap().put(key, mappedStatement);
}
}
private Class getClassType(String paramterType) throws ClassNotFoundException {
Class aClass = Class.forName(paramterType);
return aClass;
}
}
3.创建 SqlSessionFactory 接口及实现类DefaultSqlSessionFactory
public interface SqlSessionFactory {
SqlSession openSession();
}
public class DefaultSqlSessionFactory implements SqlSessionFactory {
private Configuration configuration;
public DefaultSqlSessionFactory(Configuration configuration) {
this.configuration = configuration;
}
@Override
public SqlSession openSession() {
return new DefaultSqlSession(configuration);
}
}
4. 创建 SqlSession接口及实现类DefaultSqlSession
public interface SqlSession {
List selectList(String statementId, Object... param) throws Exception ;
T selectOne(String statementId, Object... params) throws Exception;
void close() throws SQLException;
}
public class DefaultSqlSession implements SqlSession {
private Configuration configuration;
// 处理器对象
private Executor simpleExcutor = new SimpleExecutor();
public DefaultSqlSession(Configuration configuration) {
this.configuration = configuration;
}
@Override
public List selectList(String statementId, Object... param) throws Exception {
// 完成对 simpleExcutor里的query方法的调用
MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementId);
List list = simpleExcutor.query(configuration, mappedStatement, param);
return list;
}
@Override
public T selectOne(String statementId, Object... params) throws Exception {
List
浏览
44评论