开始编码前的准备工作:
1.pom.xml加入apache框架基本工具包、dom4j XML文件解析工具、mysql驱动包。
核心配置管理器Configuration主要是基于目前主流的dom4j组件来解析XML配置文件
注意:不要引入多余的MyBatis或Hibernate相关Jar包,因为我们是自己手写实现整个持久层框架执行过程实现ORM
2.src/main/resources目录下新增数据源配置文件dataSource.xml
3.生成持久类、映射接口、映射文件
package com.tcf.kid.smart.framework.entity;
/***
* TODO TCF 用户信息
* @author 71485
*
*/
public class UserInfo {
//TODO TCF 主键ID
private String id;
//TODO TCF 姓名
private String name;
//TODO TCF 性别
private String sex;
//TODO TCF 年龄
private Integer age;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
package com.tcf.kid.smart.framework.dao;
import com.tcf.kid.smart.framework.entity.UserInfo;
/***
* TODO TCF 用户信息持久层接口
* @author 71485
*
*/
public interface UserInfoMapper {
public UserInfo selectById(String id);
}
手写实现步骤:
1.定义模型类封装映射文件的持久化方法以及映射器接口和映射方法之间的关联关系
Function 定义SQL持久化方法相关信息,包括SQL语句类型、SQL语句、方法名、参数类型、返回值类型
package com.tcf.kid.smart.framework.model;
/***
* TODO TCF 封装映射器接口中的持久化方法信息
* @author 71485
*
*/
public class Function {
//TODO TCF 映射的SQL语句
private String sql;
//TODO TCF SQL语句类型(SQL前缀)
private String sqlType;
//TODO TCF 方法名
private String methodName;
//TODO TCF 返回值类型
private Object returnType;
//TODO TCF 参数类型
private String parameterType;
public String getSql() {
return sql;
}
public void setSql(String sql) {
this.sql = sql;
}
public String getSqlType() {
return sqlType;
}
public void setSqlType(String sqlType) {
this.sqlType = sqlType;
}
public String getMethodName() {
return methodName;
}
public void setMethodName(String methodName) {
this.methodName = methodName;
}
public Object getReturnType() {
return returnType;
}
public void setReturnType(Object returnType) {
this.returnType = returnType;
}
public String getParameterType() {
return parameterType;
}
public void setParameterType(String parameterType) {
this.parameterType = parameterType;
}
}
MapperBean封装映射器接口相关信息,包括映射器接口名(映射文件命名空间名)、映射器接口中定义的所有持久化方法
package com.tcf.kid.smart.framework.model;
import java.util.List;
/***
* TODO TCF 映射器接口封装
* TODO TCF 映射器接口名、接口中的所有方法
* @author 71485
*
*/
public class MapperBean {
//TODO TCF 映射器接口名
private String interfaceName;
//TODO TCF 映射器接口中的所有方法
private List
public String getInterfaceName() {
return interfaceName;
}
public void setInterfaceName(String interfaceName) {
this.interfaceName = interfaceName;
}
public List
return functions;
}
public void setFunctions(List
this.functions = functions;
}
}
2.定义MyBatis核心配置类Configuration,作用是读取数据源配置文件加载数据源信息、读取映射文件加载映射器接口信息并和映射器接口中定义的所有持久化方法建立关联关系
package com.tcf.kid.smart.framework.conf;
import java.io.InputStream;
import java.sql.DriverManager;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import com.tcf.kid.smart.framework.model.Function;
import com.tcf.kid.smart.framework.model.MapperBean;
import java.sql.*;
/***
* TODO TCF MyBatis核心配置工具类
* TODO TCF 读取数据源文件加载数据源
* TODO TCF 根据映射文件名读取映射文件
* @author 71485
*
*/
public class Configuration {
//TODO TCF 获取类加载器
public ClassLoader getClassLoader()
{
return Thread.currentThread().getContextClassLoader();
}
//TODO TCF 读取数据源文件,注册数据源
public Connection loadDataSource(String sourceFileName)
{
Connection connection=null;
try
{
InputStream inputStream=getClassLoader().getResourceAsStream(sourceFileName);
if(inputStream!=null)
{
//TODO TCF 读取到的数据源配置信息
//TODO TCF 驱动类
String driverClassName="";
//TODO TCF 数据库连接字符串
String url="";
//TODO TCF 用户名
String userName="";
//TODO TCF 密码
String password="";
//TODO TCF 读取xml数据源配置文件
SAXReader reader=new SAXReader();
Document document=reader.read(inputStream);
//TODO TCF 根节点
Element rootElement=document.getRootElement();
if(rootElement!=null)
{
//TODO TCF 根节点名称
String rootName=rootElement.getName();
if(StringUtils.isNotEmpty(rootName) && rootName.equals("dataSource"))
{
for(Object node:rootElement.elements())
{
Element element=(Element)node;
if(element!=null)
{
//TODO TCF 节点Value值
String value="";
if(element.hasContent())
{
value=element.getText();
}
else
{
value=element.attributeValue("value");
}
//TODO TCF 节点Name属性值
String name=element.attributeValue("name");
switch(name)
{
case "driverClassName":
driverClassName=value;
break;
case "url":
url=value;
break;
case "userName":
userName=value;
break;
case "password":
password=value;
break;
}
}
}
//TODO TCF 获取数据库连接
if(StringUtils.isNotEmpty(driverClassName) && StringUtils.isNotEmpty(userName) && StringUtils.isNotEmpty(password))
{
connection=DriverManager.getConnection(url,userName,password);
}
}
}
}
}
catch(Exception e)
{
e.printStackTrace();
}
return connection;
}
//TODO TCF 根据持久类映射文件名读取映射文件中的SQL方法
public MapperBean loadMapperFile(String mapperFileName)
{
MapperBean mapperBean=new MapperBean();
try
{
if(StringUtils.isNotEmpty(mapperFileName))
{
//TODO TCF 构建输入流读取xml映射文件
InputStream inputStream=getClassLoader().getResourceAsStream(mapperFileName);
if(inputStream!=null)
{
SAXReader reader=new SAXReader();
Document document=reader.read(inputStream);
//TODO TCF 根节点
Element rootElement=document.getRootElement();
if(rootElement!=null)
{
//TODO TCF 根节点属性命名空间(映射的持久层接口名)
String namespace=rootElement.attributeValue("namespace");
mapperBean.setInterfaceName(namespace);
//TODO TCF 映射文件中定义的所有持久化方法
List
for(Object node:rootElement.elements())
{
Element element=(Element)node;
if(element!=null)
{
Function function=new Function();
//TODO TCF SQL前缀(SQL语句类型)
String sqlType=element.getName().trim();
function.setSqlType(sqlType);
//TODO TCF SQL参数类型
String parameterType=element.attributeValue("parameterType");
function.setParameterType(parameterType);
//TODO TCF SQL返回值类型
String resultType=element.attributeValue("resultType");
//TODO TCF 根据SQL定义的返回值类型获取实例
Object instance=Class.forName(resultType);
function.setReturnType(instance);
//TODO TCF SQL语句
String sql=element.getText();
function.setSql(sql);
//TODO TCF 方法名
String methodName=element.attributeValue("id");
function.setMethodName(methodName);
functions.add(function);
}
}
//TODO TCF 当前映射器接口中定义的所有持久化方法
mapperBean.setFunctions(functions);
}
}
}
}
catch (Exception e)
{
e.printStackTrace();
}
return mapperBean;
}
}
3.定义SQL语句执行器接口Executor,用来定义需要执行的持久化方法基于JDBC的具体实现
package com.tcf.kid.smart.framework.core;
/***
* TODO TCF 定义SQL执行策略封装JDBC操作
* @author 71485
*
*/
public interface Executor {
//TODO TCF 根据id查询唯一结果
public
}
4.定义Executor SQL执行器接口实现类SQLExecutor,基于JDBC实现SQL操作
package com.tcf.kid.smart.framework.core;
import java.sql.*;
import com.tcf.kid.smart.framework.conf.Configuration;
import com.tcf.kid.smart.framework.entity.UserInfo;
/**
* TODO TCF SQL执行控制器接口实现类,定义具体的JDBC封装操作
* @author 71485
*
*/
public class SQLExecutor implements Executor{
//TODO TCF 核心配置类
private Configuration configuration=new Configuration();
//TODO TCF 根据id查询唯一返回结果
@SuppressWarnings("unchecked")
@Override
public
{
T result=null;
//TODO TCF 获取连接
Connection connection=configuration.loadDataSource("dataSource.xml");
//TODO TCF SQL执行实例
PreparedStatement preparedStatement=null;
//TODO TCF 查询结果集
ResultSet resultSet=null;
try
{
if(connection!=null)
{
preparedStatement=connection.prepareStatement(sql);
preparedStatement.setString(1,parameter.toString());
resultSet=preparedStatement.executeQuery();
if(resultSet!=null)
{
while(resultSet.next())
{
//TODO TCF 读取的用户信息
String id=resultSet.getString(1);
String name=resultSet.getString(2);
String sex=resultSet.getString(3);
Integer age=resultSet.getInt("age");
UserInfo userInfo=new UserInfo();
userInfo.setId(id);
userInfo.setName(name);
userInfo.setSex(sex);
userInfo.setAge(age);
result=(T)userInfo;
}
}
}
}
catch(Exception e)
{
e.printStackTrace();
}
finally
{
try
{
//TODO TCF 资源释放
if(resultSet!=null)
{
resultSet.close();
}
if(preparedStatement!=null)
{
preparedStatement.close();
}
if(connection!=null)
{
connection.close();
}
}
catch(Exception e)
{
e.printStackTrace();
}
}
return result;
}
}
5.定义MapperProxyManage类,作用是:作为映射器接口的代理类,实现InvocationInterceptor接口重写invoke方法定义代理方法执行体,读取映射文件并基于反射和MapperBean存放的接口和持久化方法映射关系调用对应的持久化方法进行持久化操作。
package com.tcf.kid.smart.framework.core;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import com.tcf.kid.smart.framework.conf.Configuration;
import com.tcf.kid.smart.framework.model.Function;
import com.tcf.kid.smart.framework.model.MapperBean;
/***
* TODO TCF 映射器代理目标,读取映射文件,基于反射执行对应SQL语句
* @author 71485
*
*/
public class MapperProxyManage implements InvocationHandler{
//TODO TCF 核心配置信息
private Configuration configuration;
//TODO TCF 会话
private SqlSession sqlSession;
//TODO TCF 构造注入
public MapperProxyManage(Configuration configuration,SqlSession sqlSession)
{
this.configuration=configuration;
this.sqlSession=sqlSession;
}
//TODO TCF 默认无参构造
public MapperProxyManage()
{
}
//TODO TCF 映射器代理目标需要执行的代理方法(织入到映射器接口的对应持久化操作方法执行)
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
{
//TODO TCF SQL执行之后的返回值
Object invokeResult=null;
try
{
//TODO TCF 获取映射文件
MapperBean mapperBean=configuration.loadMapperFile("UserInfoMapper.xml");
//TODO TCF 映射器接口类名
String interfaceClassName=mapperBean.getInterfaceName();
if(StringUtils.isNotEmpty(interfaceClassName))
{
//TODO TCF 匹配需要执行的目标方法所在类类名必须和读取到的映射器命名空间对应
if(method.getDeclaringClass().getName().equals(interfaceClassName))
{
//TODO TCF 映射器定义的所有持久化方法
List
if(functions!=null && functions.size()>0)
{
for(Function function:functions)
{
//TODO TCF 方法名
String methodName=function.getMethodName();
if(StringUtils.isNotEmpty(methodName))
{
if(methodName.equals(method.getName()))
{
invokeResult=sqlSession.selectOne(function.getSql(),String.valueOf(args[0]));
}
}
}
}
}
}
}
catch(Exception e)
{
e.printStackTrace();
}
return invokeResult;
}
}
6.定义SqlSession作为会话工厂,建立不同类型映射器的jdk动态代理实例并调用MapperProxyManage中的invoke执行代理的目标方法,定义SQL执行策略,如根据主键id查询返回唯一结果的方法、根据查询条件查询多个匹配记录的方法等,分别调用SQL执行器Executor的接口实现类如SQLExecutor基于JDBC实现持久化操作。
package com.tcf.kid.smart.framework.core;
import java.lang.reflect.Proxy;
import com.tcf.kid.smart.framework.conf.Configuration;
/***
* TODO TCF SQL会话类,定义执行的持久化方法策略类型:如查询一个结果、查询多个结果等
* @author 71485
*
*/
public class SqlSession {
//TODO TCF MyBatis核心配置信息
private Configuration configuration=new Configuration();
//TODO TCF MyBatis SQL执行控制器
private Executor executor=new SQLExecutor();
//TODO TCF 定义需要执行的SQL策略
//TODO TCF 根据id查询返回唯一结果
public
{
return executor.selectOne(sql,parameter);
}
//TODO TCF 定义其他需要执行的SQL策略...
//TODO TCF 基于JDK动态代理创建映射器接口的的代理实例,获取映射器实例
@SuppressWarnings("unchecked")
public
{
return (T) Proxy.newProxyInstance(
mapperClass.getClassLoader(),
new Class[] {mapperClass},
new MapperProxyManage(configuration,this)
);
}
}
7.编写测试类查看测试结果
package com.tcf.kid.smart.framework.test;
import com.tcf.kid.smart.framework.core.SqlSession;
import com.tcf.kid.smart.framework.dao.UserInfoMapper;
import com.tcf.kid.smart.framework.entity.UserInfo;
public class Test {
public static void main(String[] args)
{
SqlSession sqlSession=new SqlSession();
UserInfoMapper userInfoMapper=sqlSession.getMapper(UserInfoMapper.class);
UserInfo userInfo=userInfoMapper.selectById("20190824001");
System.out.println(userInfo.getId());
System.out.println(userInfo.getName());
System.out.println(userInfo.getSex());
System.err.println(userInfo.getAge());
}
}
运行结果: