一、实现手写Mybatis三个难点

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

2、参数如何和sql绑定

3、返回结果

下面是Mybatis接口

二、Demo实现

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

下一步

下一步

然后点击“完成”

2、配置pom.xml

  1. <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">
  2. <modelVersion>4.0.0</modelVersion>
  3. <groupId>com.example</groupId>
  4. <artifactId>writeMybatisDemo</artifactId>
  5. <version>0.0.1-SNAPSHOT</version>
  6. <dependencies>
  7. <!-- mybatis核心包 -->
  8. <dependency>
  9. <groupId>org.mybatis</groupId>
  10. <artifactId>mybatis</artifactId>
  11. <version>3.4.5</version>
  12. </dependency>
  13.  
  14. <!-- 引入Spring-AOP等相关Jar -->
  15. <dependency>
  16. <groupId>org.springframework</groupId>
  17. <artifactId>spring-core</artifactId>
  18. <version>3.0.6.RELEASE</version>
  19. </dependency>
  20. <dependency>
  21. <groupId>org.springframework</groupId>
  22. <artifactId>spring-context</artifactId>
  23. <version>3.0.6.RELEASE</version>
  24. </dependency>
  25. <dependency>
  26. <groupId>org.springframework</groupId>
  27. <artifactId>spring-aop</artifactId>
  28. <version>3.0.6.RELEASE</version>
  29. </dependency>
  30. <dependency>
  31. <groupId>org.springframework</groupId>
  32. <artifactId>spring-orm</artifactId>
  33. <version>3.0.6.RELEASE</version>
  34. </dependency>
  35. <dependency>
  36. <groupId>org.aspectj</groupId>
  37. <artifactId>aspectjrt</artifactId>
  38. <version>1.6.1</version>
  39. </dependency>
  40. <dependency>
  41. <groupId>aspectj</groupId>
  42. <artifactId>aspectjweaver</artifactId>
  43. <version>1.5.3</version>
  44. </dependency>
  45. <dependency>
  46. <groupId>cglib</groupId>
  47. <artifactId>cglib</artifactId>
  48. <version>2.1_2</version>
  49. </dependency>
  50.  
  51. <!-- https://mvnrepository.com/artifact/com.mchange/c3p0 -->
  52. <dependency>
  53. <groupId>com.mchange</groupId>
  54. <artifactId>c3p0</artifactId>
  55. <version>0.9.5.2</version>
  56. </dependency>
  57. <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
  58. <dependency>
  59. <groupId>mysql</groupId>
  60. <artifactId>mysql-connector-java</artifactId>
  61. <version>5.1.37</version>
  62. </dependency>
  63. <!-- https://mvnrepository.com/artifact/asm/asm -->
  64. <dependency>
  65. <groupId>asm</groupId>
  66. <artifactId>asm</artifactId>
  67. <version>3.3.1</version>
  68. </dependency>
  69.  
  70. </dependencies>
  71. </project>

  

