手写MyBatis ORM框架

末蓝、 2023-07-21 13:28 160阅读 0赞

手写MyBatis ORM框架

  • 目标:
  • 实现:
      • 思路:
    • 一、前期准备
      • 1、加入依赖
      • 2、自定义插入注解
      • 3、自定义删除注解
      • 4、自定义更新注解
      • 5、自定义查询注解
      • 6、自定义参数注解
      • 7、定义和数据库对应的实体类
      • 8、定义对数据库的操作接口
      • 9、自定义动态代理方法
      • 10、自定义动态代理
      • 11、自定义测试类
    • 二、自定义代理完善
      • 1、JDBC工具类
      • 2、SQL语句处理类
      • 3、完成拦截类
  • 总结
    • 概括
    • 流程图
    • 重点及易错点
      • 1、返回值
      • 2、res.next()

目标:

  实现MyBatis的基本功能。主要包括在方法中加入以下自定义注解、则获得相应的功能:

  • ChenInsert 实现添加数据的功能
  • ChenQuery 实现查询数据的功能,如果有多条数据只返回第一条数据
  • ChenQueryList 实现查询数据的功能,如果有多条数据返回全部数据
  • ChenDelete 实现删除数据的功能
  • ChenUpdate 实现更新数据的功能,返回更新后的对象
  • ChenUpdateInt 实现更新数据的功能,返回更新的记录数
  • ChenParam 定义参数注解,获取变量名称

实现:

思路:

  自定义SqlSession,完成动态代理拦截接口方法的功能。大致步骤如下:

  • 首先判断方法上是否存在自定义注解。
  • 如果没有自定义注解则直接结束,否则获取注解上的SQL语句。
  • 然后获取方法上的参数,将参数名称和参数值绑定在一起。
  • 然后将mybatis语法的SQL语句替换成jdbc的SQL语句。
  • 接着调用jdbc代码底层执行sql语句。
  • 如果是基本类型则直接返回,如果是引用对象则使用反射机制实例对象,获取方法返回的类型,进行实例化返回给调用者。

一、前期准备

  根据测试先行的敏捷方法,我们先搭好业务应用场景、实现自定义注解,并用自定义注解模拟对数据库的增删改查。先实现空方法,再对具体需求进行开发。

1、加入依赖

  本项目MyBatisORM是对数据库的操作,所以需要引入MySQL驱动包、数据库连接池等jar包。源码如下:

pom.xml

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

2、自定义插入注解

  定义ChenInsert 类,使得该注解的作用范围在方法上 。同时保证注解会在class字节码文件中存在,在运行时可以通过反射获取到。设置value属性值,为后面获取SQL语句做准备。源码如下:

ChenInsert .java

  1. package com.njust.mybatisorm.orm.annotation;
  2. /** * @author Chen * @version 1.0 * @date 2020/4/2 8:37 * @description: */
  3. import java.lang.annotation.*;
  4. /** * 自定义插入注解 <br> */
  5. @Documented
  6. @Retention(RetentionPolicy.RUNTIME)
  7. @Target(ElementType.METHOD)
  8. public @interface ChenInsert {
  9. String value();
  10. }

3、自定义删除注解

  定义ChenDelete 类,使得该注解的作用范围在方法上 。同时保证注解会在class字节码文件中存在,在运行时可以通过反射获取到。设置value属性值,为后面获取SQL语句做准备。源码如下:

ChenDelete .java

  1. package com.njust.mybatisorm.orm.annotation;
  2. import java.lang.annotation.*;
  3. /** * 自定义删除注解 <br> */
  4. @Documented
  5. @Retention(RetentionPolicy.RUNTIME)
  6. @Target(ElementType.METHOD)
  7. public @interface ChenDelete {
  8. String value();
  9. }

4、自定义更新注解

  定义ChenUpdate 类,使得该注解的作用范围在方法上 。同时保证注解会在class字节码文件中存在,在运行时可以通过反射获取到。设置value属性值,为后面获取SQL语句做准备。注意:该注解使得注解方法更新的返回值是更新后的实体对象。源码如下:

ChenUpdate .java

  1. package com.njust.mybatisorm.orm.annotation;
  2. import java.lang.annotation.*;
  3. /** * 自定义更新注解 <br> */
  4. @Documented
  5. @Retention(RetentionPolicy.RUNTIME)
  6. @Target(ElementType.METHOD)
  7. public @interface ChenUpdate {
  8. String value();
  9. }

  定义ChenUpdateInt 类,使得该注解的作用范围在方法上 。同时保证注解会在class字节码文件中存在,在运行时可以通过反射获取到。设置value属性值,为后面获取SQL语句做准备。注意:该注解使得注解方法更新的返回值是成功更新的记录数,注意和上面的注解区别。源码如下:

ChenUpdateInt.java

  1. package com.njust.mybatisorm.orm.annotation;
  2. import java.lang.annotation.*;
  3. /** * 自定义更新注解 <br> * 返回更新数量 */
  4. @Documented
  5. @Retention(RetentionPolicy.RUNTIME)
  6. @Target(ElementType.METHOD)
  7. public @interface ChenUpdateInt {
  8. String value();
  9. }

5、自定义查询注解

  定义ChenQuery 类,使得该注解的作用范围在方法上 。同时保证注解会在class字节码文件中存在,在运行时可以通过反射获取到。设置value属性值,为后面获取SQL语句做准备。注意:该注解使得注解方法,如果有查询结果则返回查询结果的第一个值,注意和下面的注解区别。源码如下:

ChenQuery .java

  1. package com.njust.mybatisorm.orm.annotation;
  2. import java.lang.annotation.*;
  3. /** * 自定义查询注解 <br> */
  4. @Documented
  5. @Retention(RetentionPolicy.RUNTIME)
  6. @Target(ElementType.METHOD)
  7. public @interface ChenQuery {
  8. String value();
  9. }

  定义ChenQueryList 类,使得该注解的作用范围在方法上 。同时保证注解会在class字节码文件中存在,在运行时可以通过反射获取到。设置value属性值,为后面获取SQL语句做准备。注意:该注解使得注解方法,如果有查询结果则返回全部查询结果,注意和上面的注解区别。源码如下:

ChenQueryList .java

  1. package com.njust.mybatisorm.orm.annotation;
  2. import java.lang.annotation.*;
  3. /** * 自定义查询注解 <br> * 返回多个参数 */
  4. @Documented
  5. @Retention(RetentionPolicy.RUNTIME)
  6. @Target(ElementType.METHOD)
  7. public @interface ChenQueryList {
  8. String value();
  9. }

6、自定义参数注解

  定义ChenParam 类,使得该注解的作用范围在参数上 。同时保证注解会在class字节码文件中存在,在运行时可以通过反射获取到。设置value属性值,为后面获取具体参数值做准备。源码如下:

ChenParam .java

  1. package com.njust.mybatisorm.orm.annotation;
  2. import java.lang.annotation.*;
  3. /** * 自定义参数注解<br> */
  4. @Documented
  5. @Retention(RetentionPolicy.RUNTIME)
  6. @Target(ElementType.PARAMETER)
  7. public @interface ChenParam {
  8. String value();
  9. }

7、定义和数据库对应的实体类

  定义User 类,和数据库属性对应,包括id,用户名,年龄等基本属性。提供get()set()方法实现对对象的操作,tostring()方法可以不要,但是为了便于测试,我们此处将其加入进来。源码如下:

User .java

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

8、定义对数据库的操作接口

  定义UserMapper 类,模拟mybatis的操作方法,对数据库进行增删改查。源码如下:

UserMapper .java

  1. package com.njust.mybatisorm.mapper;
  2. import com.njust.mybatisorm.entity.User;
  3. import com.njust.mybatisorm.orm.annotation.*;
  4. import java.util.List;
  5. public interface UserMapper {
  6. @ChenInsert("insert into user(userName,userAge) values(#{userName1},#{userAge})")
  7. public int insertUser(@ChenParam("userName1") String userName, @ChenParam("userAge") Integer userAge);
  8. @ChenDelete("DELETE from user WHERE id=#{id}")
  9. public int deleteUser(@ChenParam("id") Integer id);
  10. @ChenQuery("select * from User where userName=#{userName} and userAge=#{userAge} ")
  11. User selectUser(@ChenParam("userName") String name, @ChenParam("userAge") Integer userAge);
  12. @ChenQueryList("select * from User where userName=#{userName} and userAge=#{userAge} ")
  13. List<User> selectUserList(@ChenParam("userName") String name, @ChenParam("userAge") Integer userAge);
  14. @ChenUpdate("UPDATE user set userName=#{userName} ,userAge=#{userAge} WHERE id=#{id} ")
  15. User updateUser(@ChenParam("id") Integer id, @ChenParam("userName") String name, @ChenParam("userAge") Integer userAge);
  16. @ChenUpdateInt("UPDATE user set userName=#{userName} ,userAge=#{userAge} WHERE id=#{id} ")
  17. int updateUserInt(@ChenParam("id") Integer id, @ChenParam("userName") String name, @ChenParam("userAge") Integer userAge);
  18. }

