Mybatis学习1:从零手写Mybatis精简版框架
前言
参考博客:https://zhuanlan.zhihu.com/p/67086285
不废话,直接上源码
框架目录
源码如下
Function类
package com.hs.tyj.config; /** * @description:用于映射sql.xml中的sql方法 * @date:2019/6/20 * @author:tangyj * @remark: * */ public class Function { private String sqlType; private String funcName; private String sql; private Object resultType; private String parameterType; public String getSqlType() { return sqlType; } public void setSqlType(String sqlType) { this.sqlType = sqlType; } public String getFuncName() { return funcName; } public void setFuncName(String funcName) { this.funcName = funcName; } public String getSql() { return sql; } public void setSql(String sql) { this.sql = sql; } public Object getResultType() { return resultType; } public void setResultType(Object resultType) { this.resultType = resultType; } public String getParameterType() { return parameterType; } public void setParameterType(String parameterType) { this.parameterType = parameterType; } }
MapperBean类
package com.hs.tyj.config; import java.util.List; /** * @description:用于映射sql.xml * @date:2019/6/20 * @author:tangyj * @remark: * */ public class MapperBean { private String interfaceName; private List<Function> list; public String getInterfaceName() { return interfaceName; } public void setInterfaceName(String interfaceName) { this.interfaceName = interfaceName; } public List<Function> getList() { return list; } public void setList(List<Function> list) { this.list = list; } }
User类
package com.hs.tyj.entity; public class User { private Integer id; private String userName; private String password; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } 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; } @Override public String toString() { return "User{" + "id=" + id + ", userName='" + userName + '\'' + ", password='" + password + '\'' + '}'; } }
UserMapper类
package com.hs.tyj.mapper; import com.hs.tyj.entity.User; import java.util.List; public interface UserMapper { public User getUserById(Integer id); }
Excutor类
package com.hs.tyj.sqlSession; public interface Excutor { public <T> T query(String stattement, Object parameter); }
MyConfiguration类
package com.hs.tyj.sqlSession; import com.hs.tyj.config.Function; import com.hs.tyj.config.MapperBean; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.Element; import org.dom4j.io.SAXReader; import java.io.InputStream; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; /** * @description:Configuration类,1-读取数据库相关配置-2-提供数据库链接方法-3-解析sql.Mapper文件 * @date:2019/6/20 * @author:tangyj * @remark: * */ public class MyConfiguration { private static ClassLoader loader = ClassLoader.getSystemClassLoader(); /** * @description:读取xml信息并处理 * @param:[resource] * @return:com.mysql.jdbc.Connection * @date:2019/6/19 * @author:tangyj * @remark: * */ public Connection bulid(String resource){ try { InputStream resourceAsStream = loader.getResourceAsStream(resource); SAXReader reader = new SAXReader();//SAXReader creates a DOM4J tree from SAX parsing events. Document document = reader.read(resourceAsStream); Element rootElement = document.getRootElement(); return evalDataSource(rootElement); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException("error accured while eval xml " + resource); } } /** * @description:解析mapper.xml文件 * @param:[path] * @return:com.hs.tyj.config.MapperBean * @date:2019/6/19 * @author:tangyj * @remark: * */ @SuppressWarnings("rawtypes") public MapperBean readMapper(String path){ MapperBean mapper = new MapperBean(); try { InputStream resourceAsStream = loader.getResourceAsStream(path); SAXReader reader = new SAXReader(); Document document = reader.read(resourceAsStream); Element rootElement = document.getRootElement(); mapper.setInterfaceName(rootElement.attributeValue("nameSpace").trim());//把mapper节点的nameSpace值存为接口名 List<Function> list = new ArrayList<Function>(); for(Iterator rootIter = rootElement.elementIterator();rootIter.hasNext();){//遍历跟节点下的所有子节点 Function func = new Function(); Element e = (Element)rootIter.next(); String sqlType = e.getName().trim(); String funcName = e.attributeValue("id").trim(); String sql = e.getText().trim(); String resultType = e.attributeValue("resultType").trim(); func.setSqlType(sqlType); func.setFuncName(funcName); Object newInstance = null; try { newInstance = Class.forName(resultType).newInstance(); } catch (InstantiationException e1) { e1.printStackTrace(); } catch (IllegalAccessException e1) { e1.printStackTrace(); } catch (ClassNotFoundException e1) { e1.printStackTrace(); } func.setResultType(newInstance); func.setSql(sql); list.add(func); } mapper.setList(list); } catch (DocumentException e) { e.printStackTrace(); } return mapper; } //本类私有方法 //1-解析数据库配置 private Connection evalDataSource(Element node) throws ClassNotFoundException { if(!node.getName().equals("database")){ throw new RuntimeException("root should be <database>"); } String driverClassName = null; String url = null; String userName = null; String password = null; //1-获取属性节点 List property = node.elements("property"); for(Object item : property){ Element i = (Element)item; String value = getValue(i); String name = i.attributeValue("name"); if(name == null || value == null){ throw new RuntimeException("[database]:<property> should contain name and value"); } //2-赋值 if(name.equals("driverClassName")){ driverClassName = value; }else if(name.equals("url")){ url = value; }else if(name.equals("username")){ userName = value; }else if(name.equals("password")){ password = value; }else{ throw new RuntimeException("[database]:<property> unknow name"); } } Class.forName(driverClassName); Connection connection = null; try { connection = DriverManager.getConnection(url, userName, password); } catch (SQLException e) { e.printStackTrace(); } return connection; } //2-获取property的属性的值,如果有value值,则读取,则设置 private String getValue(Element node){ return node.hasContent()? node.getText() : node.attributeValue("value"); } }
MyExcutor类
package com.hs.tyj.sqlSession; import com.hs.tyj.entity.User; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; /** * @description:Excutor实现类;提供数据库查询等方法 * @date:2019/6/20 * @author:tangyj * @remark: * */ public class MyExcutor implements Excutor{ private MyConfiguration xmlConfiguration = new MyConfiguration(); //1-获取单条数据 public <T> T query(String sql, Object parameter) { Connection connection = getConnecttion(); ResultSet set = null; PreparedStatement pre = null; try { pre = connection.prepareStatement(sql); //设置参数 pre.setString(1,parameter.toString()); set = pre.executeQuery(); User u = new User(); //遍历结果集 while(set.next()){ u.setId(set.getInt(1)); u.setUserName(set.getString(2)); u.setPassword(set.getString(3)); } return (T)u; } catch (SQLException e) { e.printStackTrace(); }finally { try { if(set != null){ set.close(); } if(pre != null){ pre.close(); } if(connection != null){ connection.close(); } } catch (SQLException e) { e.printStackTrace(); } } return null; } //本类私有方法 private Connection getConnecttion(){ try { Connection connection = xmlConfiguration.bulid("config.xml"); return connection; } catch (Exception e) { e.printStackTrace(); } return null; } }
MyMapperProxy类
package com.hs.tyj.sqlSession; import com.hs.tyj.config.Function; import com.hs.tyj.config.MapperBean; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.util.List; /** * @description:Mapper接口代理,主要功能:读取sql.xml文件,通过动态代理防范,实现UserMapper接口 * @date:2019/6/20 * @author:tangyj * @remark: * */ public class MyMapperProxy implements InvocationHandler{ private MySqlSession mySqlSession; private MyConfiguration myConfiguration; public MyMapperProxy(MySqlSession mySqlSession, MyConfiguration myConfiguration) { this.mySqlSession = mySqlSession; this.myConfiguration = myConfiguration; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { MapperBean readMapper = myConfiguration.readMapper("UserMapper.xml");//读取sql.xml,并将将接口名称和方法体封装到MapperBean; //是否是文件对应的接口 if(!method.getDeclaringClass().getName().equals(readMapper.getInterfaceName())){ return null; } List<Function> list = readMapper.getList();//读取方法体 if(null != list || 0 != list.size()){ for(Function func : list){ //id是否和接口方法名一样 if(method.getName().equals(func.getFuncName())){ return mySqlSession.selectOne(func.getSql(),String.valueOf(args[0]));//执行selectOne方法 } } } return null; } }
MySqlSession类
package com.hs.tyj.sqlSession; import java.lang.reflect.Proxy; import java.util.List; /** * @description:sqlSession类,SqlSession提供select/insert/update/delete方法 * @date:2019/6/20 * @author:tangyj * @remark: * */ public class MySqlSession { private Excutor excutor = new MyExcutor(); private MyConfiguration myConfiguration = new MyConfiguration(); //1-获取单条数据 public <T> T selectOne(String statement, Object parameter){ return excutor.query(statement,parameter); } public <T> T getMapper(Class<T> clas){ //动态代理 return (T) Proxy.newProxyInstance(clas.getClassLoader(),new Class[]{clas},new MyMapperProxy(this,myConfiguration)); } }
TestMybatis类
package com.hs.tyj; import com.hs.tyj.entity.User; import com.hs.tyj.mapper.UserMapper; import com.hs.tyj.sqlSession.MySqlSession; import java.util.List; public class TestMybatis { public static void main(String[] args){ MySqlSession mySqlSession = new MySqlSession(); UserMapper mapper = mySqlSession.getMapper(UserMapper.class); User user = mapper.getUserById(1); System.out.println(user); } }
config.xml
<?xml version="1.0" encoding="UTF-8"?> <database> <property name="driverClassName" >com.mysql.jdbc.Driver</property> <property name="url" >jdbc:mysql://XXXXX:3306/saichang?useUnicode=true&characterEncoding=utf8</property> <property name="username" >saichang</property> <property name="password" >saichang1234</property> </database>
UserMapper.xml
<?xml version="1.0" encoding="UTF-8"?> <mapper nameSpace="com.hs.tyj.mapper.UserMapper"> <select id="getUserById" resultType="com.hs.tyj.entity.User"> select * from user where id = ? </select> </mapper>
pom文件
<?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.hs</groupId> <artifactId>tyj-mybatis</artifactId> <version>1.0-SNAPSHOT</version> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <java.version>1.8</java.version> </properties> <dependencies> <!-- https://mvnrepository.com/artifact/dom4j/dom4j --> <dependency> <groupId>dom4j</groupId> <artifactId>dom4j</artifactId> <version>1.6.1</version> </dependency> <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.29</version> </dependency> </dependencies> </project>
数据设计
日志数据
User{id=1, userName='我不吃番茄', password='123456'}