Mybatis simple framework source code implementation

Mybatis simple framework source code implementation

Source Download
Here Insert Picture Description

Mybatis create a framework, we first need to do the following things
1. Create a configuration information corresponding entity class Configuration and MapperStatement, when the configuration information extracted from the package
2. Create SqlSessionFactory factory class, load configuration information
3. Create by a SqlSessionFactory SqlSession
4. by obtaining SqlSession dynamic proxy interface mapper
queries 5. the method of dynamic callback proxy SqlSession in
6.SqlSession method of transmitting a query to the executor actuator
7. the actuator database connection
8. the actuator by the reflection statement is executed, returns geiSqlSession
9. the data is returned to the caller

First, we load a package of xml entity class

package com.ys.config;
/**
 * 解析xml配置文件中的标签含义
 * @author ys
 */
public class MappedStatement {
	//namespace标签
	private String namespace;
	//SQL语句标签的id属性
	private String sourceId;
	//返回值类型标签
	private String resultType;
	//存储sql语句
	private String 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;
	}
}

Create a file package properties of the entity class

In this class, the information stored in the xml configuration file in such a map set
package name + method name as key value
object encapsulates xml value as the value of the class

package com.ys.config;

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

/**
 * 这是一个更大的存储配置信息的文件
 * 
 * 加载properties文件
 * @author ys
 */
public class Configuration {
	//加载properties配置文件中的连接数据库的信息
	private String jdbcDriver;
	private String jdbcUrl;
	private String jdbcUsername;
	private String jdbcPassword;
	//存储多个xml文件
	private Map<String,MappedStatement> mappedStatement = new HashMap<String, MappedStatement>();
	
	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> getMappedStatement() {
		return mappedStatement;
	}
	public void setMappedStatement(Map<String, MappedStatement> mappedStatement) {
		this.mappedStatement = mappedStatement;
	}
}

sqlsessionfactory class (two functions, 1. loading configuration information, generating SqlSession)

package com.ys.session;
/**
 *1.在初始化是加载配置信息到configuration
 *2.生成sqlSession
 * @author ys
 */

import java.io.File;
import java.io.InputStream;
import java.net.URL;
import java.util.List;
import java.util.Properties;

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

import com.ys.config.Configuration;
import com.ys.config.MappedStatement;

public class SqlSessionFactory {
	//全局唯一的对象
	private final Configuration conf = new Configuration();
	
	public SqlSessionFactory(){
		//实例化时加载db.properties的数据库配置信息
		loadDbInfo();
		//将多个xml配置文件加载到Configuration对象中
		loadMappersInfo();
	}
	//记录mapper.xml文件存储位置
	public static final String MAPPER_CONFIG_LOCATION = "mappers";
	//记录连接数据库连接信息文件的存储位置
	public static final String DB_CONFIG_FILE = "db.properties";
	
	//加载数据库配置信息
	private void loadMappersInfo() {
		//加载数据库信息配置文件
		InputStream dbIn = SqlSessionFactory.class.getClassLoader().getResourceAsStream(DB_CONFIG_FILE);
		//持久化存储集合
		Properties p = new Properties();
		try{
			//读取配置文件中的信息.写入Properties对象
			p.load(dbIn);
		}catch (Exception e) {
			e.printStackTrace();
		}
		/*
		 *  jdbc.driver=com.mysql.jdbc.Driver
			jdbc.url=jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true
			jdbc.username=root
			jdbc.password=root
		 */
		//将数据库配置信息写入conf对象
		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文件
	private void loadDbInfo() {
		//创建mapper.xml的存储文件夹
		URL resources = null;
		//赋予路径
		resources = SqlSessionFactory.class.getClassLoader().getResource(MAPPER_CONFIG_LOCATION);
		//获取指定文件夹信息
		File mappers = new File(resources.getFile());
		//遍历文件
		if(mappers.isDirectory()){
			//将该文件夹下所有目录和文件存储
			File[] listFiles = mappers.listFiles();
			//遍历文件夹下所有mapper.xml,解析信息后,注册至conf
			for (File file : listFiles) {
				//判断是否是xml文件
//				if(file.getName().endsWith(".xml")){
					loadMapperInfo(file);
//				}
			}
		}
	}

	//加载指定的
	private void loadMapperInfo(File file) {
		//创建saxReader对象
		SAXReader reader = new SAXReader();
		//通过read方法读取一个文件转换成Document对象
		Document document = null;
		try{
			document = reader.read(file);
		}catch (Exception e) {
			e.printStackTrace();
		}
		//获取节点元素mapper/根节点
		Element root = document.getRootElement();
		//获取命名空间
		String namespace = root.attribute("namespace").getData().toString();
		//获取select子节点列表
		List<Element>  selects = root.elements("select"); 
		//遍历select节点,将信息记录到MappedStatement对象,并记录到Configuration对象中的map中
		for (Element element : selects) {
			//实例化MappedStatement
			MappedStatement mappedStatement = new MappedStatement();
			//选取id属性
			String id = element.attribute("id").getData().toString();
			//获取resultType属性
			String resultType = element.attribute("resultType").getData().toString();
			//读取sql语句信息
			String sql = element.getData().toString();
			//对 namespace和id属性进行拼接
			String sourceId = namespace+"."+id;
			//将信息封装到mappedStatement对象中
			mappedStatement.setSourceId(sourceId);
			mappedStatement.setResultType(resultType);
			mappedStatement.setSql(sql);
			mappedStatement.setNamespace(namespace);
			//注册到Configuration实例化对象map集合中
			conf.getMappedStatement().put(sourceId, mappedStatement);
		}
	}
	
	
	//第二功能  生成sqlsession
	public SqlSession openSession(){
		return new DefaultSqlSession(conf);
	}
}

sqlsession interface class

package com.ys.session;

import java.util.List;

/**
 * mybatis暴露给外部的接口,实现增删改查的能力
 * @author ys
 *
 *1.对外提供数据访问的API
 *2.对内将请求转发给executor
 */
public interface SqlSession {
	/**
	 * 根据传入的条件查询单一结果
	 * 
	 * @param statement 方法对应的sql语句 
	 * @param parameter 要传入到SQL语句中的查询参数
	 * @return 返回指定的结果对象
	 */
	<T> T selectOne(String statement,Object parameter);
	
