框架端
定义数据源配置和映射语句实体
Configuration
代码语言:javascript复制@Data
public class Configuration {
private DataSource dataSource;
/**
* key: statementId value:封装好的mappedStatement对象
*/
Map<String,MappedStatement> mappedStatementMap = new HashMap<>();
}
MappedStatement
代码语言:javascript复制@Data
public final class MappedStatement {
private String id;
/**
* sql语句
*/
private String sql;
/**
* 参数值类型
*/
private Class<?> parameterType;
/**
* 返回值类型
*/
private Class<?> resultType;
}
定义配置文件访问的工具类
代码语言:javascript复制public class Resources {
/**
* 根据配置文件的路径,将配置文件加载成字节输入流,存储在内存中
*
* @param path
* @return
*/
public static InputStream getResourceAsSteam(String path) throws IOException {
InputStream in = Resources.class.getClassLoader().getResourceAsStream(path);
if (in == null) {
throw new IOException("找不到资源" path);
}
return in;
}
}
定义SqlSessionFactoryBuilder
代码语言:javascript复制public class SqlSessionFactoryBuilder {
private Configuration configuration;
public SqlSessionFactoryBuilder() {
this.configuration = new Configuration();
}
public SqlSessionFactory build(InputStream inputStream) throws
DocumentException, PropertyVetoException, ClassNotFoundException {
//1.dom4j解析配置⽂件,封装Configuration
XMLConfigerBuilder xmlConfigerBuilder = new
XMLConfigerBuilder(configuration);
Configuration configuration =
xmlConfigerBuilder.parseConfiguration(inputStream);
//2.创建SqlSessionFactory对象(工厂类),生产sqlSession:会话对象
SqlSessionFactory sqlSessionFactory = new
DefaultSqlSessionFactory(configuration);
return sqlSessionFactory;
}
}
数据源配置和映射语句解析XMLConfigBuilder、XMLMapperBuilder
XMLConfigBuilder
代码语言:javascript复制public class XMLConfigerBuilder {
private Configuration configuration;
public XMLConfigerBuilder() {
this.configuration = new Configuration();
}
/**
* 该方法就是使用dom4j对配置文件进行解析,封装Configuration
*/
public Configuration parseConfig(InputStream inputStream) throws DocumentException, PropertyVetoException, IOException, ClassNotFoundException {
Document document = new SAXReader().read(inputStream);
//<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解析: 拿到路径--字节输入流---dom4j进行解析
List<Element> mapperList = rootElement.selectNodes("//mapper");
for (Element element : mapperList) {
String mapperPath = element.attributeValue("resource");
InputStream resourceAsSteam = Resources.getResourceAsSteam(mapperPath);
XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(configuration);
xmlMapperBuilder.parse(resourceAsSteam);
}
return configuration;
}
}
XMLMapperBuilder
代码语言:javascript复制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<Element> select = rootElement.selectNodes("select");
for (Element element : select) {
//id的值
String id = element.attributeValue("id");
String parameterType = element.attributeValue("parameterType");
String resultType = element.attributeValue("resultType");
//输⼊参数class
Class<?> parameterTypeClass = getClassType(parameterType);
//返回结果class
Class<?> resultTypeClass = getClassType(resultType);
//statementId
String key = namespace "." id;
//sql语句
String textTrim = element.getTextTrim();
//封装 mappedStatement
MappedStatement mappedStatement = new MappedStatement();
mappedStatement.setId(id);
mappedStatement.setParameterType(parameterTypeClass);
mappedStatement.setResultType(resultTypeClass);
mappedStatement.setSql(textTrim);
//填充 configuration
configuration.getMappedStatementMap().put(key, mappedStatement);
}
}
private Class<?> getClassType (String parameterType) throws
ClassNotFoundException {
Class<?> aClass = Class.forName(parameterType);
return aClass;
}
}
SqlSessionFactory 接口及DefaultSqlSessionFactory 实现类
SqlSessionFactory
代码语言:javascript复制public interface SqlSessionFactory {
SqlSession openSession();
}
DefaultSqlSessionFactory
代码语言:javascript复制public class DefaultSqlSessionFactory implements SqlSessionFactory {
private Configuration configuration;
public DefaultSqlSessionFactory(Configuration configuration) {
this.configuration = configuration;
}
@Override
public SqlSession openSession() {
return new DefaultSqlSession(configuration);
}
}
SqlSession 接口及 DefaultSqlSession 实现类
SqlSession
代码语言:javascript复制public interface SqlSession {
/**
* 查询所有
*
* @param statementId
* @param params
* @param <E>
* @return
* @throws Exception
*/
<E> List<E> selectList(String statementId, Object... params) throws Exception;
/**
* 根据条件查询单个
*
* @param statementId
* @param params
* @param <T>
* @return
* @throws Exception
*/
<T> T selectOne(String statementId, Object... params) throws Exception;
}
DefaultSqlSession
代码语言:javascript复制public class DefaultSqlSession implements SqlSession {
private Configuration configuration;
public DefaultSqlSession(Configuration configuration) {
this.configuration = configuration;
}
@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;
}
@Override
public <T> T selectOne(String statementid, Object... params) throws Exception {
List<Object> objects = selectList(statementid, params);
if (objects.size() == 1) {
return (T) objects.get(0);
} else {
throw new RuntimeException("查询结果为空或者返回结果过多");
}
}
}
SQL执行器接口和实现类Executor、SimpleExecutor
Executor
代码语言:javascript复制public interface Executor {
/**
* 执行查询SQL
* @param configuration
* @param mappedStatement
* @param params
* @param <E>
* @return
* @throws Exception
*/
<E> List<E> query(Configuration configuration, MappedStatement mappedStatement, Object... params) throws Exception;
}
SimpleExecutor
代码语言:javascript复制/**
* SQL执行器实现
*
* @author zjq
* @date 2022/3/15
*/
public class SimpleExecutor implements Executor {
@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}
//转换sql语句: select * from user where id = ? and username = ? ,转换的过程中,还需要对#{}里面的值进行解析存储
String sql = mappedStatement.getSql();
BoundSql boundSql = getBoundSql(sql);
// 3.获取预处理对象:preparedStatement
PreparedStatement preparedStatement = connection.prepareStatement(boundSql.getSqlText());
// 4. 设置参数
//获取到了参数的全路径
String paramterType = mappedStatement.getParamterType();
Class<?> paramtertypeClass = getClassType(paramterType);
List<ParameterMapping> parameterMappingList = boundSql.getParameterMappingList();
for (int i = 0; i < parameterMappingList.size(); i ) {
ParameterMapping parameterMapping = parameterMappingList.get(i);
String content = parameterMapping.getContent();
//反射
Field declaredField = paramtertypeClass.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);
Class<?> resultTypeClass = mappedStatement.getResultType();
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;
}
private Class<?> getClassType(String paramterType) throws ClassNotFoundException {
if (paramterType != null) {
Class<?> aClass = Class.forName(paramterType);
return aClass;
}
return null;
}
/**
* 完成对#{}的解析工作:1.将#{}使用?进行代替,2.解析出#{}里面的值进行存储
*
* @param sql
* @return
*/
private BoundSql getBoundSql(String sql) {
//标记处理类:配置标记解析器来完成对占位符的解析处理工作,主要是配合通⽤标记解析器GenericTokenParser类完成对配置⽂件等的解析⼯作,其中TokenHandler主要完成处理
ParameterMappingTokenHandler parameterMappingTokenHandler = new ParameterMappingTokenHandler();
//GenericTokenParser :通⽤的标记解析器,完成了代码⽚段中的占位符的解析,然后再根据给定的标记处理器(TokenHandler)来进⾏表达式的处理
//三个参数:分别为openToken (开始标记)、closeToken (结束标记)、handler (标记处理器)
GenericTokenParser genericTokenParser = new GenericTokenParser("#{", "}", parameterMappingTokenHandler);
//解析出来的sql
String parseSql = genericTokenParser.parse(sql);
//#{}里面解析出来的参数名称
List<ParameterMapping> parameterMappings = parameterMappingTokenHandler.getParameterMappings();
BoundSql boundSql = new BoundSql(parseSql, parameterMappings);
return boundSql;
}
}
使用端
创建 sqlMapConfig.xml
代码语言:javascript复制<configuration>
<!--数据库配置信息-->
<dataSource>
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql:///my-orm-framework"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</dataSource>
<!--存放mapper.xml的全路径-->
<mapper resource="UserMapper.xml"></mapper>
</configuration>
创建对应的mapper.xml
代码语言:javascript复制<mapper namespace="com.zjq.dao.IUserDao">
<!--sql的唯一标识:namespace.id来组成 : statementId-->
<select id="findAll" resultType="com.zjq.pojo.User" >
select * from user
</select>
<!--
User user = new User()
user.setId(1);
user.setUsername("zhangsan")
-->
<select id="findByCondition" resultType="com.zjq.pojo.User" paramterType="com.zjq.pojo.User">
select * from user where id = #{id} and username = #{username}
</select>
</mapper>
UserDao
代码语言:javascript复制public interface IUserDao {
//查询所有用户
public List<User> findAll() throws Exception;
//根据条件进行用户查询
public User findByCondition(User user) throws Exception;
}
User实体
代码语言:javascript复制@Data
public class User {
private Long id;
private String username;
private String password;
}
框架查询验证
代码语言:javascript复制 @Test
public void test() throws Exception {
InputStream resourceAsSteam = Resources.getResourceAsSteam("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsSteam);
SqlSession sqlSession = sqlSessionFactory.openSession();
//调用
User user = new User();
user.setId(1);
user.setUsername("张三");
User user2 = sqlSession.selectOne("user.selectOne", user);
System.out.println(user2);
List<User> users = sqlSession.selectList("user.selectList");
for (User user1 : users) {
System.out.println(user1);
}
}