3、JDBCUtil工具类

  1. package com.exmaple.orm.util;
  2.  
  3. import java.sql.Connection;
  4. import java.sql.DriverManager;
  5. import java.sql.PreparedStatement;
  6. import java.sql.ResultSet;
  7. import java.sql.ResultSetMetaData;
  8. import java.sql.SQLException;
  9. import java.sql.Statement;
  10. import java.util.ArrayList;
  11. import java.util.HashMap;
  12. import java.util.List;
  13. import java.util.Map;
  14.  
  15. public final class JDBCUtils {
  16.  
  17. private static String connect;
  18. private static String driverClassName;
  19. private static String URL;
  20. private static String username;
  21. private static String password;
  22. private static boolean autoCommit;
  23.  
  24. /** 声明一个 Connection类型的静态属性,用来缓存一个已经存在的连接对象 */
  25. private static Connection conn;
  26.  
  27. static {
  28. config();
  29. }
  30.  
  31. /**
  32. * 开头配置自己的数据库信息
  33. */
  34. private static void config() {
  35. /*
  36. * 获取驱动
  37. */
  38. driverClassName = "com.mysql.jdbc.Driver";
  39. /*
  40. * 获取URL
  41. */
  42. URL = "jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8";
  43. /*
  44. * 获取用户名
  45. */
  46. username = "root";
  47. /*
  48. * 获取密码
  49. */
  50. password = "123456";
  51. /*
  52. * 设置是否自动提交,一般为false不用改
  53. */
  54. autoCommit = false;
  55.  
  56. }
  57.  
  58. /**
  59. * 载入数据库驱动类
  60. */
  61. private static boolean load() {
  62. try {
  63. Class.forName(driverClassName);
  64. return true;
  65. } catch (ClassNotFoundException e) {
  66. System.out.println("驱动类 " + driverClassName + " 加载失败");
  67. }
  68.  
  69. return false;
  70. }
  71.  
  72. /**
  73. * 专门检查缓存的连接是否不可以被使用 ,不可以被使用的话,就返回 true
  74. */
  75. private static boolean invalid() {
  76. if (conn != null) {
  77. try {
  78. if (conn.isClosed() || !conn.isValid(3)) {
  79. return true;
  80. /*
  81. * isValid方法是判断Connection是否有效,如果连接尚未关闭并且仍然有效,则返回 true
  82. */
  83. }
  84. } catch (SQLException e) {
  85. e.printStackTrace();
  86. }
  87. /*
  88. * conn 既不是 null 且也没有关闭 ,且 isValid 返回 true,说明是可以使用的 ( 返回 false )
  89. */
  90. return false;
  91. } else {
  92. return true;
  93. }
  94. }
  95.  
  96. /**
  97. * 建立数据库连接
  98. */
  99. public static Connection connect() {
  100. if (invalid()) { /* invalid为true时,说明连接是失败的 */
  101. /* 加载驱动 */
  102. load();
  103. try {
  104. /* 建立连接 */
  105. conn = DriverManager.getConnection(URL, username, password);
  106. } catch (SQLException e) {
  107. System.out.println("建立 " + connect + " 数据库连接失败 , " + e.getMessage());
  108. }
  109. }
  110. return conn;
  111. }
  112.  
  113. /**
  114. * 设置是否自动提交事务
  115. **/
  116. public static void transaction() {
  117.  
  118. try {
  119. conn.setAutoCommit(autoCommit);
  120. } catch (SQLException e) {
  121. System.out.println("设置事务的提交方式为 : " + (autoCommit ? "自动提交" : "手动提交") + " 时失败: " + e.getMessage());
  122. }
  123.  
  124. }
  125.  
  126. /**
  127. * 创建 Statement 对象
  128. */
  129. public static Statement statement() {
  130. Statement st = null;
  131. connect();
  132. /* 如果连接是无效的就重新连接 */
  133. transaction();
  134. /* 设置事务的提交方式 */
  135. try {
  136. st = conn.createStatement();
  137. } catch (SQLException e) {
  138. System.out.println("创建 Statement 对象失败: " + e.getMessage());
  139. }
  140.  
  141. return st;
  142. }
  143.  
  144. /**
  145. * 根据给定的带参数占位符的SQL语句,创建 PreparedStatement 对象
  146. *
  147. * @param SQL
  148. * 带参数占位符的SQL语句
  149. * @return 返回相应的 PreparedStatement 对象
  150. */
  151. private static PreparedStatement prepare(String SQL, boolean autoGeneratedKeys) {
  152.  
  153. PreparedStatement ps = null;
  154. connect();
  155. /* 如果连接是无效的就重新连接 */
  156. transaction();
  157. /* 设置事务的提交方式 */
  158. try {
  159. if (autoGeneratedKeys) {
  160. ps = conn.prepareStatement(SQL, Statement.RETURN_GENERATED_KEYS);
  161. } else {
  162. ps = conn.prepareStatement(SQL);
  163. }
  164. } catch (SQLException e) {
  165. System.out.println("创建 PreparedStatement 对象失败: " + e.getMessage());
  166. }
  167.  
  168. return ps;
  169.  
  170. }
  171.  
  172. public static ResultSet query(String SQL, List<Object> params) {
  173.  
  174. if (SQL == null || SQL.trim().isEmpty() || !SQL.trim().toLowerCase().startsWith("select")) {
  175. throw new RuntimeException("你的SQL语句为空或不是查询语句");
  176. }
  177. ResultSet rs = null;
  178. if (params.size() > 0) {
  179. /* 说明 有参数 传入,就需要处理参数 */
  180. PreparedStatement ps = prepare(SQL, false);
  181. try {
  182. for (int i = 0; i < params.size(); i++) {
  183. ps.setObject(i + 1, params.get(i));
  184. }
  185. rs = ps.executeQuery();
  186. } catch (SQLException e) {
  187. System.out.println("执行SQL失败: " + e.getMessage());
  188. }
  189. } else {
  190. /* 说明没有传入任何参数 */
  191. Statement st = statement();
  192. try {
  193. rs = st.executeQuery(SQL); // 直接执行不带参数的 SQL 语句
  194. } catch (SQLException e) {
  195. System.out.println("执行SQL失败: " + e.getMessage());
  196. }
  197. }
  198.  
  199. return rs;
  200.  
  201. }
  202.  
  203. private static Object typeof(Object o) {
  204. Object r = o;
  205.  
  206. if (o instanceof java.sql.Timestamp) {
  207. return r;
  208. }
  209. // 将 java.util.Date 转成 java.sql.Date
  210. if (o instanceof java.util.Date) {
  211. java.util.Date d = (java.util.Date) o;
  212. r = new java.sql.Date(d.getTime());
  213. return r;
  214. }
  215. // 将 Character 或 char 变成 String
  216. if (o instanceof Character || o.getClass() == char.class) {
  217. r = String.valueOf(o);
  218. return r;
  219. }
  220. return r;
  221. }
  222.  
  223. public static boolean execute(String SQL, Object... params) {
  224. if (SQL == null || SQL.trim().isEmpty() || SQL.trim().toLowerCase().startsWith("select")) {
  225. throw new RuntimeException("你的SQL语句为空或有错");
  226. }
  227. boolean r = false;
  228. /* 表示 执行 DDL 或 DML 操作是否成功的一个标识变量 */
  229.  
  230. /* 获得 被执行的 SQL 语句的 前缀 */
  231. SQL = SQL.trim();
  232. SQL = SQL.toLowerCase();
  233. String prefix = SQL.substring(0, SQL.indexOf(" "));
  234. String operation = ""; // 用来保存操作类型的 变量
  235. // 根据前缀 确定操作
  236. switch (prefix) {
  237. case "create":
  238. operation = "create table";
  239. break;
  240. case "alter":
  241. operation = "update table";
  242. break;
  243. case "drop":
  244. operation = "drop table";
  245. break;
  246. case "truncate":
  247. operation = "truncate table";
  248. break;
  249. case "insert":
  250. operation = "insert :";
  251. break;
  252. case "update":
  253. operation = "update :";
  254. break;
  255. case "delete":
  256. operation = "delete :";
  257. break;
  258. }
  259. if (params.length > 0) { // 说明有参数
  260. PreparedStatement ps = prepare(SQL, false);
  261. Connection c = null;
  262. try {
  263. c = ps.getConnection();
  264. } catch (SQLException e) {
  265. e.printStackTrace();
  266. }
  267. try {
  268. for (int i = 0; i < params.length; i++) {
  269. Object p = params[i];
  270. p = typeof(p);
  271. ps.setObject(i + 1, p);
  272. }
  273. ps.executeUpdate();
  274. commit(c);
  275. r = true;
  276. } catch (SQLException e) {
  277. System.out.println(operation + " 失败: " + e.getMessage());
  278. rollback(c);
  279. }
  280.  
  281. } else { // 说明没有参数
  282.  
  283. Statement st = statement();
  284. Connection c = null;
  285. try {
  286. c = st.getConnection();
  287. } catch (SQLException e) {
  288. e.printStackTrace();
  289. }
  290. // 执行 DDL 或 DML 语句,并返回执行结果
  291. try {
  292. st.executeUpdate(SQL);
  293. commit(c); // 提交事务
  294. r = true;
  295. } catch (SQLException e) {
  296. System.out.println(operation + " 失败: " + e.getMessage());
  297. rollback(c); // 回滚事务
  298. }
  299. }
  300. return r;
  301. }
  302.  
  303. /*
  304. *
  305. * @param SQL 需要执行的 INSERT 语句
  306. *
  307. * @param autoGeneratedKeys 指示是否需要返回由数据库产生的键(自增长)
  308. *
  309. * @param params 将要执行的SQL语句中包含的参数占位符的 参数值
  310. *
  311. * @return 如果指定 autoGeneratedKeys 为 true 则返回由数据库产生的键; 如果指定 autoGeneratedKeys
  312. * 为 false 则返回受当前SQL影响的记录数目
  313. */
  314. public static int insert(String SQL, boolean autoGeneratedKeys, List<Object> params) {
  315. int var = -1;
  316. if (SQL == null || SQL.trim().isEmpty()) {
  317. throw new RuntimeException("你没有指定SQL语句,请检查是否指定了需要执行的SQL语句");
  318. }
  319. // 如果不是 insert 开头开头的语句
  320. if (!SQL.trim().toLowerCase().startsWith("insert")) {
  321. System.out.println(SQL.toLowerCase());
  322. throw new RuntimeException("你指定的SQL语句不是插入语句,请检查你的SQL语句");
  323. }
  324. // 获得 被执行的 SQL 语句的 前缀 ( 第一个单词 )
  325. SQL = SQL.trim();
  326. SQL = SQL.toLowerCase();
  327. if (params.size() > 0) { // 说明有参数
  328. PreparedStatement ps = prepare(SQL, autoGeneratedKeys);
  329. Connection c = null;
  330. try {
  331. c = ps.getConnection(); // 从 PreparedStatement 对象中获得 它对应的连接对象
  332. } catch (SQLException e) {
  333. e.printStackTrace();
  334. }
  335. try {
  336. for (int i = 0; i < params.size(); i++) {
  337. Object p = params.get(i);
  338. p = typeof(p);
  339. ps.setObject(i + 1, p);
  340. }
  341. int count = ps.executeUpdate();
  342. if (autoGeneratedKeys) { // 如果希望获得数据库产生的键
  343. ResultSet rs = ps.getGeneratedKeys(); // 获得数据库产生的键集
  344. if (rs.next()) { // 因为是保存的是单条记录,因此至多返回一个键
  345. var = rs.getInt(1); // 获得值并赋值给 var 变量
  346. }
  347. } else {
  348. var = count; // 如果不需要获得,则将受SQL影像的记录数赋值给 var 变量
  349. }
  350. commit(c);
  351. } catch (SQLException e) {
  352. System.out.println("数据保存失败: " + e.getMessage());
  353. rollback(c);
  354. }
  355. } else { // 说明没有参数
  356. Statement st = statement();
  357. Connection c = null;
  358. try {
  359. c = st.getConnection(); // 从 Statement 对象中获得 它对应的连接对象
  360. } catch (SQLException e) {
  361. e.printStackTrace();
  362. }
  363. // 执行 DDL 或 DML 语句,并返回执行结果
  364. try {
  365. int count = st.executeUpdate(SQL);
  366. if (autoGeneratedKeys) { // 如果企望获得数据库产生的键
  367. ResultSet rs = st.getGeneratedKeys(); // 获得数据库产生的键集
  368. if (rs.next()) { // 因为是保存的是单条记录,因此至多返回一个键
  369. var = rs.getInt(1); // 获得值并赋值给 var 变量
  370. }
  371. } else {
  372. var = count; // 如果不需要获得,则将受SQL影像的记录数赋值给 var 变量
  373. }
  374. commit(c); // 提交事务
  375. } catch (SQLException e) {
  376. System.out.println("数据保存失败: " + e.getMessage());
  377. rollback(c); // 回滚事务
  378. }
  379. }
  380. return var;
  381. }
  382.  
  383. /** 提交事务 */
  384. private static void commit(Connection c) {
  385. if (c != null && !autoCommit) {
  386. try {
  387. c.commit();
  388. } catch (SQLException e) {
  389. e.printStackTrace();
  390. }
  391. }
  392. }
  393.  
  394. /** 回滚事务 */
  395. private static void rollback(Connection c) {
  396. if (c != null && !autoCommit) {
  397. try {
  398. c.rollback();
  399. } catch (SQLException e) {
  400. e.printStackTrace();
  401. }
  402. }
  403. }
  404.  
  405. /**
  406. * 释放资源
  407. **/
  408. public static void release(Object cloaseable) {
  409.  
  410. if (cloaseable != null) {
  411.  
  412. if (cloaseable instanceof ResultSet) {
  413. ResultSet rs = (ResultSet) cloaseable;
  414. try {
  415. rs.close();
  416. } catch (SQLException e) {
  417. e.printStackTrace();
  418. }
  419. }
  420.  
  421. if (cloaseable instanceof Statement) {
  422. Statement st = (Statement) cloaseable;
  423. try {
  424. st.close();
  425. } catch (SQLException e) {
  426. e.printStackTrace();
  427. }
  428. }
  429.  
  430. if (cloaseable instanceof Connection) {
  431. Connection c = (Connection) cloaseable;
  432. try {
  433. c.close();
  434. } catch (SQLException e) {
  435. e.printStackTrace();
  436. }
  437. }
  438.  
  439. }
  440.  
  441. }
  442.  
  443. }

  