9、自定义动态代理方法

  定义SqlSession 类,拦截代理接口,使用自定义ChenInvocationHandlerMybatis拦截器对方法实际处理。源码如下:

SqlSession .java

  1. package com.njust.mybatisorm.sql;
  2. import com.njust.mybatisorm.orm.aop.ChenInvocationHandlerMybatis;
  3. import java.lang.reflect.Proxy;
  4. public class SqlSession {
  5. // 加载Mapper接口
  6. public static <T> T getMapper(Class classz) {
  7. //loader: 用哪个类加载器去加载代理对象
  8. //interfaces:动态代理类需要实现的接口
  9. //h:动态代理方法在执行时,会调用h里面的invoke方法去执行
  10. return (T) Proxy.newProxyInstance(classz.getClassLoader(), new Class[]{ classz},
  11. new ChenInvocationHandlerMybatis(classz));
  12. }
  13. }

10、自定义动态代理

  定义ChenInvocationHandlerMybatiss 类,继承InvocationHandler类,实现其代理拦截方法,这里我们要检测业务环境是否搭建成功,只是简单的拦截并输出。测试成功之后再实现具体的业务逻辑。源码如下:

ChenInvocationHandlerMybatiss .java

  1. package com.njust.mybatisorm.orm.aop;
  2. import com.njust.mybatisorm.orm.annotation.*;
  3. import com.njust.mybatisorm.utils.JDBCUtils;
  4. import com.njust.mybatisorm.utils.SQLUtils;
  5. import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl;
  6. import java.lang.reflect.*;
  7. import java.sql.ResultSet;
  8. import java.sql.SQLException;
  9. import java.util.ArrayList;
  10. import java.util.List;
  11. import java.util.concurrent.ConcurrentHashMap;
  12. import static com.njust.mybatisorm.utils.SQLUtils.getTableName;
  13. /** * 使用反射动态代理技术 拦截接口防范 */
  14. public class ChenInvocationHandlerMybatiss implements InvocationHandler {
  15. private Object object;
  16. public ChenInvocationHandlerMybatiss(Object object) {
  17. this.object = object;
  18. }
  19. // proxy 代理对象 method拦截方法 args方法上的参数值
  20. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  21. System.out.println("使用动态代理技术拦截接口方法开始");
  22. // 使用白话问翻译,@ChenInsert封装过程
  23. // 1. 判断方法上是否存在@ChenInsert
  24. ChenInsert chenInsert = method.getDeclaredAnnotation(ChenInsert.class);
  25. if (chenInsert != null) {
  26. System.out.println("拦截到ChenInsert注解");
  27. }
  28. // 1. 判断方法上是否存 在删除注解
  29. ChenDelete chenDelete = method.getDeclaredAnnotation(ChenDelete.class);
  30. if (chenDelete != null) {
  31. System.out.println("拦截到ChenDelete注解");
  32. }
  33. // 2.查询的思路
  34. // 1. 判断方法上是否存 在查询注解
  35. ChenQuery chenQuery = method.getDeclaredAnnotation(ChenQuery.class);
  36. if (chenQuery != null) {
  37. System.out.println("拦截到ChenQuery注解");
  38. }
  39. // 1. 判断方法上是否存 在查询注解
  40. ChenQueryList chenQueryList = method.getDeclaredAnnotation(ChenQueryList.class);
  41. if (chenQueryList != null) {
  42. System.out.println("拦截到ChenQueryList注解");
  43. }
  44. /** * 更新 */
  45. ChenUpdate chenUpdate = method.getDeclaredAnnotation(ChenUpdate.class);
  46. if (chenUpdate != null) {
  47. System.out.println("拦截到ChenUpdate注解");
  48. }
  49. /** * 更新 返回更新数量 */
  50. ChenUpdateInt chenUpdateInt = method.getDeclaredAnnotation(ChenUpdateInt.class);
  51. if (chenUpdateInt != null) {
  52. System.out.println("拦截到ChenUpdateInt注解");
  53. }
  54. return new Object();
  55. }
  56. }

11、自定义测试类

  定义TestOK 类,类似MyBatis般操作数据库,这里节约时间,我只测试了selectUserList()方法,其他方法类似。源码如下:

