手写MyBatis ORM框架实践

一、实现手写Mybatis三个难点

1、接口既然不能被实例化?那么我们是怎么实现能够调用的?

2、参数如何和sql绑定

3、返回结果

下面是Mybatis接口

二、Demo实现

1、创建Maven工程(开发工具Eclipse)

下一步

下一步

然后点击“完成”

2、配置pom.xml

<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.example</groupId>
  <artifactId>writeMybatisDemo</artifactId>
  <version>0.0.1-SNAPSHOT</version>
 <dependencies>
		<!-- mybatis核心包 -->
		<dependency>
			<groupId>org.mybatis</groupId>
			<artifactId>mybatis</artifactId>
			<version>3.4.5</version>
		</dependency>

		<!-- 引入Spring-AOP等相关Jar -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-core</artifactId>
			<version>3.0.6.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>3.0.6.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-aop</artifactId>
			<version>3.0.6.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-orm</artifactId>
			<version>3.0.6.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjrt</artifactId>
			<version>1.6.1</version>
		</dependency>
		<dependency>
			<groupId>aspectj</groupId>
			<artifactId>aspectjweaver</artifactId>
			<version>1.5.3</version>
		</dependency>
		<dependency>
			<groupId>cglib</groupId>
			<artifactId>cglib</artifactId>
			<version>2.1_2</version>
		</dependency>

		<!-- https://mvnrepository.com/artifact/com.mchange/c3p0 -->
		<dependency>
			<groupId>com.mchange</groupId>
			<artifactId>c3p0</artifactId>
			<version>0.9.5.2</version>
		</dependency>
		<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>5.1.37</version>
		</dependency>
		<!-- https://mvnrepository.com/artifact/asm/asm -->
		<dependency>
			<groupId>asm</groupId>
			<artifactId>asm</artifactId>
			<version>3.3.1</version>
		</dependency>

	</dependencies>
</project>

  

3、JDBCUtil工具类

