欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

mybatis的简单模拟

程序员文章站 2024-02-27 19:17:51
...

我们首先来观察 mybatis-config.xml,里面需要配置一个数据源,那么我们准备一个数据源的实体类。

mybatis-config.xml:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
  <environments default="development">
    <environment id="development">
      <transactionManager type="JDBC"/>
      <!-- 配置数据源 -->
      <dataSource type="POOLED">
        <property name="driver" value="oracle.jdbc.driver.OracleDriver"/>
        <property name="url" value="jdbc:oracle:thin:@127.0.0.1:1521:orcl"/>
        <property name="username" value="scott"/>
        <property name="password" value="123456"/>
      </dataSource>
    </environment>
  </environments>
  <mappers>
    <mapper resource="com/me/mybatis/DeptMapper.xml"/>
  </mappers>
</configuration>

数据源的实体类:

mybatis的简单模拟

对于mapper的映射文件,我们也可以提取成一个实体类,我们观察mapper.xml发现,我们可以从中把 sql语句、参数类型、返回值类型、是否为更新的sql语句  组成一个实体类。

DeptMapper.xml:

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

<mapper namespace="com.me.bean.DeptMapper">
 <insert id="addDept" parameterType="com.me.bean.DeptBean">
   insert into dept values(#{deptno},#{dname},#{loc}) 
 </insert>
 
  <insert id="addDept1" parameterType="map">
   insert into dept values(#{deptno},#{dname},#{loc}) 
 </insert>
 
  <select id="findAll" resultType="com.me.bean.DeptBean">
    select deptno,dname,loc from dept 
  </select>
</mapper>

mapper实体类: 

mybatis的简单模拟

 我们查询oracle中的dept表作为测试,创建一个dept的实体类

mybatis的简单模拟

 接下来具体实现。

先看看解析mybatis-config.xml的MyBatisConfig.java。使用dom4j解析xml。这个类主要解析了 数据源信息、mapper映射路径信息。

public class MyBatisConfig {
	private DataSource dataSource;  //数据源实体
	private List<String> mappers=new ArrayList<String>(); //保存mapper映射文件
	public MyBatisConfig(String config) {
		try {
			readXml(config);
		} catch (DocumentException e) {
			e.printStackTrace();
		}
	}
	/**
	 * 解析mybatis-config.xml
	 * @param config //文件路径
	 * @throws DocumentException
	 */
	private void readXml(String config) throws DocumentException {
		SAXReader reader = new SAXReader();
		Document doc = reader.read(this.getClass().getClassLoader().getResourceAsStream(config));
		
		XPath xPath = doc.createXPath("//dataSource/property"); //解析mybatis-config.xml下的property所有节点
		List<Element> properties = xPath.selectNodes(doc);
		
		dataSource=new DataSource();//创建一个数据源实体
		String pname=null;
		for (Element e1:properties) { //获取所有的property节点信息,保存到数据源实体类中
			pname=e1.attributeValue("name");
			switch (pname) {
			case "driver":
				dataSource.setDriver(e1.attributeValue("value"));
				break;
			case "url":
				dataSource.setUrl(e1.attributeValue("value"));
				break;
			case "username":
				dataSource.setUsername(e1.attributeValue("value"));
				break;
			case "password":
				dataSource.setPassword(e1.attributeValue("value"));
				break;
			
			default:
				break;
			}
		}
		
		//System.out.println(dataSource);
		//开始获取mybatis-config.xml中的<mappers>节点,即映射文件
		xPath = doc.createXPath("//mappers/mapper");
		List<Element> list = xPath.selectNodes(doc);
		for (Element el:list) {
			mappers.add(el.attributeValue("resource")); //保存到一个集合中
		}
		//到这里我们已经解析完了mybatis-config.xml
		//System.out.println(mappers); 
	}
	
	public DataSource getDataSource() {
		return dataSource;
	}
	
	public List<String> getMappers() {
		return mappers;
	}
	
	public static void main(String[] args) {
		MyBatisConfig myBatisConfig = new MyBatisConfig("mybatis-config.xml");
	}
}

模拟SqlSessionFactory的SqlSessionFactory.java。这个类解析了mapper映射文件,并将解析出来的信息保存到实体类MapperInfo中。

public class SqlSessionFactory {

	private MyBatisConfig config;  //mybatis-config.xml的解析结果
	private Map<String, MapperInfo> mapperInfos =new HashMap<String, MapperInfo>();//mapper映射文件的解析结果

	public  SqlSessionFactory(String configXml) {
		config=new MyBatisConfig(configXml);  //解析mybatis-config.xml
		try {
			parseXml(); //解析mapper映射文件
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * 解析mapper映射文件
	 * @throws DocumentException
	 */
	private void parseXml() throws DocumentException {
		List<String> mappers = config.getMappers(); //获取映射文件路径
		if (mappers!=null && !mappers.isEmpty()) {
			SAXReader reader = new SAXReader();
			Document doc= null;
			XPath xPath = null;
			List<Element> ops=null;

			String opname=null;
			MapperInfo mapperInfo=null;

			//循环解析所有映射文件
			for (String mapper:mappers) {
				doc = reader.read(this.getClass().getClassLoader().getResourceAsStream(mapper));
				xPath=doc.createXPath("//mapper/*"); //获取DeptMapper.xml下mapper下的所有节点
				ops=xPath.selectNodes(doc);

				for (Element el:ops) {
					mapperInfo=new MapperInfo(); //创建一个mapper实体类
					opname=el.getName();
					//如果是select节点,说明不是更新语句
					if (opname.equals("select")) { 
						mapperInfo.setUpdate(false);
					}
					//将各项信息填入新创建的mapper实体类
					mapperInfo.setParameterType(el.attributeValue("parameterType"));
					mapperInfo.setResultType(el.attributeValue("resultType"));
					mapperInfo.setSql(el.getTextTrim());

					//保存结果到一个集合中
					mapperInfos.put(el.attributeValue("id"), mapperInfo);
				}
			}
		}
	}

	public MyBatisConfig getConfig() {
		return config;
	}

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

最后就是比较难实现的SqlSession。这个类反射机制用的比较多,代码有详细注释。

public class SqlSession {

	private SqlSessionFactory factory; //SqlSessionFactory
	private DBHelper db;

	public SqlSession(SqlSessionFactory factory){
		this.factory=factory;
		db=new DBHelper(factory.getConfig().getDataSource()); //用已经解析好的数据源实体类去初始化DBhelper
	}

	/**
	 * 查询
	 * @param sqlId mapper映射文件下的节点id
	 * @param params 参数
	 * @return
	 */
	public <T> List<T> selectList(String sqlId,Object ... params) {
		String sql;//要执行的sql语句
		Class C;  //反射的对象
		try {
			MapperInfo mapperInfo=factory.getMapperInfos().get(sqlId);//获取该sqlId节点下的所有信息
			if (mapperInfo==null) {
				return null;
			}

			sql=mapperInfo.getSql(); //获取sql语句
			if (mapperInfo.isUpdate()) {
				return null;
			}

			String className = mapperInfo.getResultType();//获取返回值类型
			C=Class.forName(className); //反射得到该返回值类型的class
			return db.findObject(sql, C,params); //调用dbhelper按对象查询

		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}
	/**
	 * 更新情况(update,insert,delete)
	 * @param sqlId
	 * @param obj
	 * @return
	 */
	public int update(String sqlId,Object obj) {
		int result=0; //执行结果

		MapperInfo mapperInfo = factory.getMapperInfos().get(sqlId);
		if (mapperInfo==null) {
			return 0;
		}

		String sql=mapperInfo.getSql();
		String className = mapperInfo.getParameterType();

		Pattern pattern = Pattern.compile("[#][{]\\w+}"); //正则表达式 。将#{xxxx}变成#{?}
		Matcher matcher = pattern.matcher(sql);

		ArrayList<String> colNames = new ArrayList<String>();
		while (matcher.find()) {
			colNames.add(matcher.group().replaceAll("[#{}]*", ""));
		}
		sql=matcher.replaceAll("?");
		//如果参数类型为map
		if ("map".equals(className)) {
			Map<String, Object> map = (Map<String, Object>) obj;
			List<Object> params=new ArrayList<Object>(); //参数集合
			for (String colName:colNames) {
				params.add(map.get(colName));
			}
			return db.update(sql, params);//使用DBhelper查询
		}
		if ("String".equals(className)) {
			return -1;
		}

		//参数类型为对象时
		try {
			Class c =Class.forName(className); //反射得到class
			Method[] methods=c.getDeclaredMethods();  //获取该类中的声明方法
			List<Method> getMethods = new ArrayList<Method>();  //保存该对象的get方法
			for (Method md:methods) {
				if (md.getName().startsWith("get")) { 
					getMethods.add(md); //如果方法为get方法,保存
				}
			}
			List<Object> params=new ArrayList<Object>();//参数集合
			String mname;
			for (String colName:colNames) { 
				for(Method md:getMethods){
					mname="get"+colName.substring(0,1).toUpperCase()+colName.substring(1);//转换为getXxx的形式,例如getdeptno转换为getDeptno
					if (mname.equals(md.getName())) { //用get方法或的参数
						params.add(md.invoke(obj));  //反向执行
					}
				}
			}
			return db.update(sql, params); //返回执行结果
		} catch (Exception e) {
			e.printStackTrace();
		}
		return result;
	}
}

测试类:

public class _Main {

	@Test
	public void test() {
		SqlSessionFactory sqFactory = new SqlSessionFactory("mybatis-config.xml");
		//System.out.println(sqFactory.getMapperInfos());
		SqlSession session = new SqlSession(sqFactory);
		List<Object> dept = session.selectList("findAll");
		System.out.println(dept);
	}
	@Test
	public void test2() {
		SqlSessionFactory sqFactory = new SqlSessionFactory("mybatis-config.xml");
		SqlSession session = new SqlSession(sqFactory);
		int result=session.update("addDept", new DeptBean(88,"hah","666"));
		System.out.println(result);
		
	}
	
	@Test
	public void test3() {
		SqlSessionFactory sqFactory = new SqlSessionFactory("mybatis-config.xml");
		SqlSession session = new SqlSession(sqFactory);
		Map<String, Object> map = new HashMap<String, Object>();
		map.put("deptno", 99);
		map.put("dname", "呵呵");
		map.put("loc", "888");
		int result=session.update("addDept1", map);
		System.out.println(result);
	}
}

 DBHelper部分代码;

public class DBHelper {
	private DataSource dataSource;
	public  DBHelper(DataSource dataSource) {
		this.dataSource=dataSource;
		try {
			Class.forName(dataSource.getDriver());
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 获取连接
	 * @return
	 */
	public Connection getConnection() {
		Connection con = null;
		try {
			con = DriverManager.getConnection(dataSource.getUrl(),dataSource.getUsername(),dataSource.getPassword());
		    //Context context = new InitialContext();
		    //DataSource ds = (DataSource) context.lookup("java:comp/env/mysql");
		  //  con = ds.getConnection();
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return con;
	}
}

mybatis的简单模拟

相关标签: mybatis java