TestOK .java

  1. package com.njust.mybatisorm;
  2. import com.njust.mybatisorm.entity.User;
  3. import com.njust.mybatisorm.mapper.UserMapper;
  4. import com.njust.mybatisorm.sql.SqlSession;
  5. import org.junit.Test;
  6. import java.util.List;
  7. /** * @author Chen * @version 1.0 * @date 2020/4/2 14:49 * @description: */
  8. public class TestOK {
  9. @Test
  10. public void test7(){
  11. // 使用动态代理技术虚拟调用方法
  12. UserMapper userMapper = SqlSession.getMapper(UserMapper.class);
  13. List<User> users = userMapper.selectUserList("陈1", 20);
  14. System.out.println(users);
  15. }
  16. }

  控制台输出如下:

Console

  1. 使用动态代理技术拦截接口方法开始
  2. 拦截到ChenQueryList注解
  3. java.lang.ClassCastException: java.lang.Object cannot be cast to java.util.List
  4. at com.sun.proxy.$Proxy4.selectUserList(Unknown Source)
  5. at com.njust.mybatisorm.TestOK.test7(TestOK.java:22)
  6. at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  7. at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
  8. at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
  9. at java.lang.reflect.Method.invoke(Method.java:498)
  10. at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
  11. at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
  12. at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
  13. at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
  14. at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
  15. at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
  16. at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
  17. at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
  18. at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
  19. at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
  20. at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
  21. at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
  22. at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
  23. at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
  24. at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
  25. at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
  26. at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
  27. at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
  28. at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
  29. at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
  30. at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
  31. Process finished with exit code -1

  控制台成功输出我们的拦截信息。异常是因为我们还没有具体实现业务逻辑,这也是我们接下来要做的。至此,我们的业务环境已经搭建成功。

二、自定义代理完善

  完善实际对数据库的增删改查功能。

1、JDBC工具类

  定义JDBCUtils 类,封装JDBC对数据库的操作,便于我们在拦截类中直接调用。这里为了简单起见,直接将数据库相关信息写死,读者可以将其配置到配置文件中。源码如下:

JDBCUtils .java

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

2、SQL语句处理类

  定义SQLUtils 类,封装Mybatis语句到JDBC语句的转化。由于mybatis的底层是JDBC,所以会出现Mybatis语法和JDBC语法的转换,所以我们将这些公共方法封装起来,便于拦截类调用。源码如下:

SQLUtils .java

  1. package com.njust.mybatisorm.utils;
  2. import java.util.ArrayList;
  3. import java.util.List;
  4. /** * SQL拼接<br> */
  5. public class SQLUtils {
  6. /** * * 获取Insert语句后面values 参数信息<br> * @param sql * @return */
  7. public static String[] sqlInsertParameter(String sql) {
  8. int startIndex = sql.indexOf("values");
  9. int endIndex = sql.length();
  10. String substring = sql.substring(startIndex + 6, endIndex).replace("(", "").replace(")", "").replace("#{", "")
  11. .replace("}", "");
  12. String[] split = substring.split(",");
  13. return split;
  14. }
  15. /** * * 获取select 后面where语句 * @param sql * @return */
  16. public static List<String> sqlSelectParameter(String sql) {
  17. int startIndex = sql.indexOf("where");
  18. int endIndex = sql.length();
  19. String substring = sql.substring(startIndex + 5, endIndex);
  20. String[] split = substring.split("and");
  21. List<String> listArr = new ArrayList<>();
  22. for (String string : split) {
  23. String[] sp2 = string.split("=");
  24. listArr.add(sp2[0].trim());
  25. }
  26. return listArr;
  27. }
  28. public static String deal(StringBuilder sql) {
  29. String result = null;
  30. int start = sql.indexOf("#{");
  31. int end = sql.indexOf("}");
  32. result = sql.substring(start + 2, end);
  33. return result;
  34. }
  35. /** * 将SQL参数提取出来,并将最后生成的jdbc语句也放到list中 * @param sql * @return */
  36. public static List<String> dealList(String sql) {
  37. StringBuilder stringBuilder = new StringBuilder(sql);
  38. ArrayList<String> strings = new ArrayList<>();
  39. while (stringBuilder.indexOf("#{") != -1) {
  40. strings.add(deal(stringBuilder));
  41. stringBuilder = stringBuilder.replace(stringBuilder.indexOf("#{"),
  42. stringBuilder.indexOf("}") + 1, "?");
  43. }
  44. strings.add(String.valueOf(stringBuilder));
  45. return strings;
  46. }
  47. public static String getTableName(String sql) {
  48. sql = sql.toLowerCase();
  49. String name = null;
  50. int start = sql.indexOf("update");
  51. int end = sql.indexOf("set");
  52. name = sql.substring(start+6, end);
  53. name = name.trim();
  54. return name;
  55. }
  56. /** * 将SQL语句的参数替换变为?<br> * @param sql * @param parameterName * @return */
  57. public static String parameQuestion(String sql, String[] parameterName) {
  58. for (int i = 0; i < parameterName.length; i++) {
  59. String string = parameterName[i];
  60. sql = sql.replace("#{" + string + "}", "?");
  61. }
  62. return sql;
  63. }
  64. public static String parameQuestion(String sql, List<String> parameterName) {
  65. for (int i = 0; i < parameterName.size(); i++) {
  66. String string = parameterName.get(i);
  67. sql = sql.replace("#{" + string + "}", "?");
  68. }
  69. return sql;
  70. }
  71. }

