手写极简Mybatis
- 加载已经配置的mybatis-config.xml,从里边加载数据源和mapper.xml的存放路径
- 并将mapper.xml里面的通过全类名(也就是namespace)作为key以及包装的sql作为value,最后存于一个map中
- 然后建立一个Session,通过代理和map中的sql生成代理对象从而实现对数据库的增删查改
大致方法执行过程
public static void main(String[] args) throws SQLException {
SessionFactory sessionFactory = new SessionFactory("config/mybatis-config.xml");
Session session = sessionFactory.openSession();
UserDao mapper = session.getMapper(UserDao.class);
session.begin();
mapper.saveUser(new User(1,"11","111"));
session.commit();
}
1.配置文件准备
mybatis.config.xml
<mybatis>
<dataSource>druid</dataSource>
<mapper>config/UserMapper.xml</mapper>
</mybatis>
druid.properties
druid.username=root
druid.password=123
druid.url=jdbc:mysql://127.0.0.1:3306/ssm?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC
druid.driverClassName=com.mysql.jdbc.Driver
2.创建数据源
由于嫌麻烦所以只设置了一个数据源
public static DataSource createDataSource(String type) {
DataSource dataSource = null;
Properties properties = new Properties();
if (DATASOURCE.equals(type)) {
try {
properties.load(DataSourceFactory.class.getClassLoader()
.getResourceAsStream("config/hikari.properties"));
} catch (Exception e) {
e.printStackTrace();
}
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.configFromPropety(properties);
dataSource = druidDataSource;
}
return dataSource;
}
3.创建会话工厂
SessionFactory.java
package com.mybatis.core;
import com.mybatis.DataSourceFactory;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.*;
/**
* @author 2358189586
*/
public class SessionFactory {
private DataSource dataSource;
private Map<String, Map<String, DaoWrapper>> env = new HashMap<>(8);
public SessionFactory(String config) {
loadXml(config);
}
// 打开一个会话
public Session openSession() {
Connection connection = null;
try {
connection = dataSource.getConnection();
} catch (SQLException e) {
e.printStackTrace();
}
return new Session(connection, env);
}
// 加载资源环境
public void loadXml(String config) {
try {
SAXReader reader = new SAXReader();
Document configDom = reader.read(Session.class.getClassLoader().getResourceAsStream(config));
Element configRoot = configDom.getRootElement();
String dataSourceType = configRoot.element("dataSource").getTextTrim();
dataSource = DataSourceFactory.createDataSource(dataSourceType);
List mapperElements = configRoot.elements("mapper");
List<String> mapperPaths = new ArrayList<>();
for (Object element : mapperElements) {
Element mapper = (Element) element;
mapperPaths.add(mapper.getTextTrim());
}
for (String mapperPath : mapperPaths) {
Map<String, DaoWrapper> wrapper = new HashMap<>(2);
Document document = reader.read(Session.class.getClassLoader().getResourceAsStream(mapperPath));
Element root = ((org.dom4j.Document) document).getRootElement();
String namespace = root.attribute("namespace").getValue();
Iterator iterator = root.elementIterator();
while (iterator.hasNext()) {
Element el = (Element) iterator.next();
String type = el.getName();
String id = el.attribute("id").getValue();
String resultType = el.attribute("resultType").getValue();
String paramType = el.attribute("paramType").getValue();
String sqlStr = el.getTextTrim();
wrapper.put(id, new DaoWrapper(type, resultType, paramType, sqlStr));
}
env.put(namespace, wrapper);
}
} catch (DocumentException e) {
e.printStackTrace();
}
}
}
Session.java
package com.mybatis.core;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
/**
* @author 2358189586
*/
public class Session {
/**
* 每个会话持有一个链接
*/
private Connection connection;
/**
* 当前会话的上下文
*/
private Map<String, Map<String, DaoWrapper>> env = new HashMap<>(8);
public Session(Connection connection, Map<String, Map<String,
DaoWrapper>> env) {
this.connection = connection;
this.env = env;
}
/**
* 拿到一个包装
* 类
*
* @param clazz
* @param <T>
* @return
*/
public <T> T getMapper(Class<T> clazz) {
T t = (T) Proxy.newProxyInstance(this.getClass().getClassLoader(),
new Class[]{clazz},
new SQLHandler(connection, clazz, env.get(clazz.getName())));
return t;
}
/**
* // 开始会话
*/
public void begin() {
try {
connection.setAutoCommit(false);
} catch (SQLException e) {
e.printStackTrace();
}
}
/**
* // 提交
*/
public void commit() {
try {
connection.commit();
} catch (SQLException e) {
e.printStackTrace();
}
}
/**
* // 回滚
*/
public void rollback() {
try {
connection.rollback();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
对于每条sql的封装
DaoWrapper
package com.mybatis.core;
/**
* @author 2358189586
*/
public class DaoWrapper {
/**
* 类型,insert|update|delete
*/
private String type;
/**
* 返回值的类型
*/
private String resultType;
/**
* 参数的类型
*/
private String paramType;
/**
* sql语句
*/
private String sql;
public DaoWrapper(){}
public DaoWrapper(String type, String resultType, String paramType, String sql) {
this.type = type;
this.resultType = resultType;
this.paramType = paramType;
this.sql = sql;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getResultType() {
return resultType;
}
public void setResultType(String resultType) {
this.resultType = resultType;
}
public String getParamType() {
return paramType;
}
public void setParamType(String paramType) {
this.paramType = paramType;
}
public String getSql() {
return sql;
}
public void setSql(String sql) {
this.sql = sql;
}
@Override
public String toString() {
return "DaoWrapper{" +
"type='" + type + '\'' +
", resultType='" + resultType + '\'' +
", paramType='" + paramType + '\'' +
", sql='" + sql + '\'' +
'}';
}
}
4.代理对象的生成
SQLHandler.java
package com.mybatis.core;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* @author 2358189586
*/
public class SQLHandler implements InvocationHandler {
/**
* 需传入一个链接
*/
private Connection connection;
/**
* 需传入一个dao的类型
*/
private Class clazz;
/**
* 需传入一个独立的环境
*/
private Map<String, DaoWrapper> env;
public SQLHandler(Connection connection, Class clazz, Map<String,
DaoWrapper> env) {
this.connection = connection;
this.clazz = clazz;
this.env = env;
}
/**
* 生成代理对象
*
* @param proxy
* @param method
* @param args
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws
Throwable {
// 拿到包装
DaoWrapper wrapper = env.get(method.getName());
PreparedStatement statement =
connection.prepareStatement(wrapper.getSql());
// 对每一种sql语句进行独立的操作
if ("insert".equals(wrapper.getType())) {
String paramType = wrapper.getParamType();
// 暂定传入一个对象
Class<?> clazz = args[0].getClass();
Field[] fields = clazz.getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
fields[i].setAccessible(true);
statement.setObject(i + 1, fields[i].get(args[0]));
}
return statement.executeUpdate();
} else if ("delete".equals(wrapper.getType())) {
for (int i = 0; i < args.length; i++) {
statement.setObject(i + 1, args[i]);
}
return statement.executeUpdate();
} else if ("select".equals(wrapper.getType())) {
for (int i = 0; i < args.length; i++) {
statement.setObject(i + 1, args[i]);
}
ResultSet result = statement.executeQuery();
List list = new ArrayList();
while (result.next()) {
Class<?> clazz = Class.forName(wrapper.getResultType());
Object object = clazz.newInstance();
Field[] fields = clazz.getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
fields[i].setAccessible(true);
fields[i].set(object,
result.getObject(fields[i].getName()));
}
list.add(object);
}
return list;
}
return null;
}
}
END:本文的实体类和Dao接口请自行生成