4、测试JDBC

1)增加测试

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

  输出结果

2) 查询测试

  1. public static void query() throws SQLException {
  2. String selectSql = "select * from User where userName=? and userAge= ? ";
  3. ArrayList<Object> arrayList = new ArrayList<>(); //非线程安全
  4. arrayList.add("Nick");
  5. arrayList.add(30);
  6. ResultSet res = JDBCUtils.query(selectSql, arrayList);
  7. while (res.next()) {
  8. String userName = res.getString("userName");
  9. System.out.println("userName:" +userName);
  10.  
  11. }
  12.  
  13. }

  输出结果:

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

1、创建自定义插入注解

  1. package com.example.annotation;
  2.  
  3. import java.lang.annotation.Documented;
  4. import java.lang.annotation.ElementType;
  5. import java.lang.annotation.Retention;
  6. import java.lang.annotation.RetentionPolicy;
  7. import java.lang.annotation.Target;
  8.  
  9. /**
  10. * 自定义插入注解
  11. *
  12. */
  13. @Documented
  14. @Retention(RetentionPolicy.RUNTIME)
  15. @Target(ElementType.METHOD)
  16. public @interface ExtInsert {
  17. String value();
  18. }

  

2、自定义参数注解

  1. package com.example.annotation;
  2.  
  3. import java.lang.annotation.Documented;
  4. import java.lang.annotation.ElementType;
  5. import java.lang.annotation.Retention;
  6. import java.lang.annotation.RetentionPolicy;
  7. import java.lang.annotation.Target;
  8.  
  9. /**
  10. * 自定义参数注解
  11. *
  12. */
  13. @Documented
  14. @Retention(RetentionPolicy.RUNTIME)
  15. @Target(ElementType.PARAMETER)
  16. public @interface ExtParam {
  17. String value();
  18. }

  

3、创建接口UserMapper

  1. package com.example.mapper;
  2.  
  3. import com.example.annotation.ExtInsert;
  4. import com.example.annotation.ExtParam;
  5.  
  6. //@Mapper
  7. public interface UserMapper {
  8.  
  9. @ExtInsert("insert into user(userName,userAge) values(#{userName},#{userAge})")
  10. public int insertUser(@ExtParam("userName") String userName, @ExtParam("userAge")Integer userAge);
  11. }

 

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

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

  1. package com.example.mybatis.aop;
  2.  
  3. import java.lang.reflect.InvocationHandler;
  4. import java.lang.reflect.Method;
  5.  
  6. /**
  7. * 使用反射动态拦截 接口方法
  8. *
  9. */
  10. public class MyInvocationHandlerMybatis implements InvocationHandler {
  11.  
  12. private Object object;
  13.  
  14. public MyInvocationHandlerMybatis(Object object) {
  15. this.object = object;
  16. }
  17.  
  18. //proxy,代理对象 。 method: 拦截方法 。 args 方法上的参数值
  19. @Override
  20. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  21. System.out.println("使用动态代理技术拦截接口方法开始");
  22. return 1;
  23. }
  24.  
  25. }

  

2、创建SqlSession类

  1. package com.example.sql;
  2.  
  3. import java.lang.reflect.Proxy;
  4.  
  5. import com.example.mybatis.aop.MyInvocationHandlerMybatis;
  6.  
  7. public class SqlSession {
  8.  
  9. //加载Mapper接口
  10. @SuppressWarnings("unchecked")
  11. public static <T> T getMapper(Class<T> classz)
  12. throws IllegalArgumentException, InstantiationException, IllegalAccessException {
  13. return (T) Proxy.newProxyInstance(classz.getClassLoader(),
  14. new Class[]{classz}, new MyInvocationHandlerMybatis(classz));
  15. }
  16. }

  

