SSM框架--手动实现mybatis


一:概述

最近在学习SSM框架,有时间来整合一下,想深入了解一下框架底层,先手动实现一下mybatis。

完整代码链接:点击打开链接

首先先总结一下SSM框架的大体内容:

SSM框架在项目开发中经常使用到,相比于SSH框架,它在仅几年的开发中运用的更加广泛。

  • Spring作为一个轻量级的框架,有很多的拓展功能,最主要的我们一般项目使用的就是IOC和AOP。
  • SpringMVC是Spring实现的一个Web层,相当于Struts的框架,但是比Struts更加灵活和强大!
  • Mybatis是 一个持久层的框架,在使用上相比Hibernate更加灵活,可以控制sql的编写,使用 XML或注解进行相关的配置
这次就模拟mybatis实现数据层的持久化。

二:编写思路

1.先来看一下项目目录结构:



1)这里我使用oracle数据库进行测试,引入了oracle的驱动包。
2)mybatis是使用xml或注解来配置和映射原生信息。避免了繁琐的强耦合的JDBC代码。这里模拟xml配置过程,首先就要对xml配置文件进行解析。使用dom4j来解析xml文件,引入相关包。

2.mybatis-config.xml文件:

首先要对这样一个xml文件进行解析,这个文件对应的是mybatis的配置xml文件
<?xml version="1.0" encoding="UTF-8"?>

<configuration>
	<dataSource>
		<property name="driver" value="oracle.jdbc.driver.OracleDriver"></property>
		<property name="url" value="jdbc:oracle:thin:@127.0.0.1:1521:orcl"></property>
		<property name="user" value="vera"></property>
		<property name="password" value="a"></property>
	</dataSource>

	<mappers>
		<mapper resource="com/yee/mybatis/bean/DeptMapper.xml" />
	</mappers>
</configuration>
解析是要获取两个对象:
1) DataSource对象
2)mappers下的映射文件路径(com/yee/mybatis/bean/DeptMapper.xml)
编写了一个MybatisConfig来解析

3.MybatisConfig解析

可以看到,生成对应的DataSource对象,这个对象是mybatis的内置对象,我模拟提供了如下属性。(这里只是简单的模拟,真正mybatis封装的内容非常多)

<environments default="development">
	<environment id="development">
		<transactionManager type="JDBC" />
		<dataSource type="POOLED">
			<property name="driver" value="${jdbc.driverClassName}" />
			<property name="url" value="${jdbc.url}" />
			<property name="username" value="${jdbc.username}" />
			<property name="password" value="${jdbc.password}" />
		</dataSource>
	</environment>
</environments>
例如: <dataSource type="POOLED"> 的type属性、其有三种取值: 
  • POOLED:使用Mybatis自带的数据库连接池来管理数据库连接
  • UNPOOLED:不使用任何数据库连接池来管理数据库连接
  • JNDI:jndi形式使用数据库连接、主要用于项目正常使用的时候


4.Data Source 对象

根据mybatis-config中DataSource的属性我提供了包含这几个属性的实体类 DataSource:
p.s:这个是自己手动模拟的!!不是官方提供的!!不要误解
package com.yee.mybatis;

public class DataSource {
	private String driver;
	private String url;
	private String user;
	private String password;
	
	@Override
	public String toString() {
		return "DataSource [driver=" + driver + ", url=" + url + ", user=" + user + ", password=" + password + "]";
	}

	public String getDriver() {
		return driver;
	}

	public void setDriver(String driver) {
		this.driver = driver;
	}

	public String getUrl() {
		return url;
	}

	public void setUrl(String url) {
		this.url = url;
	}

	public String getUser() {
		return user;
	}

	public void setUser(String user) {
		this.user = user;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}
}

为了模拟实现 我封装了一个MapperInfo对象:

这个对象的属性是根据mybatis的实体映射属性来确定的,简单模拟,我提供了parameterType,resultMap几个常用属性。
这里的isUpdate是为了方便实现自己添加的一个属性,因为修改和查看的操作略有区别。

p.s:Dept实体类的代码就不粘了 就是属性加get(),set(),toString(),构造方法,基本操作哈;

1)MapperInfo对象

package com.yee.mybatis;

public class MapperInfo {
	private String parameterType;
	private String resultType;
	private String sql;
	private boolean isUpdate=false;
	
	@Override
	public String toString() {
		return "MapperInfo [parameterType=" + parameterType + ", resultType=" + resultType + ", sql=" + sql
				+ ", isUpdate=" + isUpdate + "]";
	}

	public String getParameterType() {
		return parameterType;
	}