3、完成拦截类

  修改ChenInvocationHandlerMybatis 类,完成具体的业务逻辑处理。各个注解的处理流程类似,基本流程是首先判断方法上是否存在自定义注解。如果没有自定义注解则直接结束,否则获取注解上的SQL语句。然后获取方法上的参数,将参数名称和参数值绑定在一起。
然后将mybatis语法的SQL语句替换成jdbcSQL语句。接着调用jdbc代码底层执行sql语句。如果是基本类型则直接返回,如果是引用对象则使用反射机制实例对象,获取方法返回的类型,进行实例化返回给调用者。源码如下:

ChenInvocationHandlerMybatis .java

  1. package com.njust.mybatisorm.orm.aop;
  2. import com.njust.mybatisorm.orm.annotation.*;
  3. import com.njust.mybatisorm.utils.JDBCUtils;
  4. import com.njust.mybatisorm.utils.SQLUtils;
  5. import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl;
  6. import java.lang.reflect.*;
  7. import java.sql.ResultSet;
  8. import java.sql.SQLException;
  9. import java.util.ArrayList;
  10. import java.util.List;
  11. import java.util.concurrent.ConcurrentHashMap;
  12. import static com.njust.mybatisorm.utils.SQLUtils.getTableName;
  13. /** * 使用反射动态代理技术 拦截接口防范 */
  14. public class ChenInvocationHandlerMybatis implements InvocationHandler {
  15. private Object object;
  16. public ChenInvocationHandlerMybatis(Object object) {
  17. this.object = object;
  18. }
  19. // proxy 代理对象 method拦截方法 args方法上的参数值
  20. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  21. System.out.println("使用动态代理技术拦截接口方法开始");
  22. // 使用白话问翻译,@ChenInsert封装过程
  23. // 1. 判断方法上是否存在@ChenInsert
  24. ChenInsert chenInsert = method.getDeclaredAnnotation(ChenInsert.class);
  25. if (chenInsert != null) {
  26. return chenInsert(chenInsert, proxy, method, args);
  27. }
  28. // 1. 判断方法上是否存 在删除注解
  29. ChenDelete chenDelete = method.getDeclaredAnnotation(ChenDelete.class);
  30. if (chenDelete != null) {
  31. return chenDelete(chenDelete, proxy, method, args);
  32. }
  33. // 2.查询的思路
  34. // 1. 判断方法上是否存 在查询注解
  35. ChenQuery chenQuery = method.getDeclaredAnnotation(ChenQuery.class);
  36. if (chenQuery != null) {
  37. return chenQuery(chenQuery, proxy, method, args);
  38. }
  39. // 1. 判断方法上是否存 在查询注解
  40. ChenQueryList chenQueryList = method.getDeclaredAnnotation(ChenQueryList.class);
  41. if (chenQueryList != null) {
  42. return chenQueryList(chenQueryList, proxy, method, args);
  43. }
  44. /** * 更新 */
  45. ChenUpdate chenUpdate = method.getDeclaredAnnotation(ChenUpdate.class);
  46. if (chenUpdate != null) {
  47. return chenUpdate(chenUpdate, proxy, method, args);
  48. }
  49. /** * 更新 返回更新数量 */
  50. ChenUpdateInt chenUpdateInt = method.getDeclaredAnnotation(ChenUpdateInt.class);
  51. if (chenUpdateInt != null) {
  52. return chenUpdateInt(chenUpdateInt, proxy, method, args);
  53. }
  54. return null;
  55. }
  56. private Object chenQuery(ChenQuery chenQuery, Object proxy, Method method, Object[] args)
  57. throws SQLException, IllegalAccessException, InstantiationException {
  58. // 2. 获取注解上查询的SQL语句
  59. String selectSQL = chenQuery.value();
  60. // 3. 获取方法上的参数,绑定在一起
  61. ConcurrentHashMap<Object, Object> paramsMap = paramsMap(proxy, method, args);
  62. // 4. 参数替换?传递方式
  63. List<String> sqlSelectParameter = SQLUtils.sqlSelectParameter(selectSQL);
  64. // 5.传递参数
  65. List<Object> sqlParams = new ArrayList<>();
  66. for (String parameterName : sqlSelectParameter) {
  67. Object parameterValue = paramsMap.get(parameterName);
  68. sqlParams.add(parameterValue);
  69. }
  70. // 6.将sql语句替换成?
  71. String newSql = SQLUtils.parameQuestion(selectSQL, sqlSelectParameter);
  72. System.out.println("newSQL:" + newSql + ",sqlParams:" + sqlParams.toString());
  73. return getObject(method, newSql, sqlParams);
  74. }
  75. private List<Object> chenQueryList(ChenQueryList chenQueryList, Object proxy, Method method, Object[] args)
  76. throws SQLException, IllegalAccessException, InstantiationException {
  77. // 2. 获取注解上查询的SQL语句
  78. String selectSQL = chenQueryList.value();
  79. // 3. 获取方法上的参数,绑定在一起
  80. ConcurrentHashMap<Object, Object> paramsMap = paramsMap(proxy, method, args);
  81. // 4. 参数替换?传递方式
  82. List<String> sqlSelectParameter = SQLUtils.sqlSelectParameter(selectSQL);
  83. // 5.传递参数
  84. List<Object> sqlParams = new ArrayList<>();
  85. for (String parameterName : sqlSelectParameter) {
  86. Object parameterValue = paramsMap.get(parameterName);
  87. sqlParams.add(parameterValue);
  88. }
  89. // 6.将sql语句替换成?
  90. String newSql = SQLUtils.parameQuestion(selectSQL, sqlSelectParameter);
  91. System.out.println("newSQL:" + newSql + ",sqlParams:" + sqlParams.toString());
  92. return getObjectList(method, newSql, sqlParams);
  93. }
  94. /** * 5.调用jdbc代码底层执行sql语句 * 6.使用反射机制实例对象### 获取方法返回的类型,进行实例化 * 思路: * 1.使用反射机制获取方法的类型 * 2.判断是否有结果集,如果有结果集,在进行初始化 * 3.使用反射机制,给对象赋值 * * @param method * @param newSql * @param sqlParams * @return * @throws SQLException * @throws IllegalAccessException * @throws InstantiationException */
  95. private Object getObject(Method method, String newSql, List<Object> sqlParams) throws SQLException, IllegalAccessException, InstantiationException {
  96. ResultSet res = JDBCUtils.query(newSql, sqlParams);
  97. // 判断是否存在值
  98. if (!res.next()) {
  99. return null;
  100. }
  101. // 下标往上移动移位
  102. res.previous();
  103. // 使用反射机制获取方法的类型
  104. Class<?> returnType = method.getReturnType();
  105. Object object = returnType.newInstance();
  106. if (res.next()) {
  107. // 获取当前所有的属性
  108. Field[] declaredFields = returnType.getDeclaredFields();
  109. for (Field field : declaredFields) {
  110. String fieldName = field.getName();
  111. Object fieldValue = res.getObject(fieldName);
  112. field.setAccessible(true);
  113. field.set(object, fieldValue);
  114. }
  115. }
  116. return object;
  117. }
  118. private List<Object> getObjectList(Method method, String newSql, List<Object> sqlParams)
  119. throws SQLException, IllegalAccessException, InstantiationException {
  120. ResultSet res = JDBCUtils.query(newSql, sqlParams);
  121. // 判断是否存在值
  122. if (!res.next()) {
  123. return null;
  124. }
  125. // 下标往上移动移位
  126. res.previous();
  127. // 使用反射机制获取方法的类型
  128. Class<?> returnType = method.getReturnType();
  129. // 获取泛型返回值
  130. Type genericReturnType = method.getGenericReturnType();
  131. // 获取list中泛型参数
  132. Type[] actualTypeArguments = ((ParameterizedTypeImpl) genericReturnType).getActualTypeArguments();
  133. ArrayList<Object> objects = new ArrayList<>();
  134. while (res.next()) {
  135. Object object = ((Class)actualTypeArguments[0]).newInstance();
  136. // 获取当前所有的属性
  137. Field[] declaredFields = ((Class)actualTypeArguments[0]).getDeclaredFields();
  138. for (Field field : declaredFields) {
  139. String fieldName = field.getName();
  140. Object fieldValue = res.getObject(fieldName);
  141. field.setAccessible(true);
  142. field.set(object, fieldValue);
  143. }
  144. objects.add(object);
  145. }
  146. return objects;
  147. }
  148. private Object chenInsert(ChenInsert chenInsert, Object proxy, Method method, Object[] args) {
  149. // 方法上存在@ChenInsert,获取他的SQL语句
  150. // 2. 获取SQL语句,获取注解Insert语句
  151. String insertSql = chenInsert.value();
  152. // System.out.println("insertSql:" + insertSql);
  153. // 3. 获取方法的参数和SQL参数进行匹配
  154. // 定一个一个Map集合 KEY为@ChenParamValue,Value 结果为参数值
  155. ConcurrentHashMap<Object, Object> paramsMap = paramsMap(proxy, method, args);
  156. // 存放sql执行的参数---参数绑定过程
  157. String[] sqlInsertParameter = SQLUtils.sqlInsertParameter(insertSql);
  158. List<Object> sqlParams = sqlParams(sqlInsertParameter, paramsMap);
  159. // 4. 根据参数替换参数变为?
  160. String newSQL = SQLUtils.parameQuestion(insertSql, sqlInsertParameter);
  161. System.out.println("newSQL:" + newSQL + ",sqlParams:" + sqlParams.toString());
  162. // 5. 调用jdbc底层代码执行语句
  163. return JDBCUtils.insert(newSQL, false, sqlParams);
  164. }
  165. private Object chenDelete(ChenDelete chenDelete, Object proxy, Method method, Object[] args) {
  166. // 方法上存在@ChenInsert,获取他的SQL语句
  167. // 2. 获取SQL语句,获取注解Insert语句
  168. String deleteSql = chenDelete.value();
  169. // System.out.println("insertSql:" + insertSql);
  170. // 3. 获取方法的参数和SQL参数进行匹配
  171. // 定一个一个Map集合 KEY为@ChenParamValue,Value 结果为参数值
  172. ConcurrentHashMap<Object, Object> paramsMap = paramsMap(proxy, method, args);
  173. // 4. 参数替换?传递方式
  174. List<String> sqlSelectParameter = SQLUtils.dealList(deleteSql);
  175. // 5.传递参数
  176. List<Object> sqlParams = new ArrayList<>();
  177. for (int i = 0; i < sqlSelectParameter.size() - 1; i++) {
  178. Object parameterValue = paramsMap.get(sqlSelectParameter.get(i));
  179. sqlParams.add(parameterValue);
  180. }
  181. // 4. 根据参数替换参数变为?
  182. String newSQL = sqlSelectParameter.get(sqlSelectParameter.size() - 1);
  183. System.out.println("newSQL:" + newSQL + ",sqlParams:" + sqlParams.toString());
  184. // 5. 调用jdbc底层代码执行语句
  185. return JDBCUtils.delete(newSQL, sqlParams);
  186. }
  187. private Object chenUpdate(ChenUpdate chenUpdate, Object proxy, Method method, Object[] args) throws IllegalAccessException, SQLException, InstantiationException {
  188. // 方法上存在@ChenInsert,获取他的SQL语句
  189. // 2. 获取SQL语句,获取注解Insert语句
  190. String deleteSql = chenUpdate.value();
  191. // System.out.println("insertSql:" + insertSql);
  192. // 3. 获取方法的参数和SQL参数进行匹配
  193. // 定一个一个Map集合 KEY为@ChenParamValue,Value 结果为参数值
  194. ConcurrentHashMap<Object, Object> paramsMap = paramsMap(proxy, method, args);
  195. // 4. 参数替换?传递方式
  196. List<String> sqlSelectParameter = SQLUtils.dealList(deleteSql);
  197. // 5.传递参数
  198. List<Object> sqlParams = new ArrayList<>();
  199. for (int i = 0; i < sqlSelectParameter.size() - 1; i++) {
  200. Object parameterValue = paramsMap.get(sqlSelectParameter.get(i));
  201. sqlParams.add(parameterValue);
  202. }
  203. // 4. 根据参数替换参数变为?
  204. String newSQL = sqlSelectParameter.get(sqlSelectParameter.size() - 1);
  205. System.out.println("newSQL:" + newSQL + ",sqlParams:" + sqlParams.toString());
  206. // 5. 调用jdbc底层代码执行语句
  207. JDBCUtils.update(newSQL, sqlParams);
  208. newSQL = "select * from " + getTableName(newSQL) + " where id=?";
  209. ArrayList<Object> objects = new ArrayList<>();
  210. objects.add(paramsMap.get("id"));
  211. return getObject(method, newSQL, objects);
  212. }
  213. private Object chenUpdateInt(ChenUpdateInt chenUpdateInt, Object proxy, Method method, Object[] args) throws IllegalAccessException, SQLException, InstantiationException {
  214. // 方法上存在@ChenInsert,获取他的SQL语句
  215. // 2. 获取SQL语句,获取注解Insert语句
  216. String deleteSql = chenUpdateInt.value();
  217. // System.out.println("insertSql:" + insertSql);
  218. // 3. 获取方法的参数和SQL参数进行匹配
  219. // 定一个一个Map集合 KEY为@ChenParamValue,Value 结果为参数值
  220. ConcurrentHashMap<Object, Object> paramsMap = paramsMap(proxy, method, args);
  221. // 4. 参数替换?传递方式
  222. List<String> sqlSelectParameter = SQLUtils.dealList(deleteSql);
  223. // 5.传递参数
  224. List<Object> sqlParams = new ArrayList<>();
  225. for (int i = 0; i < sqlSelectParameter.size() - 1; i++) {
  226. Object parameterValue = paramsMap.get(sqlSelectParameter.get(i));
  227. sqlParams.add(parameterValue);
  228. }
  229. // 4. 根据参数替换参数变为?
  230. String newSQL = sqlSelectParameter.get(sqlSelectParameter.size() - 1);
  231. System.out.println("newSQL:" + newSQL + ",sqlParams:" + sqlParams.toString());
  232. // 5. 调用jdbc底层代码执行语句
  233. return JDBCUtils.update(newSQL, sqlParams);
  234. }
  235. private List<Object> sqlParams(String[] sqlInsertParameter, ConcurrentHashMap<Object, Object> paramsMap) {
  236. List<Object> sqlParams = new ArrayList<>();
  237. for (String paramName : sqlInsertParameter) {
  238. Object paramValue = paramsMap.get(paramName);
  239. sqlParams.add(paramValue);
  240. }
  241. return sqlParams;
  242. }
  243. private ConcurrentHashMap<Object, Object> paramsMap(Object proxy, Method method, Object[] args) {
  244. ConcurrentHashMap<Object, Object> paramsMap = new ConcurrentHashMap<>();
  245. // 获取方法上的参数
  246. Parameter[] parameters = method.getParameters();
  247. for (int i = 0; i < parameters.length; i++) {
  248. Parameter parameter = parameters[i];
  249. ChenParam ChenParam = parameter.getDeclaredAnnotation(ChenParam.class);
  250. if (ChenParam != null) {
  251. // 参数名称
  252. String paramName = ChenParam.value();
  253. Object paramValue = args[i];
  254. // System.out.println(paramName + "," + paramValue);
  255. paramsMap.put(paramName, paramValue);
  256. }
  257. }
  258. return paramsMap;
  259. }
  260. }