3、测试

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

  返回结果

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

1、ExtSelect 注解

  1. /**
  2. * 自定义查询注解
  3. *
  4. */
  5. @Documented
  6. @Retention(RetentionPolicy.RUNTIME)
  7. @Target(ElementType.METHOD)
  8. public @interface ExtSelect {
  9. String value();
  10. }

  

2、ExtInsert 注解

  1. /**
  2. * 自定义插入注解
  3. *
  4. */
  5. @Documented
  6. @Retention(RetentionPolicy.RUNTIME)
  7. @Target(ElementType.METHOD)
  8. public @interface ExtInsert {
  9. String value();
  10. }

  

3、User 类

  1. package com.example.entity;
  2.  
  3. public class User {
  4. private String userName;
  5. private Integer userAge;
  6.  
  7. public String getUserName() {
  8. return userName;
  9. }
  10.  
  11. public void setUserName(String userName) {
  12. this.userName = userName;
  13. }
  14.  
  15. public Integer getUserAge() {
  16. return userAge;
  17. }
  18.  
  19. public void setUserAge(Integer userAge) {
  20. this.userAge = userAge;
  21. }
  22.  
  23. @Override
  24. public String toString() {
  25. return "User [userName=" + userName + ", userAge=" + userAge + "]";
  26. }
  27.  
  28. }

 

4、UserMapper 接口

  1. public interface UserMapper {
  2.  
  3. @ExtInsert("insert into user(userName,userAge) values(#{userName},#{userAge})")
  4. public int insertUser(@ExtParam("userName") String userName, @ExtParam("userAge")Integer userAge);
  5.  
  6. @ExtSelect("select * from User where userName=#{userName} and userAge=#{userAge} ")
  7. User selectUser(@ExtParam("userName") String name, @ExtParam("userAge") Integer userAge);
  8.  
  9. }

  