	/**
	 * 根据条件经过查询,返回泛型集合
	 * @param statement 方法对应的sql语句 
	 * @param parameter 要传入到SQL语句中的查询参数
	 * @return 返回指定的结果对象
	 */
	<E> List<E> selectList(String statement,Object parameter);
	/**
	 * 根据mapper接口获取接口对应的动态代理实现
	 * @param type 指定的mapper接口
	 * @return		
	 */
	<T> T getMapper(Class<T> type);
}

sqlsession implementation class

1. To provide external implemented method (a good collection of map directly to the package configuration object acquires the corresponding sql statement)
2. Get actuator
3. dynamic interface proxy object

package com.ys.session;
import java.lang.reflect.Proxy;
/**
 * mybatis暴露给外部的接口,实现增删该查的能力
 * @author ys
 * 
 * 1.对外提供数据访问的API
 * 2.对内将请求转发给executor
 */
import java.util.List;

import com.ys.Executor.DefaultExecutor;
import com.ys.Executor.Executor;
import com.ys.binding.MapperProxy;
import com.ys.config.Configuration;
import com.ys.config.MappedStatement;

public class DefaultSqlSession implements SqlSession{
	//获取容器(加了final字段内存地址不会改变,不可修改,这样就可以提升效率)
	private final Configuration conf;
	//获取执行器
	private Executor executor;
	
	
	public DefaultSqlSession(Configuration conf){
		super();
		this.conf = conf;
		//加载执行器
		executor = new DefaultExecutor(conf);
	}
	
	
	public <T> T selectOne(String statement, Object parameter) {
		List<Object> selectList = this.selectList(statement, parameter);
		if(selectList == null || selectList.size() == 0){
			return null;
		}
		if(selectList.size() == 1){
			//类型强转
			return (T) selectList.get(0);
		}else{
			throw new RuntimeException("Too Many Results!");
		}
	}

	public <E> List<E> selectList(String statement, Object parameter) {
		//获取方法对应的SQL语句
		MappedStatement ms = conf.getMappedStatement().get(statement);
		return executor.query(ms, parameter);
	}
	
	//等待传入接口类型
	public <T> T getMapper(Class<T> type) {
		MapperProxy mp = new MapperProxy(this);
		//生成动态代理对象
		return (T) Proxy.newProxyInstance(
				type.getClassLoader(),
				new Class[]{type}, 
				mp);
	}
}

Dynamic agency business to achieve

1. Obtain the name of the method name after the object by stitching method name, you can find a map of the key values of the
2 sql statement to determine whether you need to pass parameters

package com.ys.binding;
/**
 * 动态代理业务逻辑实现
 */
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Collection;

import com.ys.session.SqlSession;

public class MapperProxy implements InvocationHandler {
	
	private SqlSession session;
	
	public MapperProxy(SqlSession session) {
		super();
		this.session = session;
	}

	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		//获取调用方法的返回类型
		Class<?> returnType = method.getReturnType();
		//如果返回值是集合类
		if(Collection.class.isAssignableFrom(returnType)){
			//第一个参数:获取类名全写+方法名(即Configuration对象中的map的key值)
			//第二个参数:简写,应该写成集合返回,这里只写了第一个测试
			return session.selectList(method.getDeclaringClass().getName()+"."+method.getName(), 
									args == null?null:args[0]);
		}else{
			return session.selectOne(method.getDeclaringClass().getName()+"."+method.getName(), 
					args == null?null:args[0]);
		}
	}
}