package com.exmaple.orm.util;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public final class JDBCUtils {

	private static String connect;
	private static String driverClassName;
	private static String URL;
	private static String username;
	private static String password;
	private static boolean autoCommit;

	/** 声明一个 Connection类型的静态属性,用来缓存一个已经存在的连接对象 */
	private static Connection conn;

	static {
		config();
	}

	/**
	 * 开头配置自己的数据库信息
	 */
	private static void config() {
		/*
		 * 获取驱动
		 */
		driverClassName = "com.mysql.jdbc.Driver";
		/*
		 * 获取URL
		 */
		URL = "jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8";
		/*
		 * 获取用户名
		 */
		username = "root";
		/*
		 * 获取密码
		 */
		password = "123456";
		/*
		 * 设置是否自动提交,一般为false不用改
		 */
		autoCommit = false;

	}

	/**
	 * 载入数据库驱动类
	 */
	private static boolean load() {
		try {
			Class.forName(driverClassName);
			return true;
		} catch (ClassNotFoundException e) {
			System.out.println("驱动类 " + driverClassName + " 加载失败");
		}

		return false;
	}

	/**
	 * 专门检查缓存的连接是否不可以被使用 ,不可以被使用的话,就返回 true
	 */
	private static boolean invalid() {
		if (conn != null) {
			try {
				if (conn.isClosed() || !conn.isValid(3)) {
					return true;
					/*
					 * isValid方法是判断Connection是否有效,如果连接尚未关闭并且仍然有效,则返回 true
					 */
				}
			} catch (SQLException e) {
				e.printStackTrace();
			}
			/*
			 * conn 既不是 null 且也没有关闭 ,且 isValid 返回 true,说明是可以使用的 ( 返回 false )
			 */
			return false;
		} else {
			return true;
		}
	}

	/**
	 * 建立数据库连接
	 */
	public static Connection connect() {
		if (invalid()) { /* invalid为true时,说明连接是失败的 */
			/* 加载驱动 */
			load();
			try {
				/* 建立连接 */
				conn = DriverManager.getConnection(URL, username, password);
			} catch (SQLException e) {
				System.out.println("建立 " + connect + " 数据库连接失败 , " + e.getMessage());
			}
		}
		return conn;
	}

	/**
	 * 设置是否自动提交事务
	 **/
	public static void transaction() {

		try {
			conn.setAutoCommit(autoCommit);
		} catch (SQLException e) {
			System.out.println("设置事务的提交方式为 : " + (autoCommit ? "自动提交" : "手动提交") + " 时失败: " + e.getMessage());
		}

	}

	/**
	 * 创建 Statement 对象
	 */
	public static Statement statement() {
		Statement st = null;
		connect();
		/* 如果连接是无效的就重新连接 */
		transaction();
		/* 设置事务的提交方式 */
		try {
			st = conn.createStatement();
		} catch (SQLException e) {
			System.out.println("创建 Statement 对象失败: " + e.getMessage());
		}

		return st;
	}

	/**
	 * 根据给定的带参数占位符的SQL语句,创建 PreparedStatement 对象
	 *
	 * @param SQL
	 *            带参数占位符的SQL语句
	 * @return 返回相应的 PreparedStatement 对象
	 */
	private static PreparedStatement prepare(String SQL, boolean autoGeneratedKeys) {

		PreparedStatement ps = null;
		connect();
		/* 如果连接是无效的就重新连接 */
		transaction();
		/* 设置事务的提交方式 */
		try {
			if (autoGeneratedKeys) {
				ps = conn.prepareStatement(SQL, Statement.RETURN_GENERATED_KEYS);
			} else {
				ps = conn.prepareStatement(SQL);
			}
		} catch (SQLException e) {
			System.out.println("创建 PreparedStatement 对象失败: " + e.getMessage());
		}

		return ps;

	}

	public static ResultSet query(String SQL, List<Object> params) {

		if (SQL == null || SQL.trim().isEmpty() || !SQL.trim().toLowerCase().startsWith("select")) {
			throw new RuntimeException("你的SQL语句为空或不是查询语句");
		}
		ResultSet rs = null;
		if (params.size() > 0) {
			/* 说明 有参数 传入,就需要处理参数 */
			PreparedStatement ps = prepare(SQL, false);
			try {
				for (int i = 0; i < params.size(); i++) {
					ps.setObject(i + 1, params.get(i));
				}
				rs = ps.executeQuery();
			} catch (SQLException e) {
				System.out.println("执行SQL失败: " + e.getMessage());
			}
		} else {
			/* 说明没有传入任何参数 */
			Statement st = statement();
			try {
				rs = st.executeQuery(SQL); // 直接执行不带参数的 SQL 语句
			} catch (SQLException e) {
				System.out.println("执行SQL失败: " + e.getMessage());
			}
		}

		return rs;

	}

	private static Object typeof(Object o) {
		Object r = o;

		if (o instanceof java.sql.Timestamp) {
			return r;
		}
		// 将 java.util.Date 转成 java.sql.Date
		if (o instanceof java.util.Date) {
			java.util.Date d = (java.util.Date) o;
			r = new java.sql.Date(d.getTime());
			return r;
		}
		// 将 Character 或 char 变成 String
		if (o instanceof Character || o.getClass() == char.class) {
			r = String.valueOf(o);
			return r;
		}
		return r;
	}

	public static boolean execute(String SQL, Object... params) {
		if (SQL == null || SQL.trim().isEmpty() || SQL.trim().toLowerCase().startsWith("select")) {
			throw new RuntimeException("你的SQL语句为空或有错");
		}
		boolean r = false;
		/* 表示 执行 DDL 或 DML 操作是否成功的一个标识变量 */

		/* 获得 被执行的 SQL 语句的 前缀 */
		SQL = SQL.trim();
		SQL = SQL.toLowerCase();
		String prefix = SQL.substring(0, SQL.indexOf(" "));
		String operation = ""; // 用来保存操作类型的 变量
		// 根据前缀 确定操作
		switch (prefix) {
		case "create":
			operation = "create table";
			break;
		case "alter":
			operation = "update table";
			break;
		case "drop":
			operation = "drop table";
			break;
		case "truncate":
			operation = "truncate table";
			break;
		case "insert":
			operation = "insert :";
			break;
		case "update":
			operation = "update :";
			break;
		case "delete":
			operation = "delete :";
			break;
		}
		if (params.length > 0) { // 说明有参数
			PreparedStatement ps = prepare(SQL, false);
			Connection c = null;
			try {
				c = ps.getConnection();
			} catch (SQLException e) {
				e.printStackTrace();
			}
			try {
				for (int i = 0; i < params.length; i++) {
					Object p = params[i];
					p = typeof(p);
					ps.setObject(i + 1, p);
				}
				ps.executeUpdate();
				commit(c);
				r = true;
			} catch (SQLException e) {
				System.out.println(operation + " 失败: " + e.getMessage());
				rollback(c);
			}

		} else { // 说明没有参数

			Statement st = statement();
			Connection c = null;
			try {
				c = st.getConnection();
			} catch (SQLException e) {
				e.printStackTrace();
			}
			// 执行 DDL 或 DML 语句,并返回执行结果
			try {
				st.executeUpdate(SQL);
				commit(c); // 提交事务
				r = true;
			} catch (SQLException e) {
				System.out.println(operation + " 失败: " + e.getMessage());
				rollback(c); // 回滚事务
			}
		}
		return r;
	}

	/*
	 *
	 * @param SQL 需要执行的 INSERT 语句
	 *
	 * @param autoGeneratedKeys 指示是否需要返回由数据库产生的键(自增长)
	 *
	 * @param params 将要执行的SQL语句中包含的参数占位符的 参数值
	 *
	 * @return 如果指定 autoGeneratedKeys 为 true 则返回由数据库产生的键; 如果指定 autoGeneratedKeys
	 * 为 false 则返回受当前SQL影响的记录数目
	 */
	public static int insert(String SQL, boolean autoGeneratedKeys, List<Object> params) {
		int var = -1;
		if (SQL == null || SQL.trim().isEmpty()) {
			throw new RuntimeException("你没有指定SQL语句,请检查是否指定了需要执行的SQL语句");
		}
		// 如果不是 insert 开头开头的语句
		if (!SQL.trim().toLowerCase().startsWith("insert")) {
			System.out.println(SQL.toLowerCase());
			throw new RuntimeException("你指定的SQL语句不是插入语句,请检查你的SQL语句");
		}
		// 获得 被执行的 SQL 语句的 前缀 ( 第一个单词 )
		SQL = SQL.trim();
		SQL = SQL.toLowerCase();
		if (params.size() > 0) { // 说明有参数
			PreparedStatement ps = prepare(SQL, autoGeneratedKeys);
			Connection c = null;
			try {
				c = ps.getConnection(); // 从 PreparedStatement 对象中获得 它对应的连接对象
			} catch (SQLException e) {
				e.printStackTrace();
			}
			try {
				for (int i = 0; i < params.size(); i++) {
					Object p = params.get(i);
					p = typeof(p);
					ps.setObject(i + 1, p);
				}
				int count = ps.executeUpdate();
				if (autoGeneratedKeys) { // 如果希望获得数据库产生的键
					ResultSet rs = ps.getGeneratedKeys(); // 获得数据库产生的键集
					if (rs.next()) { // 因为是保存的是单条记录,因此至多返回一个键
						var = rs.getInt(1); // 获得值并赋值给 var 变量
					}
				} else {
					var = count; // 如果不需要获得,则将受SQL影像的记录数赋值给 var 变量
				}
				commit(c);
			} catch (SQLException e) {
				System.out.println("数据保存失败: " + e.getMessage());
				rollback(c);
			}
		} else { // 说明没有参数
			Statement st = statement();
			Connection c = null;
			try {
				c = st.getConnection(); // 从 Statement 对象中获得 它对应的连接对象
			} catch (SQLException e) {
				e.printStackTrace();
			}
			// 执行 DDL 或 DML 语句,并返回执行结果
			try {
				int count = st.executeUpdate(SQL);
				if (autoGeneratedKeys) { // 如果企望获得数据库产生的键
					ResultSet rs = st.getGeneratedKeys(); // 获得数据库产生的键集
					if (rs.next()) { // 因为是保存的是单条记录,因此至多返回一个键
						var = rs.getInt(1); // 获得值并赋值给 var 变量
					}
				} else {
					var = count; // 如果不需要获得,则将受SQL影像的记录数赋值给 var 变量
				}
				commit(c); // 提交事务
			} catch (SQLException e) {
				System.out.println("数据保存失败: " + e.getMessage());
				rollback(c); // 回滚事务
			}
		}
		return var;
	}

	/** 提交事务 */
	private static void commit(Connection c) {
		if (c != null && !autoCommit) {
			try {
				c.commit();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}

	/** 回滚事务 */
	private static void rollback(Connection c) {
		if (c != null && !autoCommit) {
			try {
				c.rollback();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * 释放资源
	 **/
	public static void release(Object cloaseable) {

		if (cloaseable != null) {

			if (cloaseable instanceof ResultSet) {
				ResultSet rs = (ResultSet) cloaseable;
				try {
					rs.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}

			if (cloaseable instanceof Statement) {
				Statement st = (Statement) cloaseable;
				try {
					st.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}

			if (cloaseable instanceof Connection) {
				Connection c = (Connection) cloaseable;
				try {
					c.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}

		}

	}

}

  

4、测试JDBC

1)增加测试

public class JdbcTest {
	public static void main(String[] args) {
		// #{userName} 替换成?
		String insertSql = "insert into user(userName,userAge) values(?,?)";
		ArrayList<Object> arrayList = new ArrayList<>(); //非线程安全
		arrayList.add("Nick");
		arrayList.add(30);
		int insert = JDBCUtils.insert(insertSql, false, arrayList);
		System.out.println("insert:" + insert);
	}
}

  输出结果

2) 查询测试

	public static void query() throws SQLException {
		String selectSql = "select * from User where userName=? and userAge= ? ";
		ArrayList<Object> arrayList = new ArrayList<>(); //非线程安全
		arrayList.add("Nick");
		arrayList.add(30);
		ResultSet res = JDBCUtils.query(selectSql, arrayList);
		while (res.next()) {
			String userName = res.getString("userName");
			System.out.println("userName:" +userName);

		}

	}

  输出结果:

三、手写Mybatis注解版本框架-环境搭建

1、创建自定义插入注解

package com.example.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 自定义插入注解
 *
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ExtInsert {
	String value();
}

  

2、自定义参数注解

package com.example.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 自定义参数注解
 *
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface ExtParam {
	String value();
}

  

3、创建接口UserMapper

package com.example.mapper;

import com.example.annotation.ExtInsert;
import com.example.annotation.ExtParam;

//@Mapper
public interface UserMapper {

	@ExtInsert("insert into user(userName,userAge) values(#{userName},#{userAge})")
	public int insertUser(@ExtParam("userName") String userName, @ExtParam("userAge")Integer userAge);
}

 

四、基于AOP实现拦截mapper接口

1、使用反射动态拦截 接口方法

package com.example.mybatis.aop;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * 使用反射动态拦截 接口方法
 *
 */
public class MyInvocationHandlerMybatis implements InvocationHandler {

	private Object object;

	public MyInvocationHandlerMybatis(Object object) {
		this.object = object;
	}

	//proxy,代理对象 。 method: 拦截方法 。  args 方法上的参数值
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		System.out.println("使用动态代理技术拦截接口方法开始");
		return 1;
	}

}

  

2、创建SqlSession类

package com.example.sql;

import java.lang.reflect.Proxy;

import com.example.mybatis.aop.MyInvocationHandlerMybatis;

public class SqlSession {

	//加载Mapper接口
	@SuppressWarnings("unchecked")
	public static <T> T getMapper(Class<T> classz)
			throws IllegalArgumentException, InstantiationException, IllegalAccessException {
			return (T) Proxy.newProxyInstance(classz.getClassLoader(),
					new Class[]{classz}, new MyInvocationHandlerMybatis(classz));
	}
}

  

3、测试

public class Test003 {
	public static void main(String[] args) throws IllegalArgumentException, InstantiationException, IllegalAccessException {
		//使用动态代理技术虚拟调用方法
		UserMapper userMapper = SqlSession.getMapper(UserMapper.class);
		int result = userMapper.insertUser("Larry", 40);
		System.out.println("insert result:" + result);
	}
}

  返回结果

五、@extinsert和@extselect注解基本实现思路

1、ExtSelect 注解

/**
 * 自定义查询注解
 *
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ExtSelect {
	String value();
}

  

2、ExtInsert 注解

/**
 * 自定义插入注解
 *
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ExtInsert {
	String value();
}

  

3、User 类

package com.example.entity;

public class User {
	private String userName;
	private Integer userAge;

	public String getUserName() {
		return userName;
	}

	public void setUserName(String userName) {
		this.userName = userName;
	}

	public Integer getUserAge() {
		return userAge;
	}

	public void setUserAge(Integer userAge) {
		this.userAge = userAge;
	}

	@Override
	public String toString() {
		return "User [userName=" + userName + ", userAge=" + userAge + "]";
	}

}

 

4、UserMapper 接口

public interface UserMapper {

	@ExtInsert("insert into user(userName,userAge) values(#{userName},#{userAge})")
	public int insertUser(@ExtParam("userName") String userName, @ExtParam("userAge")Integer userAge);

	@ExtSelect("select * from User where userName=#{userName} and userAge=#{userAge} ")
	User selectUser(@ExtParam("userName") String name, @ExtParam("userAge") Integer userAge);

}

  

5、 SQL拼接

package com.exmaple.orm.util;

import java.util.ArrayList;
import java.util.List;

/**
 * SQL拼接<br>
 */
public class SQLUtils {

	/**
	 * 获取Insert语句后面values 参数信息<br>
	 */
	public static String[] sqlInsertParameter(String sql) {
		int startIndex = sql.indexOf("values");
		int endIndex = sql.length();
		String substring = sql.substring(startIndex + 6, endIndex).replace("(", "").replace(")", "").replace("#{", "")
				.replace("}", "");
		String[] split = substring.split(",");
		return split;
	}

	/**
	 *
	 * 获取select 后面where语句
	 */
	public static List<String> sqlSelectParameter(String sql) {
		int startIndex = sql.indexOf("where");
		int endIndex = sql.length();
		String substring = sql.substring(startIndex + 5, endIndex);
		String[] split = substring.split("and");
		List<String> listArr = new ArrayList<>();
		for (String string : split) {
			String[] sp2 = string.split("=");
			listArr.add(sp2[0].trim());
		}
		return listArr;
	}

	/**
	 * 将SQL语句的参数替换变为?<br>
	 *
	 */
	public static String parameQuestion(String sql, String[] parameterName) {
		for (int i = 0; i < parameterName.length; i++) {
			String string = parameterName[i];
			sql = sql.replace("#{" + string + "}", "?");
		}
		return sql;
	}

	public static String parameQuestion(String sql, List<String> parameterName) {
		for (int i = 0; i < parameterName.size(); i++) {
			String string = parameterName.get(i);
			sql = sql.replace("#{" + string + "}", "?");
		}
		return sql;
	}

	public static void main(String[] args) {
		 String sql = "insert into user(userName,userAge) values(#{userName},#{userAge})";
		 String[] sqlParameter = sqlInsertParameter(sql);
		 for (String string : sqlParameter) {
		 System.out.println(string);
		 }
		/*List<String> sqlSelectParameter = SQLUtils
				.sqlSelectParameter("select * from User where userName=#{userName} and userAge=#{userAge} ");
		for (String string : sqlSelectParameter) {
			System.out.println(string);
		}*/
	}
}

 

6、完整的MyInvocationHandlerMybatis 类

package com.example.mybatis.aop;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import com.example.annotation.ExtInsert;
import com.example.annotation.ExtParam;
import com.example.annotation.ExtSelect;
import com.exmaple.orm.util.JDBCUtils;
import com.exmaple.orm.util.SQLUtils;

/**
 * 使用反射动态拦截 接口方法
 *
 */
public class MyInvocationHandlerMybatis implements InvocationHandler {

	private Object object;

	public MyInvocationHandlerMybatis(Object object) {
		this.object = object;
	}

	//proxy,代理对象 。 method: 拦截方法 。  args 方法上的参数值
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		System.out.println("使用动态代理技术拦截接口方法开始");
		//ExtInsert的封装过程
		//1、判断方法上是否存在@ExtInsert注解
		ExtInsert extInsert = method.getDeclaredAnnotation(ExtInsert.class);
		if(extInsert != null){
			return exeInsertSQL(extInsert, proxy, method, args);
		}
		//二、查询思路
		//1判断方法上是否存在@ExtSelect注解
		ExtSelect extSelect = method.getDeclaredAnnotation(ExtSelect.class);
		if(extSelect != null){
			// 2、获取SQL语句,获取注解的Select语句
			String selectSQL = extSelect.value();
		   //3、获取方法的参数和SQL参数进行匹配
			ConcurrentHashMap<Object, Object> paramMap = paramsMap(proxy, method, args);
			//4、替换参数变? 传递方式
			List<String> sqlSelectParams = SQLUtils.sqlSelectParameter(selectSQL);
			//5.传递参数
			List<Object> sqlParams = new ArrayList<>();
			for(String parameterName : sqlSelectParams){
				Object parameterValue = paramMap.get(parameterName);
				sqlParams.add(parameterValue);
			}
			// 6.将sql语句换成?
			String newSql = SQLUtils.parameQuestion(selectSQL, sqlSelectParams);

			System.out.println("newSQL:" + newSql + ", sqlParams:" +sqlParams.toString() );
			//6、调用jdbc底层代码执行语句;
			ResultSet res = JDBCUtils.query(newSql, sqlParams);

			//7、使用反射机制实例对象### 获取方法返回的类型,进行实例化
			//思路:
			//1).使用反射机制获取方法类型,
			//2). 判断是否有结果集,在进行初始化
			//3).使用反射机制给对象复制
			while(!res.next()){
				return null;
				//System.out.println(query.getObject(1));
			}

			//下标往上移动一位
			res.previous();

		    //1).使用反射机制获取方法类型,
			Class<?> retrunType = method.getReturnType();
			Object object = retrunType.newInstance();
			while(res.next()){
				//方式一
				/*for(String parameterName: sqlSelectParams){
					Object resultValue = res.getObject(parameterName);
					//使用Java的反射赋值
					Field field = retrunType.getDeclaredField(parameterName);
					//私有方法允许访问
					field.setAccessible(true);
					field.set(object, resultValue);
				}*/
				//方式二 获取当前所有的属性
				Field[] declaredFields = retrunType.getDeclaredFields();
				for(Field field : declaredFields){
					String fildName = field.getName();
					Object fildVale = res.getObject(fildName);
					field.setAccessible(true);
					field.set(object, fildVale);
				}
			}
			return object;

		}

		return null;
	}

	private ConcurrentHashMap<Object, Object> paramsMap(Object proxy, Method method, Object[] args){
		ConcurrentHashMap<Object,Object> paramMap = new ConcurrentHashMap<>();
		//获取方法上的参数
		Parameter[] parameters = method.getParameters();

		for(int i = 0; i <parameters.length; i++){
			//参数名称
			Parameter parameter = parameters[i];
			ExtParam extParam = parameter.getDeclaredAnnotation(ExtParam.class);
			if(extParam != null){
				//参数名称
				String paramName = extParam.value();
				Object paramValue = args[i];
				//System.out.println("paramName:" + paramName  + " paramValue:" + paramValue);
				paramMap.put(paramName,  paramValue);
			}

		}
		return paramMap;
	}

	private List<Object> sqlInsertParameter(String[] sqlInsertParameter , ConcurrentHashMap<Object, Object> paramMap){
		List<Object> sqlParams = new ArrayList<>();

		for(String paramName: sqlInsertParameter){
		  Object paramValue =	paramMap.get(paramName);
		  sqlParams.add(paramValue);
		}
		return sqlParams;
	}

	public Object exeInsertSQL(ExtInsert extInsert,Object proxy, Method method, Object[] args) {
		//方法上存在@ExtInsert,获取他的SQL语句
		//2、获取SQL语句,获取注解的Insert语句
		String insertSql = extInsert.value();
		//System.out.println("insertSql:" + insertSql);
		//3、获取方法的参数和SQL参数进行匹配
		//定义一个Map集合,KEY为@ExtParamValue, Value 结果为参数值
		ConcurrentHashMap<Object, Object> paramMap = paramsMap(proxy, method, args);

		//存放sql执行参数--参数绑定过程
		String[] sqlInsertParameter = SQLUtils.sqlInsertParameter(insertSql);
		List<Object> sqlParams = sqlInsertParameter(sqlInsertParameter, paramMap);

		//4、替换参数变?
		String newSQL = SQLUtils.parameQuestion(insertSql, sqlInsertParameter);
		System.out.println("newSQL:" + newSQL + ",sqlParams:" + sqlParams.toString());
		//5、调用jdbc底层代码执行语句;
		return JDBCUtils.insert(newSQL, false, sqlParams);
	}

}

  

 7、测试

1)查询测试

public class Test003 {
	public static void main(String[] args) throws IllegalArgumentException, InstantiationException, IllegalAccessException {
		//使用动态代理技术虚拟调用方法
		UserMapper userMapper = SqlSession.getMapper(UserMapper.class);

		User user = userMapper.selectUser("Nick", 30);
		System.out.println("user:" + user.toString());
	}
}

  返回结果

2)插入结果测试

public class Test003 {
	public static void main(String[] args) throws IllegalArgumentException, InstantiationException, IllegalAccessException {
		//使用动态代理技术虚拟调用方法
		UserMapper userMapper = SqlSession.getMapper(UserMapper.class);
		int result = userMapper.insertUser("Tom3", 20);
		System.out.println("insert result:" + result);

	}
}

  返回结果

原文地址:https://www.cnblogs.com/linlf03/p/10878831.html

时间: 2024-10-08 08:13:05

手写MyBatis ORM框架实践的相关文章

手写MyBatis,纯手工打造开源框架(第四篇:决胜千里)- 第272篇

说明 MyBatis版本:3.5.1 相关历史文章(阅读本文之前,您可能需要先看下之前的系列) Spring Boot MyBatis最全教程:你值得拥有MyBatis能脱离Spring吗一图纵览MyBatis的工作原理从源码看MyBatis,竟如此简单MyBatis的Mapper是什么`垃圾` 手写MyBatis,纯手工打造开源框架(第一篇:风云再起) 手写MyBatis,纯手工打造开源框架(第二篇:君临天下) 手写MyBatis,纯手工打造开源框架(第三篇:运筹帷幄) 前言        运

了解mybatis源码手写mybatis

一:mybatis概述 MyBatis 是一款优秀的持久层框架,它支持定制化 SQL.存储过程以及高级映射.MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集.MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Ordinary Java Object,普通的 Java对象)映射成数据库中的记录. 二:手写mybatis 实现思路:: 1创建SqlSessionFactory实例 2:实例化过程中,加载配置

手写Mybatis,彻底搞懂框架原理

mybatis的前身是iBatis,其源于"Internet"和"abatis"的组合,是一款优秀的持久层框架,它支持定制化SQL.存储过程以及高级映射.mybatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果集,它可以使用简单的XML或注解来配置和映射原生信息,将接口和Java的POJO映射成数据库中的记录. Mybatis的架构设计Mybatis的功能架构分为三层: 接口层:提供给外部使用的接口API,开发人员通过这些本地API来操纵数据库.接口层接

自己手写WEB程序框架并运行

1.新建文件夹,起名MyWeb 2.文件夹下,新建两个文件夹 WEB-INF, META-INF,,还可以新建一些jsp,html文件 ,如 index.html 3在WEB-INF中必须存在一个文件WEB.xml, 还包含两个文件夹 lib, classes 4 在WEB.xml中写入最简单的<web-app> ....  </web-app>,,,,,编辑一下index.html 下面是要运行了 1,cmd 2,切换到MyWeb的文件夹,jar 命令查看一下各个参数的含义 3,

手写Spring事务框架

Spring事务基于AOP环绕通知和异常通知 编程事务 声明事务 Spring事务底层使用编程事务+AOP进行包装的   = 声明事务 AOP应用场景:  事务 权限 参数验证 什么是AOP技术 AOP技术应用场景 面向切面编程  解决代码复用问题 AOP编程核心点: 在方法之前或者之后处理事情 AOP底层实现原理:代理设计模式 Spring事务基于AOP的环绕通知 为什么用AOP: 复用 解耦 AOP: 静态代需要生成目标代理对象 动态代理不需要生成目标代理对象 动态代理分为:JDK动态代理 

攻占SSM之手写Mybatis的xml实现Dao层接口

上午写了一个简单的 从xml读取信息实例化一个Bean对象.下午就开始想mybatis是怎么通过xml文件来实现dao层接口的,一开始想直接用Class.forName(String name)然后调用getMethods的办法来得到Dao接口所有的方法Method,结果证明这对接口实没有用的,会报一个错误.于是想百度,但是百度的结果全是怎么配置mapper的..然后我又四处翻资料,终于找到一些办法.最后我还是用到了我自己封装的DButil 和 DataUtil两个类. 反正我是这么实现的,至于

从零开始手写 dubbo rpc 框架

rpc rpc 是基于 netty 实现的 java rpc 框架,类似于 dubbo. 主要用于个人学习,由渐入深,理解 rpc 的底层实现原理. 前言 工作至今,接触 rpc 框架已经有很长时间. 但是对于其原理一直只是知道个大概,从来没有深入学习过. 以前一直想写,但由于各种原因被耽搁. 技术准备 Java 并发实战学习 TCP/IP 协议学习笔记 Netty 权威指南学习 这些技术的准备阶段,花费了比较长的时间. 也建议想写 rpc 框架的有相关的知识储备. 其他 rpc 框架使用的经验

从零开始手写 spring ioc 框架,深入学习 spring 源码

IoC Ioc 是一款 spring ioc 核心功能简化实现版本,便于学习和理解原理. 创作目的 使用 spring 很长时间,对于 spring 使用非常频繁,实际上对于源码一直没有静下心来学习过. 但是 spring 源码存在一个问题,那就是过于抽象,导致学习起来成本上升. 所以本项目由渐入深,只实现 spring 的核心功能,便于自己和他人学习 spring 的核心原理. spring 的核心 Spring 的核心就是 spring-beans,后面的一切 spring-boot,spr

尝试手写orm框架

前言: 在使用各种的orm框架的过程中,菜鸟的我始终没有搞懂底层实现技术,最近刚好没事找了些视频和资料了解一点皮毛,想记录下,大家勿喷. 所谓的ORM(Object Relational Mapping) 对象关系映射 官方解释是通过使用描述对象和数据库之间映射的元数据,将面向对象程序的对象自动持久化到关系数据库中. 个人理解就是一个数据库访问的帮助类,可以让我们不用手写sql,就完成数据库的访问 使用的技术: 泛型.反射.特性.扩展 摸索步骤: step1 新建项目,建几个类库,大家熟悉的三层