5、 SQL拼接

  1. package com.exmaple.orm.util;
  2.  
  3. import java.util.ArrayList;
  4. import java.util.List;
  5.  
  6. /**
  7. * SQL拼接<br>
  8. */
  9. public class SQLUtils {
  10.  
  11. /**
  12. * 获取Insert语句后面values 参数信息<br>
  13. */
  14. public static String[] sqlInsertParameter(String sql) {
  15. int startIndex = sql.indexOf("values");
  16. int endIndex = sql.length();
  17. String substring = sql.substring(startIndex + 6, endIndex).replace("(", "").replace(")", "").replace("#{", "")
  18. .replace("}", "");
  19. String[] split = substring.split(",");
  20. return split;
  21. }
  22.  
  23. /**
  24. *
  25. * 获取select 后面where语句
  26. */
  27. public static List<String> sqlSelectParameter(String sql) {
  28. int startIndex = sql.indexOf("where");
  29. int endIndex = sql.length();
  30. String substring = sql.substring(startIndex + 5, endIndex);
  31. String[] split = substring.split("and");
  32. List<String> listArr = new ArrayList<>();
  33. for (String string : split) {
  34. String[] sp2 = string.split("=");
  35. listArr.add(sp2[0].trim());
  36. }
  37. return listArr;
  38. }
  39.  
  40. /**
  41. * 将SQL语句的参数替换变为?<br>
  42. *
  43. */
  44. public static String parameQuestion(String sql, String[] parameterName) {
  45. for (int i = 0; i < parameterName.length; i++) {
  46. String string = parameterName[i];
  47. sql = sql.replace("#{" + string + "}", "?");
  48. }
  49. return sql;
  50. }
  51.  
  52. public static String parameQuestion(String sql, List<String> parameterName) {
  53. for (int i = 0; i < parameterName.size(); i++) {
  54. String string = parameterName.get(i);
  55. sql = sql.replace("#{" + string + "}", "?");
  56. }
  57. return sql;
  58. }
  59.  
  60. public static void main(String[] args) {
  61. String sql = "insert into user(userName,userAge) values(#{userName},#{userAge})";
  62. String[] sqlParameter = sqlInsertParameter(sql);
  63. for (String string : sqlParameter) {
  64. System.out.println(string);
  65. }
  66. /*List<String> sqlSelectParameter = SQLUtils
  67. .sqlSelectParameter("select * from User where userName=#{userName} and userAge=#{userAge} ");
  68. for (String string : sqlSelectParameter) {
  69. System.out.println(string);
  70. }*/
  71. }
  72. }

 

