了解mybatis源码手写mybatis
Posted tanglinsheng
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了了解mybatis源码手写mybatis相关的知识,希望对你有一定的参考价值。
一:mybatis概述
MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Ordinary Java Object,普通的 Java对象)映射成数据库中的记录。
二:手写mybatis
实现思路::
1创建SqlSessionFactory实例
2:实例化过程中,加载配置文件创建configuration对象
3:通过factory创建SqlSeesion
---------------------初始化阶段完成--------------------------
4:通过SqlSeesion获取mapper接口动态代理
5:动态代理回调sqlsession中某查询方法
---------------------代理阶段完成--------------------------
6:SqlSession将查询方法转发给Executor
7:Executor基于jdbc访问数据库获取数据
8:Executor通过反射将数据转化成POJP并返回给SqlSession
9:将数据返回给调用者
手写源码展示:
2.1、初始化阶段
将db.properties,以及*mapper.xml读取到内存中,主要通过SqlSessionFactory实现
/** * 初始化加载配置信息映射 * 生成sqlsession * @param: mybatis03 * @description: * @author: tangl * @create: 2019-05-13 12:07 */ public class SqlSessionFactory { public final Configuration conf = new Configuration(); public SqlSessionFactory(){ loadDbInfo(); loadMappersInfo();//加载 } //mappers.xml文件保存路径 public static final String MAPPER_CONFIG_LOCATION="mappers"; //数据库连接配置文件路径 public static final String DB_CONFIG_FILE="db.properties"; private void loadDbInfo(){ InputStream inputStream = SqlSessionFactory.class.getClassLoader().getResourceAsStream(DB_CONFIG_FILE); Properties p = new Properties(); try { p.load(inputStream); } catch (IOException e) { e.printStackTrace(); } conf.setJdbcDriver(p.get("jdbc.driver").toString()); conf.setJdbcUrl(p.get("jdbc.url").toString()); conf.setJdbcUsername(p.get("jdbc.username").toString()); conf.setJdbcPassword(p.get("jdbc.password").toString()); } //读取xml并解析成map private void loadMappersInfo(){ URL resources =null; resources = SqlSessionFactory.class.getClassLoader().getResource(MAPPER_CONFIG_LOCATION); File mappers = new File(resources.getFile()); if(mappers.isDirectory()){ File[] listFiles = mappers.listFiles(); for(File file:listFiles){ loadMapperInfo(file); } } } //加载指定*.xml文件 private void loadMapperInfo(File file){ SAXReader reader = new SAXReader(); Document document=null; try { document = reader.read(file); } catch (DocumentException e) { e.printStackTrace(); } Element root = document.getRootElement(); //获取命名空间 String namespace = root.attribute("namespace").getData().toString(); //select子元素 List<Element> selects = root.elements("select"); //遍历所有查询语句 for( Element element:selects){ MappedStatement mappedStatement = new MappedStatement(); String id = element.attribute("id").getData().toString(); String resultType = element.attribute("resultType").getData().toString(); String sql = element.getData().toString(); String sourceId = namespace+"."+id; //map的key是命名空间+id mappedStatement.setNamespace(namespace); mappedStatement.setSourceId(sourceId); mappedStatement.setResultType(resultType); mappedStatement.setSql(sql); conf.getMappedStatements().put(sourceId,mappedStatement); } } public SqlSession openSession(){ return new DefuaultSqlSession(this.conf); } }
/** * 数据库配置文件映射 * *mapper.xml映射map * @param: mybatis03 * @description: * @author: tangl * @create: 2019-05-13 12:10 */ public class Configuration { private String jdbcDriver;//数据库驱动 private String jdbcUrl;//数据库url地址 private String jdbcUsername;//数据库用户名 private String jdbcPassword;//数据库密码 private Map<String, MappedStatement> mappedStatements = new HashMap<>();//mapper.xml解析成对象 public String getJdbcDriver() { return jdbcDriver; } public void setJdbcDriver(String jdbcDriver) { this.jdbcDriver = jdbcDriver; } public String getJdbcUrl() { return jdbcUrl; } public void setJdbcUrl(String jdbcUrl) { this.jdbcUrl = jdbcUrl; } public String getJdbcUsername() { return jdbcUsername; } public void setJdbcUsername(String jdbcUsername) { this.jdbcUsername = jdbcUsername; } public String getJdbcPassword() { return jdbcPassword; } public void setJdbcPassword(String jdbcPassword) { this.jdbcPassword = jdbcPassword; } public Map<String, MappedStatement> getMappedStatements() { return mappedStatements; } public void setMappedStatements(Map<String, MappedStatement> mappedStatements) { this.mappedStatements = mappedStatements; } }
/** * mapper.xml映射文件 * @param: mybatis03 * @description: * @author: tangl * @create: 2019-05-13 12:13 */ public class MappedStatement { private String namespace;//命名空间 private String sourceId;//方法id名称 private String resultType;//返回数据类型 private String sql;//sql语句 public String getNamespace() { return namespace; } public void setNamespace(String namespace) { this.namespace = namespace; } public String getSourceId() { return sourceId; } public void setSourceId(String sourceId) { this.sourceId = sourceId; } 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.2、代理阶段
主要通过DefuaultSqlSession实现
/** * 1: 对外提供访问的api * 2:对内请求转发给Executor实现 * @param: mybatis03 * @description: * @author: tangl * @create: 2019-05-13 14:51 */ public interface SqlSession { <T> T selectOne(String statement,Object parameter) throws Exception; <E> List<E> selectList(String statement, Object parameter); <T> T getMapper(Class<T> type); }
/** * 1: 对外提供访问的api * 2:对内请求转发给Executor实现 * 3: 通过动态代理,面向接口编程 * @param: mybatis03 * @description: * @author: tangl * @create: 2019-05-13 14:51 */ public class DefuaultSqlSession implements SqlSession { private final Configuration conf; private Executor executor; public DefuaultSqlSession(Configuration conf){ super(); this.conf=conf; executor = new DefaultExcutor(conf); } @Override public <T> T selectOne(String statement, Object parameter) throws Exception { MappedStatement ms = conf.getMappedStatements().get(statement); List<Object> selectList = executor.query(ms,parameter); if(null==selectList || selectList.size()==0){ return null; } if(selectList.size()==1){ return (T)selectList.get(0); }else{ throw new Exception(); } } @Override public <E> List<E> selectList(String statement, Object parameter) { MappedStatement ms = conf.getMappedStatements().get(statement); return executor.query(ms,parameter); } @Override public <T> T getMapper(Class<T> type) { return (T) Proxy.newProxyInstance(type.getClassLoader(), new Class[]{type},new MapperProxy(this)); } public class MapperProxy implements InvocationHandler{ private SqlSession session; public MapperProxy(SqlSession session){ super(); this.session = session; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Class<?> returnType = method.getReturnType(); System.out.println("returnType="+returnType.toString()); if(Collection.class.isAssignableFrom(returnType)){//返回参数是不是集合 return session.selectList(method.getDeclaringClass().getName()+"."+method.getName(),null==args?null:args[0]); }else{ return session.selectOne(method.getDeclaringClass().getName()+"."+method.getName(),null==args?null:args[0]); } } } }
2.3、数据库操作返回数据并做映射处理阶段
主要通过Executor实现
/** * @param: mybatis03 * @description: 封装对数据库操作,以及对返回数据的封装 * @author: tangl * @create: 2019-05-13 15:25 */ public interface Executor { <E> List<E> query(MappedStatement mappedStatement, Object args); }
/** * @param: mybatis03 * @description: 封装对数据库操作,以及对返回数据的封装 * @author: tangl * @create: 2019-05-13 15:25 */ public class DefaultExcutor implements Executor { private Configuration conf; public DefaultExcutor(Configuration conf){ super(); this.conf=conf; } @Override public <E> List<E> query(MappedStatement ms, Object parameter) { List<E> ret = new ArrayList<>(); try { Class.forName(conf.getJdbcDriver()); } catch (ClassNotFoundException e) { e.printStackTrace(); } Connection connection=null; PreparedStatement preparedStatement =null; ResultSet resultSet=null; try { connection = DriverManager.getConnection(conf.getJdbcUrl(),conf.getJdbcUsername(),conf.getJdbcPassword()); preparedStatement = connection.prepareStatement(ms.getSql()); //处理sql语句中的占位符 parameterize(preparedStatement,parameter); resultSet = preparedStatement.executeQuery(); //通过发射技术,填充到list handlerResultSet(resultSet,ret,ms.getResultType()); } catch (SQLException e) { e.printStackTrace(); }finally { try { resultSet.close(); preparedStatement.close(); connection.close(); } catch (SQLException e) { e.printStackTrace(); } } return ret; } public void parameterize(PreparedStatement preparedStatement,Object parameter) throws SQLException { if(parameter instanceof String){ preparedStatement.setString(1,(String)parameter); }else if(parameter instanceof Long){ preparedStatement.setLong(1,(Long)parameter); }else if(parameter instanceof Integer){ preparedStatement.setInt(1,(int)parameter); } } public <E> void handlerResultSet(ResultSet resultSet,List<E> ret,String className){ Class<E> clazz = null; try { clazz = (Class<E>)Class.forName(className); } catch (ClassNotFoundException e) { e.printStackTrace(); } try { while(resultSet.next()){ //通过反射实例化对象 Object entity = clazz.newInstance(); //使用反射工具将resultSet中的数据填充到entity中 //id,name,sex,age // Integer id = resultSet.getInt(1); // String name = resultSet.getString(2); // String sex = resultSet.getString(3); // Integer age = resultSet.getInt(4); // // Integer id02 = resultSet.getInt("id"); // String name02 = resultSet.getString("name"); // String sex02 = resultSet.getString("id"); // Integer age02 = resultSet.getInt("age"); // ReflectionUtils. // 获取实体类的所有属性,返回Field数组 Field[] fields = clazz.getDeclaredFields(); for(Field field:fields){ field.setAccessible(true); String fname = field.getName(); Type type = field.getGenericType(); System.out.println("fname="+fname+" type="+type.toString()); if(type.toString().equals("class java.lang.String")){ String column = resultSet.getString(fname); field.set(entity,column); }else if(type.toString().equals("class java.lang.Integer")){ Integer column = resultSet.getInt(fname); field.set(entity,column); } } ret.add((E)entity); } } catch (SQLException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } }
三:测试
/** * @param: mybatis03 * @description:手写mybatis测试 * @author: tangl * @create: 2019-05-13 14:57 */ public class TestMybatis { public static void main(String[] args) throws ClassNotFoundException { //第一阶段,加载配置信息放入内存 SqlSessionFactory factory = new SqlSessionFactory(); SqlSession sqlSession = factory.openSession(); System.out.println(sqlSession); //第二阶段 封装ibatis编程模式,使用mapper接口开发的初始化工作 TUserMapper mapper = sqlSession.getMapper(TUserMapper.class); //第三阶段 TUser tUser = mapper.getUserById(1); System.out.println(tUser); List<TUser> allList = mapper.getUserAllList(); if(null!=allList && allList.size()>0){ for (TUser tUser02:allList){ System.out.println(tUser02); } } } }
四:项目补充
1:pom.xml文件
<dependencies>
<!-- 解析xml文件-->
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.11</version>
</dependency>
</dependencies>
2:db.properties
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&autoReconnect=true&verifyServerCertificate=false&useSSL=false&serverTimezone=UTC
jdbc.username=root
jdbc.password=123456
五:项目说明
这仅仅是参考源码完成mybatis的核心功能,mybatis还有一些其它辅助功能,如:数据库连接池等。
以上是关于了解mybatis源码手写mybatis的主要内容,如果未能解决你的问题,请参考以下文章