	public void setParameterType(String parameterType) {
		this.parameterType = parameterType;
	}

	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;
	}

	public boolean isUpdate() {
		return isUpdate;
	}

	public void setUpdate(boolean isUpdate) {
		this.isUpdate = isUpdate;
	}
}


2)DeptMapper.xml文件

<?xml version="1.0" encoding="UTF-8"?>

<mapper>
	<insert id="addDept" parameterType="com.yee.mybatis.bean.Dept">
		insert into dept values(#{deptno},#{dname},#{loc})
	</insert>
	
	<select id="findAll" resultType="com.yee.mybatis.bean.Dept">
		select deptno,dname,loc from dept
	</select>
</mapper>
MapperInfo对象就是保存这个映射文件的基本信息的,下面开始解析DeptMapper.xml文件并进行处理。

三:核心处理

1.SqlSessionFactory对象

读取并解析映射文件 DeptMapper.xml 获取MapperInfo对象

package com.yee.mybatis;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.XPath;
import org.dom4j.io.SAXReader;

public class SqlSessionFactory {
	private MyBatisConfig config;
	private Map<String,MapperInfo> mapperInfos=new HashMap<String,MapperInfo>();
	
	public SqlSessionFactory(MyBatisConfig config){
		this.config=config;
		
		try {
			parseXml();
		} catch (DocumentException e) {
			e.printStackTrace();
		}
	}
	
	@SuppressWarnings("unchecked")
	private void parseXml() throws DocumentException{
		List<String> mappers=config.getMappers();
		if(mappers!=null && mappers.size()>0){
				SAXReader saxReader=new SAXReader();
				for(String mapper:mappers){
					Document doc=saxReader.read(this.getClass().getClassLoader().getResourceAsStream(mapper));
				
					XPath xpath=doc.createXPath("//mapper/*");
					List<Element> ops=xpath.selectNodes(doc);
					MapperInfo info=null;
					String opname=null;
					for(Element el:ops){
						info=new MapperInfo();
						opname=el.getName();   //获取操作类型 即元素名 insert 
						if("select".equals(opname)){
							info.setUpdate(false); //不为更新操作
						}
						info.setParameterType(el.attributeValue("parameterType"));
						info.setResultType(el.attributeValue("resultType"));
						info.setSql(el.getTextTrim());
						mapperInfos.put(el.attributeValue("id"),info);
								}
				}
		}
	}


	public MyBatisConfig getConfig() {
		return config;
	}


	public Map<String, MapperInfo> getMapperInfos() {
		return mapperInfos;
	}
}

2.SqlSession对象

sqlSession对象封装了传统的DBHelper实现数据层的功能,为上层提供更为简洁易懂的接口。


1.先贴出查找的代码
package com.yee.mybatis;

import java.util.List;

public class SqlSession {
	private SqlSessionFactory factory;
	private DBHelper db;

	public SqlSession(SqlSessionFactory factory){
		this.factory=factory;
		db=new DBHelper(factory.getConfig().getDataSource());
	}

	/**
	 * 按要求返回查找的数据
	 * @param sqlId
	 * @param params
	 * @return
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public <T>List<T> selectList(String sqlId,Object ... params){
		//获取sql 
		//获取参数列表
		MapperInfo mapperInfo=factory.getMapperInfos().get(sqlId); //键值对存储可以快速获取 addDept findAll 和 对应的属性 parameterType resultMap
		if(mapperInfo!=null){
			try {
				String sql=mapperInfo.getSql();
				if(mapperInfo.isUpdate()){//如果是更新
					return null; 
				}else{  //为查询
					String className=mapperInfo.getResultType();
					Class c=Class.forName(className);
					//调用数据层返回结果
					return db.find(sql, c, params);
				}
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
			}
		}
		return null;
	}
	

四:总结体会

1.关于框架

sqlSession对象封装了传统的DBHelper实现数据层的功能,为上层提供更为简洁易懂的接口。这其实就是框架的意义所在,将底层复杂的东西封装好,对用户“隐藏”,提供处理过后的接口。也是框架搭建慢,使用方便的原因,底层各种映射,这样的处理比起“原生处理”要慢。但是框架提供了一致易于理解的接口,还是广泛应用于团队开发中,框架总的来说就是一种规范一种制度。

2.关于“原生态”编程

使用框架我们往往可以做到快速开发,迅速搭建,很多东西都交给框架去做,框架底层多用映射机制,速度比“原生”满。很多时候,如果要追求效率,就得加上优化处理。学习框架,不仅要会用,更要花时间去了解更为底层的原理和实现。


至此,我们已经完成了仿Mybatis的查找功能,增加删除和测试部分放到另一篇博客中。后续有时间还会继续对spring IOC spring AOP进行分析和模拟实现。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值