6、完整的MyInvocationHandlerMybatis 类

  1. package com.example.mybatis.aop;
  2.  
  3. import java.lang.reflect.Field;
  4. import java.lang.reflect.InvocationHandler;
  5. import java.lang.reflect.Method;
  6. import java.lang.reflect.Parameter;
  7. import java.sql.ResultSet;
  8. import java.util.ArrayList;
  9. import java.util.List;
  10. import java.util.concurrent.ConcurrentHashMap;
  11. import com.example.annotation.ExtInsert;
  12. import com.example.annotation.ExtParam;
  13. import com.example.annotation.ExtSelect;
  14. import com.exmaple.orm.util.JDBCUtils;
  15. import com.exmaple.orm.util.SQLUtils;
  16.  
  17. /**
  18. * 使用反射动态拦截 接口方法
  19. *
  20. */
  21. public class MyInvocationHandlerMybatis implements InvocationHandler {
  22.  
  23. private Object object;
  24.  
  25. public MyInvocationHandlerMybatis(Object object) {
  26. this.object = object;
  27. }
  28.  
  29. //proxy,代理对象 。 method: 拦截方法 。 args 方法上的参数值
  30. @Override
  31. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  32. System.out.println("使用动态代理技术拦截接口方法开始");
  33. //ExtInsert的封装过程
  34. //1、判断方法上是否存在@ExtInsert注解
  35. ExtInsert extInsert = method.getDeclaredAnnotation(ExtInsert.class);
  36. if(extInsert != null){
  37. return exeInsertSQL(extInsert, proxy, method, args);
  38. }
  39. //二、查询思路
  40. //1判断方法上是否存在@ExtSelect注解
  41. ExtSelect extSelect = method.getDeclaredAnnotation(ExtSelect.class);
  42. if(extSelect != null){
  43. // 2、获取SQL语句,获取注解的Select语句
  44. String selectSQL = extSelect.value();
  45. //3、获取方法的参数和SQL参数进行匹配
  46. ConcurrentHashMap<Object, Object> paramMap = paramsMap(proxy, method, args);
  47. //4、替换参数变? 传递方式
  48. List<String> sqlSelectParams = SQLUtils.sqlSelectParameter(selectSQL);
  49. //5.传递参数
  50. List<Object> sqlParams = new ArrayList<>();
  51. for(String parameterName : sqlSelectParams){
  52. Object parameterValue = paramMap.get(parameterName);
  53. sqlParams.add(parameterValue);
  54. }
  55. // 6.将sql语句换成?
  56. String newSql = SQLUtils.parameQuestion(selectSQL, sqlSelectParams);
  57.  
  58. System.out.println("newSQL:" + newSql + ", sqlParams:" +sqlParams.toString() );
  59. //6、调用jdbc底层代码执行语句;
  60. ResultSet res = JDBCUtils.query(newSql, sqlParams);
  61.  
  62. //7、使用反射机制实例对象### 获取方法返回的类型,进行实例化
  63. //思路:
  64. //1).使用反射机制获取方法类型,
  65. //2). 判断是否有结果集,在进行初始化
  66. //3).使用反射机制给对象复制
  67. while(!res.next()){
  68. return null;
  69. //System.out.println(query.getObject(1));
  70. }
  71.  
  72. //下标往上移动一位
  73. res.previous();
  74.  
  75. //1).使用反射机制获取方法类型,
  76. Class<?> retrunType = method.getReturnType();
  77. Object object = retrunType.newInstance();
  78. while(res.next()){
  79. //方式一
  80. /*for(String parameterName: sqlSelectParams){
  81. Object resultValue = res.getObject(parameterName);
  82. //使用Java的反射赋值
  83. Field field = retrunType.getDeclaredField(parameterName);
  84. //私有方法允许访问
  85. field.setAccessible(true);
  86. field.set(object, resultValue);
  87. }*/
  88. //方式二 获取当前所有的属性
  89. Field[] declaredFields = retrunType.getDeclaredFields();
  90. for(Field field : declaredFields){
  91. String fildName = field.getName();
  92. Object fildVale = res.getObject(fildName);
  93. field.setAccessible(true);
  94. field.set(object, fildVale);
  95. }
  96. }
  97. return object;
  98.  
  99. }
  100.  
  101. return null;
  102. }
  103.  
  104. private ConcurrentHashMap<Object, Object> paramsMap(Object proxy, Method method, Object[] args){
  105. ConcurrentHashMap<Object,Object> paramMap = new ConcurrentHashMap<>();
  106. //获取方法上的参数
  107. Parameter[] parameters = method.getParameters();
  108.  
  109. for(int i = 0; i <parameters.length; i++){
  110. //参数名称
  111. Parameter parameter = parameters[i];
  112. ExtParam extParam = parameter.getDeclaredAnnotation(ExtParam.class);
  113. if(extParam != null){
  114. //参数名称
  115. String paramName = extParam.value();
  116. Object paramValue = args[i];
  117. //System.out.println("paramName:" + paramName + " paramValue:" + paramValue);
  118. paramMap.put(paramName, paramValue);
  119. }
  120.  
  121. }
  122. return paramMap;
  123. }
  124.  
  125. private List<Object> sqlInsertParameter(String[] sqlInsertParameter , ConcurrentHashMap<Object, Object> paramMap){
  126. List<Object> sqlParams = new ArrayList<>();
  127.  
  128. for(String paramName: sqlInsertParameter){
  129. Object paramValue = paramMap.get(paramName);
  130. sqlParams.add(paramValue);
  131. }
  132. return sqlParams;
  133. }
  134.  
  135. public Object exeInsertSQL(ExtInsert extInsert,Object proxy, Method method, Object[] args) {
  136. //方法上存在@ExtInsert,获取他的SQL语句
  137. //2、获取SQL语句,获取注解的Insert语句
  138. String insertSql = extInsert.value();
  139. //System.out.println("insertSql:" + insertSql);
  140. //3、获取方法的参数和SQL参数进行匹配
  141. //定义一个Map集合,KEY为@ExtParamValue, Value 结果为参数值
  142. ConcurrentHashMap<Object, Object> paramMap = paramsMap(proxy, method, args);
  143.  
  144. //存放sql执行参数--参数绑定过程
  145. String[] sqlInsertParameter = SQLUtils.sqlInsertParameter(insertSql);
  146. List<Object> sqlParams = sqlInsertParameter(sqlInsertParameter, paramMap);
  147.  
  148. //4、替换参数变?
  149. String newSQL = SQLUtils.parameQuestion(insertSql, sqlInsertParameter);
  150. System.out.println("newSQL:" + newSQL + ",sqlParams:" + sqlParams.toString());
  151. //5、调用jdbc底层代码执行语句;
  152. return JDBCUtils.insert(newSQL, false, sqlParams);
  153. }
  154.  
  155. }

  

 7、测试

1)查询测试

  1. public class Test003 {
  2. public static void main(String[] args) throws IllegalArgumentException, InstantiationException, IllegalAccessException {
  3. //使用动态代理技术虚拟调用方法
  4. UserMapper userMapper = SqlSession.getMapper(UserMapper.class);
  5.  
  6. User user = userMapper.selectUser("Nick", 30);
  7. System.out.println("user:" + user.toString());
  8. }
  9. }

  返回结果