Actuator Interface

package com.ys.Executor;

import java.util.List;

import com.ys.config.MappedStatement;

/**
 * Mybatis的核心接口之一,定义了数据库的最基本的方法,sqlsession的功能都是基于它实现的
 * @author TEDU
 *
 */
public interface Executor {
	/**
	 * 查询接口
	 * @param ms		封装sql语句的MappedStatement参数
	 * @param parameter 传入sql的参数
	 * @return			将数据转换成指定对象的结果集返回
	 */
	<E> List<E> query(MappedStatement ms ,Object parameter);
}

Actuators implementation class

1. First obtain connections to the database
2. The process of perpreparedStatement placeholders
3. query the data package to the return value of type

package com.ys.Executor;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import com.ys.config.Configuration;
import com.ys.config.MappedStatement;
import com.ys.util.ReflectionUtil;

public class DefaultExecutor implements Executor{
	
	private final Configuration conf;
	
	public DefaultExecutor(Configuration conf) {
		super();
		this.conf = conf;
	}
	
	/**
	 * @param ms 封装sql语句MappsedStatement对象
	 * @param parameter 传入的sql参数
	 * @return 将数据转换成指定的对象结果集返回
	 */
	//实现JDBC规范,对数据库进行连接
	public <E> List<E> query(MappedStatement ms, Object parameter) {
//		System.out.println(ms.getSql());
//		System.out.println(ms.getResultType());
		//定义返回结果集
		List<E> ret = new ArrayList<E>();
		try{
			//加载数据驱动
			Class.forName(conf.getJdbcDriver());
		}catch (Exception e) {
			e.printStackTrace();
		}
		//获取连接
		Connection connection = null;
		//执行数据库语句
		PreparedStatement preparedStatement = null;
		//返回结果集
		ResultSet resultSet = null;
		try{
			//获取连接,从MappedStatement对象中获取数据库信息
			connection = DriverManager.getConnection(conf.getJdbcUrl(),conf.getJdbcUsername(),conf.getJdbcPassword());
			//创建preparedStatement,从MappedStatement中获取sql语句
			preparedStatement = connection.prepareStatement(ms.getSql());
			//处理sql语句中的占位符
			parameterized(preparedStatement,parameter);
			//执行查询结果获取resultSet
			resultSet = preparedStatement.executeQuery();
			//将结果集通过反射技术,填充到list集合中
			handlerResultSet(resultSet,ret,ms.getResultType());
		}catch (Exception e) {
			e.printStackTrace();
		}finally{
			try{
				resultSet.close();
				preparedStatement.close();
				connection.close();
			}catch (Exception e) {
				e.printStackTrace();
			}
		}
		return ret;
	}


