自定义框架设计
使用端
需要提供核心配置文件:
- sqlMapConfig.xml:存在数据源信息,引入mapper.xml
- Mapper.xml:sql语句的配置文件信息
框架端
- 读取配置文件(sqlMapConfig.xml)
读取完成以后以流的形式存在,我们不能将读取到的配置信息以流的形式存放在内存中,不好操作,可以创建javaBean来存储- Configuration:存放数据库基本信息、Map<唯一标识,Mapper>(唯一标识:namespace+"."+id)
- MappedStatement:存放sql语句、statement类型、输入参数java类型、输出参数java类型
- 解析配置文件
创建sqlSessionFactoryBuilder类,其中创建build()方法(解析配置文件)- 使用dom4j解析配置文件,将解析出来的内容封装到Configuration和MappedStatement中;
- 创建sqlSessionFactory的实现类DefaultSqlSession,为了创建sqlSession(会话对象)做准备,这边使用到了工厂模式
- 创建sqlSessionFactory
方法:openSession():获取sqlSession的实现类对象 - 创建sqlSession接口及实现类
封装JDBC完成对数据库表的查询操作- selectList(String statementId, Object param)
- selectOne(String statementId, Object param)
- 创建Executor接口及实现类SimpleExecutor实现类
Query(COnfiguration, MappedStatement, Object…params):执行JDBC代码
自定义框架实现
使用端
sqlMapper.xml
<configuration>
<!--数据库配置信息-->
<dataSource>
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/mybatis_details"></property>
<property name="username" value="root"></property>
<property name="password" value="1234"></property>
</dataSource>
<!--存放mapper.xml的路径-->
<mapper resource="UserMapper.xml"></mapper>
</configuration>
Mapper.xml
<!--<mapper namespace="user">-->
<mapper namespace="com.sc.dao.IUserDao">
<!--sql的唯一标识:namespace.id来组成 : statementId-->
<!-- <select id="selectList" resultType="com.sc.entity.User">-->
<select id="findAll" resultType="com.sc.entity.User">
select * from user
</select>
<!-- <select id="selectOne" resultType="com.sc.entity.User" paramterType="com.sc.entity.User">-->
<select id="findByCondition" resultType="com.sc.entity.User" paramterType="com.sc.entity.User">
select * from user where id = #{id} and username = #{username}
</select>
</mapper>
框架端
1.保存配置文件信息对象
Configuration:存储sqlMapConfig.xml解析出来的信息
public class Configuration {
private DataSource dataSource;
/**
* key:statementId
* value:封装好的mappedStatement对象
*/
Map<String, MappedStatement> mappedStatementMap = new HashMap<String, MappedStatement>();
}
MappedStatement:存储Mapper.xml解析出来的信息
public class MappedStatement {
//id标识
private String id;
//返回值类型
private String resultType;
//参数值类型
private String paramType;
//sql语句
private String sql;
}
2.dom4j解析配置文件
通过dom4j解析配置文件,将解析出来的内容封装到Configuration和MappedStatement中
XMLConfigBuilder:解析xml
public class XMLConfigBuilder {
private Configuration configuration;
/**
* 初始化Configuration对象
*/
public XMLConfigBuilder() {
this.configuration = new Configuration();
}
/**
* 使用dom4j解析配置文件并封装到Configuration中
* @param in
* @return
*/
public Configuration parseConfig(InputStream in) throws DocumentException, PropertyVetoException {
Document document = new SAXReader().read(in);
Element el = document.getRootElement();
List<Element> list = el.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 = el.selectNodes("//mapper");
for (Element element : mapperList) {
String mapperPath = element.attributeValue("resource");
InputStream resourceAsStream = Resource.getResourceAsStream(mapperPath);
XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(configuration);
xmlMapperBuilder.parse(resourceAsStream);
}
return configuration;
}
}
Resource:通过xml路径获取xml并将起解析为字节流
public class Resource {
/**
* 根据配置文件路径,将配置文件加载成字节流,存储到内存中
* @param path
* @return
*/
public static InputStream getResourceAsStream(String path) {
InputStream resourceAsStream = Resource.class.getClassLoader().getResourceAsStream(path);
return resourceAsStream;
}
}
XMLMapperBuilder:解析Mapper.xml文件
public class XMLMapperBuilder {
private Configuration configuration;
public XMLMapperBuilder(Configuration configuration) {
this.configuration = configuration;
}
public void parse(InputStream inputStream) throws DocumentException {
Document document = new SAXReader().read(inputStream);
Element rootElement = document.getRootElement();
String namespace = rootElement.attributeValue("namespace");
List<Element> list = rootElement.selectN odes("//select");
for (Element element : list) {
String id = element.attributeValue("id");
String resultType = element.attributeValue("resultType");
String paramterType = element.attributeValue("paramterType");
String sqlText = element.getTextTrim();
// mappedStatement存储Mapper.xml sql语句相关信息
MappedStatement mappedStatement = new MappedStatement();
mappedStatement.setId(id);
mappedStatement.setResultType(resultType);
mappedStatement.setParamType(paramterType);
mappedStatement.setSql(sqlText);
String key = namespace + "." + id;
configuration.getMappedStatementMap().put(key, mappedStatement);
}
}
}
3.创建SqlSessionFactory
SqlSessionFactory:生成SqlSession的工厂
public interface SqlSessionFactory {
public SqlSession openSession();
}
DefaultSqlSessionFactory:SqlSessionFactory实现类
public class DefaultSqlSessionFactory implements SqlSessionFactory {
private Configuration configuration;
public DefaultSqlSessionFactory(Configuration configuration) {
this.configuration = configuration;
}
public SqlSession openSession() {
return new DefaultSqlSession(configuration);
}
}
4.创建SqlSession接口及实现类
SqlSession:封装CRUD方法
public interface SqlSession {
//查询所有
public <E> List<E> selectList(String statementId, Object... params);
//根据条件查询单个
public <T> T selectOne(String statementId, Object... params);
//为Dao生成代理实现类
public <T> T getMapper(Class<?> mapperClass);
}
DefaultSqlSession:SqlSession实现类
public class DefaultSqlSession implements SqlSession {
private Configuration configuration;
public DefaultSqlSession(Configuration configuration) {
this.configuration = configuration;
}
public <E> List<E> selectList(String statementId, Object... params) {
//完成对simpleExecutor里query方法的调用
SimpleExecutor simpleExecutor = new SimpleExecutor();
List<Object> list = simpleExecutor.query(configuration, configuration.getMappedStatementMap().get(statementId), params);
return (List<E>) list;
}
public <T> T selectOne(String statementId, Object... params) {
List<Object> objects = selectList(statementId, params);
if (objects.size()==1) {
return (T) objects.get(0);
} else {
throw new RuntimeException("查询结果为空或者结果过多");
}
}
@Override
public <T> T getMapper(Class<?> mapperClass) {
//使用JDK动态代理来为Dao接口生成代理对象并返回
Object proxyInstance = Proxy.newProxyInstance(DefaultSqlSession.class.getClassLoader(), new Class[]{mapperClass}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
/**
* proxy:当前代理对象的应用
* method:当前被调用方法的引用
* args:传递的参数
*/
//底层还是去执行JDBC代码,准备参数:statementId--sql语句唯一标识--namespace.id=接口全限定名.方法名(namespace跟对应的接口路径一致,sqlId跟接口方法名一致)
//准备参数1:statementId
String methodName = method.getName();//方法名
String className = method.getDeclaringClass().getName();//方法所在类的全限定名
String statementId = className + "." + methodName;
//准备参数2:params:args
//根据返回值类型选择调用selectList or selectOne
//获取被调用方法的返回值类型
Type genericReturnType = method.getGenericReturnType();
//判断是否进行了 泛型类型参数化
if (genericReturnType instanceof ParameterizedType) {
List<Object> objects = selectList(statementId, args);
return objects;
}
return selectOne(statementId, args);
}
});
return (T) proxyInstance;
}
}
5.创建Executor接口及实现类SimpleExecutor
Executor:JDBC底层接口
public interface Executor {
public <E> List<E> query(Configuration configuration, MappedStatement mappedStatement, Object... params);
}
SimpleExecutor:Executor实现类
public class SimpleExecutor implements Executor {
public <E> List<E> query(Configuration configuration, MappedStatement mappedStatement, Object... params) {
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
//1、注册驱动,获取链接
connection = configuration.getDataSource().getConnection();
//2、获取sql语句 select * from user where id = #{id} and username = #{username}
String sql = mappedStatement.getSql();
//3、转化sql语句 select * from user where id = ? and username = ?
BoundSql boundSql = getBoundSql(sql);
//4、获取预处理对象:preparedStatement
preparedStatement = connection.prepareStatement(boundSql.getSqlText());
//5、设置参数
//获取参数的全路径
String paramType = mappedStatement.getParamType();
Class<?> paramClass = getClassType(paramType);
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
for (int i = 0; i < parameterMappings.size(); i++) {
ParameterMapping parameterMapping = parameterMappings.get(i);
String content = parameterMapping.getContent();
//反射
Field declaredField = paramClass.getDeclaredField(content);
//暴力访问, private私有化
declaredField.setAccessible(true);
Object o = declaredField.get(params[0]);
//设置参数
preparedStatement.setObject(i+1, o);
}
//6、执行sql
resultSet = preparedStatement.executeQuery();
//7、封装返回结果集
String resultType = mappedStatement.getResultType();
Class<?> resultTypeClass = getClassType(resultType);
ArrayList<Object> objects = new ArrayList<Object>();
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;
} catch (SQLException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IntrospectionException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
private Class<?> getClassType(String paramsType) throws ClassNotFoundException {
if (paramsType != null) {
Class<?> aClass = Class.forName(paramsType);
return aClass;
}
return null;
}
/**
* 完成对#{}的解析工作
* 1.将#{}使用?进行代替
* 2.解析出#{}里面的值进行代替
* @param sql
* @return
*/
private BoundSql getBoundSql(String sql) {
//标记处理类:配置标记解析器来完成对占位符的解析处理工作
ParameterMappingTokenHandler parameterMappingTokenHandler = new ParameterMappingTokenHandler();
GenericTokenParser genericTokenParser = new GenericTokenParser("#{", "}", parameterMappingTokenHandler);
//解析出来的sql
String parseSql = genericTokenParser.parse(sql);
System.out.println("解析后的sql:" + parseSql);
//#{}里面解析出来的参数名称
List<ParameterMapping> parameterMappings = parameterMappingTokenHandler.getParameterMappings();
BoundSql boundSql = new BoundSql(parseSql, parameterMappings);
return boundSql;
}
}
代码地址
github:https://github.com/CurryKobe/IPersistence
我只是觉得自己不仅仅如此,所以在努力呀!