2)插入结果测试

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

  返回结果

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

  1. 手写开源ORM框架介绍

    手写开源ORM框架介绍 简介 前段时间利用空闲时间,参照mybatis的基本思路手写了一个ORM框架.一直没有时间去补充相应的文档,现在正好抽时间去整理下.通过思路历程和代码注释,一方面重温下知识,另 ...

  2. 要想精通Mybatis?从手写Mybatis框架开始吧!

    1.Mybatis组成 动态SQL Config配置 Mapper配置 2.核心源码分析 Configuration源码解析 SqlSessionFactory源码解析 SqlSession源码解析 ...

  3. 闭关修炼180天--手写持久层框架(mybatis简易版)

    闭关修炼180天--手写持久层框架(mybatis简易版) 抛砖引玉 首先先看一段传统的JDBC编码的代码实现: //传统的JDBC实现 public static void main(String[ ...

  4. 手写mybatis框架笔记

    MyBatis 手写MyBatis流程 架构流程图 封装数据 封装到Configuration中 1.封装全局配置文件,包含数据库连接信息和mappers信息 2.封装*mapper.xml映射文件 ...

  5. 手写Spring事务框架

    Spring事务基于AOP环绕通知和异常通知 编程事务 声明事务 Spring事务底层使用编程事务+AOP进行包装的   = 声明事务 AOP应用场景:  事务 权限 参数验证 什么是AOP技术 AO ...

  6. 手写MyBatis流程

    MyBatis 手写MyBatis流程 架构流程图 封装数据 封装到Configuration中 1.封装全局配置文件,包含数据库连接信息和mappers信息 2.封装*mapper.xml映射文件 ...

  7. 剖析手写Vue,你也可以手写一个MVVM框架

    剖析手写Vue,你也可以手写一个MVVM框架# 邮箱:563995050@qq.com github: https://github.com/xiaoqiuxiong 作者:肖秋雄(eddy) 温馨提 ...

  8. 手写spring事务框架-蚂蚁课堂

    1.视频参加C:\Users\Administrator\Desktop\蚂蚁3期\[www.zxit8.com] 0017-(每特教育&每特学院&蚂蚁课堂)-3期-源码分析-手写Sp ...

  9. 手写mybatis框架-增加缓存&事务功能

    前言 在学习mybatis源码之余,自己完成了一个简单的ORM框架.已完成基本SQL的执行和对象关系映射.本周在此基础上,又加入了缓存和事务功能.所有代码都没有copy,如果也对此感兴趣,请赏个Sta ...

随机推荐

  1. 【转】如何使用jupyter编写数学公式(译)

    [1.如何使用jupyter编写数学公式(译)][1] [1]: https://www.jianshu.com/p/93ccc63e5a1b

  2. centos 7 安装webmin

    wget http://prdownloads.sourceforge.net/webadmin/webmin-1.930-1.noarch.rpm yum -y install perl perl- ...

  3. sqlserver数据库的启动

    sqlserver实例几种启动的方法: (1)在WINDOWS服务控制台里手动启动,或者自动启动(默认) 第二种方式是SQLSERVER本身自已提供的启动方式,可以手动启动 (3)在SQLSERVER ...

  4. spice在桌面虚拟化中的应用系列之二(Linux平台spice客户端的编译安装,支持USB映射)

    1.系统环境 1.1 测试环境 centos6.4最小化安装(centos6.x桌面版也适用) 使用yum源为163源加EPEL源 1.2 spice客户端介绍 spice作为远程连接工具,可以支持远 ...

  5. 从零开始部署Django生产环境(适用:《跟老齐学Python Django实战》)

    <跟老齐学Python Django实战>作为市面上少有的Django通俗实战书籍,给了我学习Django很大的帮助.作为一名新入门的菜鸟,全书我重复练习了至少三遍,每次都有新的收获. 前 ...

  6. zabbix-web切换为nginx及https

    目录 zabbix-web切换为nginx及https 1.背景和环境 2.安装nginx 2.1.编译参数 2.2.修改配置文件并配置https 2.3.配置nginx为系统服务 3.安装php 3 ...

  7. 简单介绍 Java 中的注解 (Annotation)

    1. 例子 首先来看一个例子: @Override public String toString() { return "xxxxx"; } 这里用了 @Override, 目的是 ...

  8. Kotlin属性揭秘与延迟初始化特性

    在上一次https://www.cnblogs.com/webor2006/p/11210181.html学习了Kotlin的伴生对象,这次来学习属性相关的东东. 属性揭秘: 先声明一个属性: 没啥可 ...

  9. CCS设置第一个li的元素与其他li样式不同

    <div class="ly-content-list"> <ul> <li> <div class="title"& ...

  10. HTTPS——https下的静态资源中如何使用http的地址

    前言 今天在改博皮的时候,将一些图片上传到七牛,但是引入的时候出问题了,因为七牛cnd设置的不是https域名,所以加载资源的时候导致自动转为https请求. 步骤 错误的写法 background: ...