一、开发环境的准备及统一
1、 Jdk环境:JDK 1.8 64bit
2、 Maven环境:MAVEN 3.3.9
二、创建Maven工程并引入坐标
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.william</groupId><artifactId>DIYMybatisFramework</artifactId><version>1.0-SNAPSHOT</version><properties><maven.compiler.source>1.8</maven.compiler.source><maven.compiler.target>1.8</maven.compiler.target></properties><dependencies><!--解析xml--><dependency><groupId>dom4j</groupId><artifactId>dom4j</artifactId><version>1.6.1</version></dependency><dependency><groupId>jaxen</groupId><artifactId>jaxen</artifactId><version>1.1.6</version></dependency></dependencies>
</project>
三、 自定义Mybatis框架配置文件结构约定
1.Sql文件约定
<?xml version="1.0" encoding="utf-8" ?>
<mapper namespace="com.william.dao.UserDao"><select id="findAll" resultType="com.william.domain.User">select * from user</select>
</mapper>
2.框架核心配置文件约定
<?xml version="1.0" encoding="UTF-8" ?>
<configuration><environments default="development"><environment id="development"><transactionManager type="JDBC" /><dataSource type="POOLED"><property name="driver" value="com.mysql.jdbc.Driver" /><property name="url" value="jdbc:mysql://127.0.0.1:3306/web02?characterEncoding=utf8" /><property name="username" value="root" /><property name="password" value="root" /></dataSource></environment></environments><mappers><mapper resource="com/william/mapper/UserMapper.xml"></mapper></mappers>
</configuration>
四、核心组件开发
1.Mapper开发
根据Jdbc存在问题的分析,我们已经知道了要将SQL语句放入配置文件中,这样将来修改SQL语句会比较方便,但放在配置文件中的SQL语句还需要读取出来,这样我们就可以基于面向对象思维定义一个Mapper类,用于将配置文件中的SQL语句保存起来,使用时更方便。定义如下:
package Framework.domain;
/*** @author :lijunxuan* @date :Created in 2019/7/9 12:14* @description :* @version: 1.0*/
public class Mapper {private String resultType;private String sql;public String getResultType() {return resultType;}public void setResultType(String resultType) {this.resultType = resultType;}public String getSql() {return sql;}public void setSql(String sql) {this.sql = sql;}
}
2.Configuration开发
我们以面向对象思维操作自定义框架的核心配置文件时,需要有一个实体类与之对应。Configuration 配置类主要用于保存SqlMapConfig.xml文件中读取的xml结点的信息,以及映射的SQL语句的集合。定义如下:
package Framework.domain;import java.util.HashMap;
import java.util.Map;/*** @author :lijunxuan* @date :Created in 2019/7/9 12:16* @description :* @version: 1.0*/
public class Configuration {private String driverClass;private String username;private String password;private String url;private Map<String,Mapper> xmlMap =new HashMap<>();@Overridepublic String toString() {return "Configuration{" +"driverClass='" + driverClass + '\'' +", username='" + username + '\'' +", password='" + password + '\'' +", url='" + url + '\'' +", xmlMap=" + xmlMap +'}';}public String getDriverClass() {return driverClass;}public void setDriverClass(String driverClass) {this.driverClass = driverClass;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public String getUrl() {return url;}public void setUrl(String url) {this.url = url;}public Map<String, Mapper> getXmlMap() {return xmlMap;}public void setXmlMap(Map<String, Mapper> xmlMap) {this.xmlMap = xmlMap;}
}
3.SqlSession接口开发
package Framework.dao;import java.util.List;public interface SqlSession {/**** @param mapperId 唯一的标识* @return*/public List selectList(String mapperId);/*** 释放资源*/public void close();
}
4.SqlSessionImpl实现类开发
package Framework.dao.Impl;import Framework.dao.SqlSession;
import Framework.domain.Configuration;
import Framework.domain.Mapper;
import Framework.utils.Executer;import java.util.List;/*** @author :lijunxuan* @date :Created in 2019/7/9 12:27* @description :* @version: 1.0*/
public class SqlSessionImpl implements SqlSession {private Configuration cfg;private Executer ex;public SqlSessionImpl(Configuration cfg) {this.cfg = cfg;ex=new Executer(cfg);}@Overridepublic List selectList(String mapperId) {//获取sql语句,结果类型Mapper mapper = cfg.getXmlMap().get(mapperId);String sql = mapper.getSql();String resultType = mapper.getResultType();//执行查询操作return ex.executeQuery(sql,resultType);}@Overridepublic void close() {ex.close();}
}
5.Executor执行器开发
Executor类,用于实现SQL语句的执行,主要是调用JDBC来实现SQL语句的执行。
package Framework.utils;
import Framework.domain.Configuration;
import java.lang.reflect.Method;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
/*** sql操作的执行器(工具类)* @author :lijunxuan* @date :Created in 2019/7/9 12:33* @description :* @version: 1.0*/
public class Executer {private Configuration cfg;public Executer(Configuration cfg) {this.cfg = cfg;}private Connection connection;private PreparedStatement preparedStatement;private ResultSet resultSet;/*** 执行sql查询操作,返回集合* @param sql* @param resultType* @return*/public List executeQuery(String sql, String resultType) {List list=new ArrayList();//1.注册驱动try {Class.forName(cfg.getDriverClass());} catch (ClassNotFoundException e) {e.printStackTrace();}//2.获取连接try {connection = DriverManager.getConnection(cfg.getUrl(), cfg.getUsername(), cfg.getPassword());//获取preparedStatement对象preparedStatement = connection.prepareStatement(sql);//3.执行sql语句resultSet = preparedStatement.executeQuery();//获取数据库的元数据:修饰代码的代码(修饰数据的数据)ResultSetMetaData metaData = resultSet.getMetaData();//获取列的个数int columnCount = metaData.getColumnCount();//创建一个列名的集合List<String> columnNames=new ArrayList<>();//获取所有的列名//列的索引从1开始for (int i = 1; i <=columnCount; i++) {String columnName = metaData.getColumnName(i);columnNames.add(columnName);}//获取实体类对象的字节码文件Class clazz = Class.forName(resultType);//根据字节码反射获取实体类中的所有方法Method[] methods = clazz.getMethods();//setUsername set + username//封装结果集(不体现具体的属性和字段名)while (resultSet.next()){//如果resultSet.next()为true,就应该new一个对象//通过反射来获取对象Object o = clazz.newInstance();for (String columnName : columnNames) {for (Method method : methods) {//比较 set +列名 与方法名if (("set"+columnName).equalsIgnoreCase(method.getName())){//如果相同,就找到列对应的set方法//获取列对应的数据Object columnValue = resultSet.getObject(columnName);//反射调用set方法给属性赋值//参数1:需要赋值的对象//参数2: 方法的参数method.invoke(o,columnValue);}}}list.add(o);}//4.处理结果集} catch (Exception e) {e.printStackTrace();}return list;}/*** 释放资源*/public void close(){if (connection!=null){try {connection.close();} catch (SQLException e) {e.printStackTrace();}}if (preparedStatement!=null){try {preparedStatement.close();} catch (SQLException e) {e.printStackTrace();}}if (resultSet!=null){try {resultSet.close();} catch (SQLException e) {e.printStackTrace();}}}
}
6.SqlSessionFactory开发
SqlSessionFactory的开发基于工厂模式,工厂模式是我们最常用的用来实例化对象的设计模式,是用工厂方法代替new操作的一种模式。创建对象的时候使用工厂模式会带来更大的可扩展性和尽量少的修改量。
package Framework.factory;import Framework.dao.Impl.SqlSessionImpl;
import Framework.dao.SqlSession;
import Framework.domain.Configuration;/*** 使用工厂模式创建sqlSession对象* @author :lijunxuan* @date :Created in 2019/7/9 15:38* @description :* @version: 1.0*/
public class SqlSessionFactory {private Configuration cgf;public SqlSessionFactory(Configuration cgf) {this.cgf = cgf;}public SqlSession openSession(){return new SqlSessionImpl(cgf);}
}
7.SqlSessionFactoryBuilder开发
package Framework.factory;import Framework.domain.Configuration;
import Framework.domain.Mapper;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;import java.io.InputStream;
import java.util.List;/*** 使用构建者模式创建SqlSessionFactory对象* 解析xml文件,数据存入Configuration对象中* @author :lijunxuan* @date :Created in 2019/7/9 15:41* @description :* @version: 1.0*/
public class SqlSessionFactoryBuilder {private InputStream inputStream;public SqlSessionFactory build(InputStream inputStream){this.inputStream=inputStream;Configuration cfg=new Configuration();loadCongiguration(cfg);return new SqlSessionFactory(cfg);}/*** 解析xml文件,把xml中的数据读入到实体类中*/private void loadCongiguration(Configuration cfg) {//创建SAXReader对象SAXReader saxReader = new SAXReader();//读取流对象,获取文档对象Document doc =null;try {doc = saxReader.read(inputStream);} catch (DocumentException e) {e.printStackTrace();}//解析文档对象,获取根节点Element rootElement = doc.getRootElement();//获取文档中的所有的propertyList<Element> propertyList = rootElement.selectNodes("//property");//遍历集合对象for (Element propertyElement : propertyList) {//获取节点的name属性值和value属性值String nameValue = propertyElement.attributeValue("name");String valueValue = propertyElement.attributeValue("value");if ("driver".equals(nameValue)){cfg.setDriverClass(valueValue);}if ("url".equals(nameValue)){cfg.setUrl(valueValue);}if ("username".equals(nameValue)){cfg.setUsername(valueValue);}if ("password".equals(nameValue)){cfg.setPassword(valueValue);}}//开始解析mappers标签//获取根节点Element root1 =doc.getRootElement();//获取mappers节点Element mappers = root1.element("mappers");//获取所有mapper节点List<Element> mapperList = mappers.elements("mapper");//循环集合for (Element element : mapperList) {String path = element.attributeValue("resource");loadXmlConfiguration(path,cfg);}}/*** 解析xml映射文件* @param path* @param cfg*/private void loadXmlConfiguration(String path, Configuration cfg) {SAXReader saxReader = new SAXReader();//根据路径获取流对象InputStream inputStream = SqlSessionFactoryBuilder.class.getClassLoader().getResourceAsStream(path);Document document = null;try {document = saxReader.read(inputStream);} catch (DocumentException e) {e.printStackTrace();}//获取根节点Element rootElement = document.getRootElement();//获取namespace的属性值String namespace = rootElement.attributeValue("namespace");/*** element(name) 获取第一个名称为name的子节点* elements(name): 获取所有名称为name的子节点* elements(): 获取的是所有的子节点*///获取根节点下的所有子节点List<Element> elements = rootElement.elements();for (Element element : elements) {String idValue = element.attributeValue("id");String resultTypeValue = element.attributeValue("resultType");String sql=element.getTextTrim();/*** key:唯一的标识*/String key=namespace+"."+idValue;Mapper mapper = new Mapper();mapper.setSql(sql);mapper.setResultType(resultTypeValue);cfg.getXmlMap().put(key,mapper);}}
}