	//读取result中的数据,并转换成目标对象
	private <E> void handlerResultSet(ResultSet resultSet, List<E> ret, String className) {
		Class<E> clazz = null;
		try{
			//通过反射获取类对象
			clazz = (Class<E>) Class.forName(className);
		}catch (Exception e) {
			e.printStackTrace();
		}
		try{
			while(resultSet.next()){
				//通过反射实例化对象
				Object entity = clazz.newInstance();
				//使用反射工具类将resultSet中的数据填充到entity中
				ReflectionUtil.setPropToBeanFromResultSet(entity, resultSet);
				//对象加入返回集合中
				ret.add((E) entity);
			}
		}catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	
	//对perpreparedStatement占位符进行处理
	private void parameterized(PreparedStatement preparedStatement, Object parameter) throws SQLException {
		if(parameter instanceof Integer){
			preparedStatement.setInt(1, (Integer) parameter);
		}else if(parameter instanceof Long){
			preparedStatement.setLong(1,  (Long) parameter);
		}else if(parameter instanceof String){
			preparedStatement.setString(1, (String) parameter);
		}
	}
}

Tools reflection

Adds data to help implement the return value of the object

package com.ys.util;
import java.lang.reflect.Field;
import java.sql.ResultSet;
import java.sql.SQLException;

import com.ys.entity.TUser;

/**
 * 反射工具类
 * @author ys
 *
 */
public class ReflectionUtil {
	/**
	 * 为指定的beanpropName属性的值设置为value
	 * 
	 * @param bean   目标对象
	 * @param proName对象的属性名
	 * @param value	 值
	 */
	public static void setPropToBean(Object bean ,String proName,Object value){
		Field f;
		try{
			//获取对象指定的属性
			f = bean.getClass().getDeclaredField(proName);
			//将字段设置为可通过反射访问
			f.setAccessible(true);
			//为属性赋值
			f.set(bean, value);
		}catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	
	/**
	 * 从resultSet中读取一行数据,并填充到指定的实体bean
	 * @param entity    	待填充的实体bean
	 * @param resultSet		从数据库中加载的数据
	 * @throws SQLException 
	 */
	public static void setPropToBeanFromResultSet(Object entity,ResultSet resultSet) throws SQLException{
		//通过反射获取对象的所有字段
		Field[] declaredFields = entity.getClass().getDeclaredFields();
		//遍历所有的字段,从resultSet中读取相应的电话机,并填充至对象的属性中
		for (int i = 0; i < declaredFields.length; i++) {
			//如果是字符串类型的数据
			if(declaredFields[i].getType().getSimpleName().equals("String")){
				setPropToBean(entity, declaredFields[i].getName(), resultSet.getString(declaredFields[i].getName()));
			//如果是int类型
			}else if(declaredFields[i].getType().getSimpleName().equals("Integer")){
				setPropToBean(entity, declaredFields[i].getName(), resultSet.getInt(declaredFields[i].getName()));
			//如果是long类型数据
			}else if(declaredFields[i].getType().getSimpleName().equals("Long")){
				setPropToBean(entity, declaredFields[i].getName(), resultSet.getLong(declaredFields[i].getName()));
			}
		}
	}
	
	
	//测试
	public static void main(String[] args) {
		TUser user = new TUser();
		ReflectionUtil.setPropToBean(user, "userName", "ys");
		System.out.println(user.getUserName());
	}
}

Data callback geisqlsession, then you can get out of the results of the query object in sqlsession

Database entity class

package com.ys.entity;

public class TUser {
	private Integer id;
	private String userName;
	private String realName;
	private String sex;
	private String mobile;
	private String email;
	private String note;
	private Integer position_id;
	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 getRealName() {
		return realName;
	}
	public void setRealName(String realName) {
		this.realName = realName;
	}
	public String getSex() {
		return sex;
	}
	public void setSex(String sex) {
		this.sex = sex;
	}
	public String getmobile() {
		return mobile;
	}
	public void setmobile(String mobile) {
		this.mobile = mobile;
	}
	public String getEmail() {
		return email;
	}
	public void setEmail(String email) {
		this.email = email;
	}
	public String getNote() {
		return note;
	}
	public void setNote(String note) {
		this.note = note;
	}
	public Integer getPosition_id() {
		return position_id;
	}
	public void setPosition_id(Integer position_id) {
		this.position_id = position_id;
	}
	@Override
	public String toString() {
		return "TUser [id=" + id + ", userName=" + userName + ", realName=" + realName + ", sex=" + sex + ", mobile="
				+ mobile + ", email=" + email + ", note=" + note + ", position_id=" + position_id + "]";
	}
}

properties profile

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true
jdbc.username=root
jdbc.password=root

xml configuration file

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ys.mapper.TUserMapper">
	<select id="selectByPrimaryKey" resultType="com.ys.entity.TUser">
		select 
			id ,userName, realName, sex, mobile, email,note,position_id
		from t_user where id = ?
	</select>
	<select id="selectAll" resultType="com.ys.entity.TUser">
		select 
			id ,userName, realName, sex, mobile, email,note,position_id
		from t_user
	</select>

</mapper>

mapper Interface

package com.ys.mapper;

import java.util.List;

import com.ys.entity.TUser;

public interface TUserMapper {
	
	TUser selectByPrimaryKey(Integer id);
	List<TUser> selectAll();
}

Database structure

Here Insert Picture Description

Test category

package com.ys;

import java.util.List;

import com.ys.entity.TUser;
import com.ys.mapper.TUserMapper;
import com.ys.session.SqlSession;
import com.ys.session.SqlSessionFactory;

public class TestMybatis {
	public static void main(String[] args) {
		//1.实例化sqlsessionfactory,加载数据配置文件及mapper.xml文件到configuration对象中
		SqlSessionFactory factory = new SqlSessionFactory();
		//2.获取sqlsession对象
		SqlSession session = factory.openSession();
		//3.通过动态代理跨域面向接口编程和ibatis编程模型的鸿沟
		TUserMapper userMapper = session.getMapper(TUserMapper.class);
		//4.遵循jdbc规范,通过底层的四大对象的合作完成数据查询和数据转化
		TUser user = userMapper.selectByPrimaryKey(2);
		System.out.println(user);
		
		System.out.println(
				"-----------------------------------------------"
				+ "---------------------------------------------"
				+ "------------------");
		List<TUser> selectAll = userMapper.selectAll();
		if(selectAll != null && selectAll.size() > 0){
			for (TUser tuser : selectAll) {
				System.out.println(tuser);
			}
		}
	}
}

Results of the

Here Insert Picture Description

Guess you like

Origin blog.csdn.net/yang134679/article/details/89073309