总结

概括

  使用java动态代理拦截接口的调用,对不同注解分别处理对应的增删改查功能。

流程图

Created with Raphaël 2.2.0开始定义操作接口Mapper拦截器拦截接口方法调用首先判断方法上 是否存在自定义注解?获取注解上的SQL语句获取方法上的参数,将参数 名称和参数值绑定在一起将mybatis语法的SQL语句 替换成jdbc的SQL语句调用jdbc代码底层执行sql语句判断方法返回类型是否是基本类型结束是引用对象则使用反射机制实例对象, 获取方法返回的类型,进行实例化返回给调用者。yesnoyesno

重点及易错点

1、返回值

  代理方法的返回值一定要和接口一致,尤其是多结果的List类型,这里不能直接使用放射的返回结果创建实例,而是要取得泛型类型,在对对象一个个的属性赋值,手动添加到容器中再返回。

2、res.next()

  使用res.next()判断是否存在值后,如果存在在一定要调用res.previous();方法进行回滚。因为我们调用res.next()函数判断的时候指针已经向后移动了。
  有问题欢迎各位读者批评指正。

点个赞再走呗!欢迎留言哦!

发表评论

表情:
评论列表 (有 0 条评论,160人围观)

还没有评论,来说两句吧...

相关阅读

    相关 仿简易 ORM 框架

    在前面的文章我们分析了 MyBatis 的底层执行原理,现在我们也来自己实现一个简易的 ORM 框架,在架构上去模仿 MyBatis。跟平时开发软件一样,我们首先要做的就是明确