• 封装JDBC,实现简单ORM框架


    本文将封装JDBC的操作,实现简单的ORM框架,提供3种风格的api来给用户使用(1.原生jdbc+SqlBuilder;2.类似jpa和mp的;3.注解+接口方法)

    代码仓库:malred/IFullORM

    1. 原生JDBC+sql构建器

    第一步: 封装jdbc

    这个框架的重点是将jdbc等操作隐藏到框架内部,从而让用户更轻松地实现sql操作,JDBC的部分就粗略地过一下代码

    JDBCUtils
    1. package org.malred.utils;
    2. import java.sql.*;
    3. public class JDBCUtils {
    4. static String url;
    5. // defaultUrl+dbName -> url
    6. static String defaultUrl = "jdbc:mysql://localhost:3306/";
    7. static String dbName;
    8. static String driverName;
    9. static String defaultDriverName = "com.mysql.cj.jdbc.Driver";
    10. static String user;
    11. static String password;
    12. public static void setDataSource(String url, String driverName, String user, String password) {
    13. JDBCUtils.url = url;
    14. JDBCUtils.driverName = driverName;
    15. JDBCUtils.user = user;
    16. JDBCUtils.password = password;
    17. }
    18. public static void setDataSource(String dbName, String user, String password) {
    19. JDBCUtils.url = defaultUrl + dbName;
    20. JDBCUtils.driverName = defaultDriverName;
    21. JDBCUtils.user = user;
    22. JDBCUtils.password = password;
    23. }
    24. public static void setUrl(String url) {
    25. JDBCUtils.url = url;
    26. }
    27. public static void setDriverName(String driverName) {
    28. JDBCUtils.driverName = driverName;
    29. }
    30. public static void setUser(String user) {
    31. JDBCUtils.user = user;
    32. }
    33. public static void setPassword(String password) {
    34. JDBCUtils.password = password;
    35. }
    36. public static Connection getConn() {
    37. try {
    38. // 四要素 -> 让用户传
    39. // String url = "jdbc:mysql://localhost:3306/test";
    40. // String user = "root";
    41. // String password = "root";
    42. // String driverName = "com.mysql.cj.jdbc.Driver";
    43. //实例化驱动
    44. Class.forName(driverName);
    45. //获取连接
    46. Connection conn = DriverManager.getConnection(url, user, password);
    47. return conn;
    48. } catch (Exception e) {
    49. e.printStackTrace();
    50. return null;
    51. }
    52. }
    53. public static void close(Connection conn, PreparedStatement ps) {
    54. try {
    55. if (conn != null) conn.close();
    56. if (ps != null) ps.close();
    57. } catch (SQLException e) {
    58. e.printStackTrace();
    59. }
    60. }
    61. public static void close(Connection conn) {
    62. try {
    63. if (conn != null) conn.close();
    64. } catch (SQLException e) {
    65. e.printStackTrace();
    66. }
    67. }
    68. public static void close(PreparedStatement ps) {
    69. try {
    70. if (ps != null) ps.close();
    71. } catch (SQLException e) {
    72. e.printStackTrace();
    73. }
    74. }
    75. public static void close(Connection conn, PreparedStatement ps, ResultSet rs) {
    76. try {
    77. if (conn != null) conn.close();
    78. if (ps != null) ps.close();
    79. if (rs != null) rs.close();
    80. } catch (Exception e) {
    81. e.printStackTrace();
    82. }
    83. }
    84. }
    Operate

    该部分代码来源于:JDBC(3)实现通用的增删改查方法_如何将增删改查方法抽成公共方法-CSDN博客

    1. public class Operate {
    2. //通用的更新数据库的方法:insert,update,delete 语句时
    3. public static int update(String sql) throws SQLException {
    4. //1、获取连接
    5. Connection conn = JDBCUtils.getConn();
    6. //2、获取 Statement 对象,这个对象是用来给服务器传 sql 并执行 sql
    7. Statement st = conn.createStatement();
    8. //3、执行 sql
    9. int len = st.executeUpdate(sql);
    10. //4、释放资源
    11. JDBCUtils.close(conn, (PreparedStatement) st);
    12. return len;
    13. }
    14. // 通用的更新数据库的方法:insert,update,delete 语句,允许 sql 带?
    15. public static int update(String sql, Object... args) throws SQLException {
    16. Connection conn = JDBCUtils.getConn();
    17. int len = update(conn, sql, args);
    18. JDBCUtils.close(conn);
    19. return len;
    20. }
    21. // 通用的更新数据库的方法:insert,update,delete 语句,允许 sql 带?
    22. public static int update(Connection conn, String sql, Object... args) throws SQLException {
    23. //2、获取 PreparedStatement 对象,这个对象是用来 sql 进行预编译
    24. PreparedStatement pst = conn.prepareStatement(sql);
    25. //3、设置 sql 中的?
    26. if (args != null && args.length > 0) {
    27. //数组的下标是从 0 开始,?的编号是 1 开始
    28. for (int i = 0; i < args.length; i++) {
    29. pst.setObject(i + 1, args[i]);
    30. }
    31. }
    32. //4、执行 sql
    33. int len = pst.executeUpdate();
    34. //5、释放资源
    35. JDBCUtils.close(pst);
    36. return len;
    37. }
    38. //通用的查询方法之一:查询一行,即一个对象
    39. /**
    40. * 执行查询操作的 SQL 语句,SQL 可以带参数(?)
    41. *
    42. * @param clazz Class 查询的结果需要封装的实体的 Class 类型,例如:学生 Student,商品 Goods,订单 Order
    43. * @param sql String 执行查询操作的 SQL 语句
    44. * @param args Object... 对应的每个?设置的值,顺序要与?对应
    45. * @return T 封装了查询结果的实体
    46. * @throws Exception
    47. */
    48. public static <T> T get(Class<T> clazz, String sql, Object... args) throws Exception {
    49. //1、注册驱动
    50. //2、获取连接
    51. Connection conn = JDBCUtils.getConn();
    52. //3、对 sql 进行预编译
    53. PreparedStatement pst = conn.prepareStatement(sql);
    54. //4、设置?
    55. if (args != null && args.length > 0) {
    56. //数组的下标是从 0 开始,?的编号是 1 开始
    57. for (int i = 0; i < args.length; i++) {
    58. pst.setObject(i + 1, args[i]);
    59. }
    60. }
    61. //5、查询
    62. ResultSet rs = pst.executeQuery();
    63. //获取查询的结果集的元数据信息
    64. ResultSetMetaData rsmd = rs.getMetaData();
    65. //这是查询的结果集中,一共有几列
    66. int count = rsmd.getColumnCount();
    67. T t = clazz.newInstance();//要求这个 Javabean 类型必须有无参构造
    68. while (rs.next()) {
    69. /*
    70. * 问题?
    71. *1)sql 语句中查询了几列,每一列是什么属性
    72. *2)怎么把这个值设置到 Javabean 的属性中
    73. */
    74. //循环每一行有几列
    75. for (int i = 0; i < count; i++) {
    76. //第几列的名称
    77. // String columnName = rsmd.getColumnName(i+1);
    78. //如果 sql 中没有取别名,那么就是列名,如果有别名,返回的是别名
    79. String fieldName = rsmd.getColumnLabel(i + 1);
    80. //该列的值
    81. // Object value = rs.getObject(columnName);
    82. Object value = rs.getObject(fieldName);
    83. //设置 obj 对象的某个属性中
    84. Field field = clazz.getDeclaredField(fieldName);//JavaBean 的属性名
    85. field.setAccessible(true);
    86. field.set(t, value);
    87. }
    88. }
    89. //5、释放资源
    90. JDBCUtils.close(conn, pst, rs);
    91. return t;
    92. }
    93. //通用的查询方法之二:查询多行,即多个对象
    94. //Class<T> clazz:用来创建实例对象,获取对象的属性,并设置属性值
    95. /**
    96. * 执行查询操作的 SQL 语句,SQL 可以带参数(?)
    97. *
    98. * @param clazz Class 查询的结果需要封装的实体的 Class 类型,例如:学生 Student,商品 Goods,订单 Order
    99. * @param sql String 执行查询操作的 SQL 语句
    100. * @param args Object... 对应的每个?设置的值,顺序要与?对应
    101. * @return ArrayList 封装了查询结果的集合
    102. * @throws Exception
    103. */
    104. public static <T> ArrayList<T> getList(Class<T> clazz, String sql, Object... args) throws Exception {
    105. //1、注册驱动,不用了
    106. //2、获取连接
    107. Connection conn = JDBCUtils.getConn();
    108. //3、对 sql 进行预编译
    109. PreparedStatement pst = conn.prepareStatement(sql);
    110. //4、对?进行设置值
    111. if (args != null && args.length > 0) {
    112. for (int i = 0; i < args.length; i++) {
    113. pst.setObject(i + 1, args[i]);
    114. }
    115. }
    116. //5、执行 sql
    117. ResultSet rs = pst.executeQuery();
    118. //获取结果集的元数据
    119. ResultSetMetaData metaData = rs.getMetaData();
    120. //获取结果中总列数
    121. int count = metaData.getColumnCount();
    122. //创建集合对象
    123. ArrayList<T> list = new ArrayList<T>();
    124. while (rs.next()) {//遍历的行
    125. //1、每一行是一个对象
    126. T obj = clazz.newInstance();
    127. //2、每一行有很多列
    128. //for 的作用是为 obj 对象的每一个属性设置值
    129. for (int i = 0; i < count; i++) {
    130. //(1)每一列的名称
    131. String fieldName = metaData.getColumnLabel(i + 1);//获取第几列的名称,如果有别名获取别名,如果没有别名获取列名
    132. //(2)每一列的值
    133. Object value = rs.getObject(i + 1);//获取第几列的值
    134. //(3)获取属性对象
    135. Field field = clazz.getDeclaredField(fieldName);
    136. //(4)设置可见性
    137. field.setAccessible(true);
    138. //(5)设置属性值
    139. field.set(obj, value);
    140. }
    141. //3、把 obj 对象放到集合中
    142. list.add(obj);
    143. }
    144. //6、释放资源
    145. JDBCUtils.close(conn, pst, rs);
    146. //7、返回结果
    147. return list;
    148. }
    149. //通用的查询方法之三:查询单个值
    150. //单值:select max(salary) from employee; 一行一列
    151. //select count(*) from t_goods; 一共几件商品
    152. public static Object getValue(String sql, Object... args) throws Exception {
    153. //2、获取连接
    154. Connection conn = JDBCUtils.getConn();
    155. //3、对 sql 进行预编译
    156. PreparedStatement pst = conn.prepareStatement(sql);
    157. //4、对?进行设置值
    158. if (args != null && args.length > 0) {
    159. for (int i = 0; i < args.length; i++) {
    160. pst.setObject(i + 1, args[i]);
    161. }
    162. }
    163. //5、执行 sql
    164. ResultSet rs = pst.executeQuery();
    165. Object value = null;
    166. if (rs.next()) {//一行
    167. value = rs.getObject(1);//一列
    168. }
    169. //6、释放资源
    170. JDBCUtils.close(conn, pst, rs);
    171. return value;
    172. }
    173. }

    第二步 sql构建器 SqlBuilder 类

    我们将sql语句拆分为小段,然后定义单独的方法,一个一个地拼接sql语句(链式调用),最后通过一个方法获取类内部存放的sql语句

    类的一些字段

    1. public class SqlBuilder {
    2. String sql;
    3. String tbName;// 表名
    4. String joinTb;// 连接的表的名称
    5. private SqlBuilder() {
    6. }
    7. public static SqlBuilder build() {
    8. return new SqlBuilder();
    9. }
    10. public String sql() {
    11. return this.sql;
    12. }
    13. // 自定义基础sql
    14. public SqlBuilder base(String U_sql) {
    15. this.sql = U_sql;
    16. return this;
    17. }
    18. public SqlBuilder tbName(String tbName) {
    19. this.tbName = tbName;
    20. return this;
    21. }
    22. }

    select

    select可以拆分为 'select x,x,x from tbName ' + 'join joinTb' + ' on tbName.x=joinTb.x ' + 'where tbName.x=?'
    为?的部分由用户调用原生接口时传入参数,jdbc填充,tbName和joinTb也需要用户传入,传入后在构建器内部保存这两个值,x是用户构建sql时要传入的,代表参数(字段)的名称,会写死在sql里
    1. public SqlBuilder select(String tbName, String... columns) {
    2. this.sql = "select ";
    3. for (int i = 0; i < columns.length; i++) {
    4. if (i == columns.length - 1) {
    5. this.sql += columns[i] + " ";
    6. break;
    7. }
    8. this.sql += columns[i] + ", ";
    9. }
    10. this.sql += " from " + tbName;
    11. return this;
    12. }
    13. public SqlBuilder select(String[] columns) {
    14. this.sql = "select ";
    15. for (int i = 0; i < columns.length; i++) {
    16. if (i == columns.length - 1) {
    17. this.sql += columns[i] + " ";
    18. break;
    19. }
    20. this.sql += columns[i] + ", ";
    21. }
    22. this.sql += " from " + tbName;
    23. return this;
    24. }
    25. public SqlBuilder select(String tbName) {
    26. this.sql = "select * from " + tbName;
    27. return this;
    28. }
    29. public SqlBuilder select() {
    30. this.sql = "select * from " + tbName;
    31. return this;
    32. }
    33. public SqlBuilder join(SqlJoinType type, String joinTb) {
    34. this.joinTb = joinTb;
    35. sql += " " + Common.JOIN_TYPE[type.ordinal()] + " join " + joinTb;
    36. return this;
    37. }
    38. public SqlBuilder on(String in_column, SqlCompareIdentity identity, String out_column) {
    39. sql += " on " + joinTb + "." + in_column +
    40. Common.Compares[identity.ordinal()]
    41. + tbName + "." + out_column;
    42. return this;
    43. }
    44. public SqlBuilder count(String tbName) {
    45. this.sql = "select count(*) from " + tbName;
    46. return this;
    47. }
    48. public SqlBuilder count() {
    49. this.sql = "select count(*) from " + tbName;
    50. return this;
    51. }
    52. public SqlBuilder where(String column, SqlCompareIdentity join) {
    53. if (!sql.contains("where")) {
    54. this.sql += " where " + column + Common.Compares[join.ordinal()] + " ? ";
    55. return this;
    56. }
    57. this.sql += " and " + column + Common.Compares[join.ordinal()] + "? ";
    58. return this;
    59. }

    update

    update可以拆分为 'update tbName set x=?,x=?' + 'where x=?'
    1. public SqlBuilder update(String tbName, String... columns) {
    2. this.sql = "update " + tbName + " set ";
    3. for (int i = 0; i < columns.length; i++) {
    4. if (i == columns.length - 1) {
    5. this.sql += columns[i] + "=? ";
    6. break;
    7. }
    8. this.sql += columns[i] + "=?,";
    9. }
    10. return this;
    11. }
    12. public SqlBuilder update(String[] columns) {
    13. this.sql = "update " + tbName + " set ";
    14. for (int i = 0; i < columns.length; i++) {
    15. if (i == columns.length - 1) {
    16. this.sql += columns[i] + "=? ";
    17. break;
    18. }
    19. this.sql += columns[i] + "=?,";
    20. }
    21. return this;
    22. }

    delete

    delete => 'delete from tbName' + 'where x= ?'
    1. public SqlBuilder delete(String tbName) {
    2. sql = "delete from " + tbName;
    3. return this;
    4. }
    5. public SqlBuilder delete() {
    6. sql = "delete from " + tbName;
    7. return this;
    8. }

    insert

    insert => 'insert into tbName(x,x,x) values (?,?,?), (?,?,?)'
    1. public SqlBuilder insert(String tbName, String... params) {
    2. sql = "insert into " + tbName;
    3. sql += "(";
    4. for (int i = 0; i < params.length; i++) {
    5. if (i == params.length - 1) {
    6. sql += params[i] + ") ";
    7. break;
    8. }
    9. sql += params[i] + ",";
    10. }
    11. sql += "values (";
    12. for (int i = 0; i < params.length; i++) {
    13. if (i == params.length - 1) {
    14. sql += "?)";
    15. break;
    16. }
    17. sql += "?,";
    18. }
    19. return this;
    20. }
    21. public SqlBuilder insert(String[] params) {
    22. sql = "insert into " + tbName;
    23. sql += "(";
    24. for (int i = 0; i < params.length; i++) {
    25. if (i == params.length - 1) {
    26. sql += params[i] + ") ";
    27. break;
    28. }
    29. sql += params[i] + ",";
    30. }
    31. sql += "values (";
    32. for (int i = 0; i < params.length; i++) {
    33. if (i == params.length - 1) {
    34. sql += "?)";
    35. break;
    36. }
    37. sql += "?,";
    38. }
    39. return this;
    40. }

    测试sqlBuilder

    1. public class t {
    2. @Before
    3. public void before() {
    4. // 设置数据库属性
    5. JDBCUtils
    6. .setDataSource("jdbc:mysql://localhost:3306/test",
    7. "com.mysql.cj.jdbc.Driver", "root", "root");
    8. }
    9. // sqlbuilder+jdbc封装
    10. @Test
    11. public void testSelectBuild() {
    12. String sql = SqlBuilder.build()
    13. .select("tb_user")
    14. .where("id", SqlCompareIdentity.NE)
    15. .sql();
    16. System.out.println(sql);
    17. String sql_cols = SqlBuilder.build()
    18. .select("tb_user", "username", "gender", "addr")
    19. .where("id", SqlCompareIdentity.NE)
    20. .sql();
    21. System.out.println(sql_cols);
    22. String sql_cols_option2 = SqlBuilder.build().select("tb_user", "username", "gender", "addr").where("id", SqlCompareIdentity.NE).where("password", SqlCompareIdentity.NE).sql();
    23. System.out.println(sql_cols_option2);
    24. String sql_count = SqlBuilder.build().count("tb_user").where("id", SqlCompareIdentity.GT).sql();
    25. System.out.println(sql_count);
    26. }
    27. @Test
    28. public void testSelectBuildTbName() {
    29. // 直接设置build的tbName,然后使用不需要tbName的方法来构建
    30. String sql1 = SqlBuilder.build().tbName("tb_user").select().where("id", SqlCompareIdentity.NE).sql();
    31. System.out.println(sql1);
    32. String sql_cols1 = SqlBuilder.build().tbName("tb_user").select(new String[]{"username", "gender", "addr"}).where("id", SqlCompareIdentity.NE).sql();
    33. System.out.println(sql_cols1);
    34. String sql_cols_option21 = SqlBuilder.build().tbName("tb_user").select(new String[]{"username", "gender", "addr"}).where("id", SqlCompareIdentity.NE).where("password", SqlCompareIdentity.NE).sql();
    35. System.out.println(sql_cols_option21);
    36. String sql_count1 = SqlBuilder.build().tbName("tb_user").count().where("id", SqlCompareIdentity.GT).sql();
    37. System.out.println(sql_count1);
    38. String sql_join = SqlBuilder.build().tbName("tb_user").select().join(SqlJoinType.INNER, "tb_product").on("id", SqlCompareIdentity.EQ, "id").where("tb_user.id", SqlCompareIdentity.EQ).sql();
    39. System.out.println(sql_join);
    40. }
    41. @Test
    42. public void testUpdateBuild() {
    43. String sql = SqlBuilder.build()
    44. .update("tb_user", "password", "username")
    45. .where("id", SqlCompareIdentity.EQ)
    46. .sql();
    47. System.out.println(sql);
    48. String sql_no_tbname = SqlBuilder.build().tbName("tb_user").update(new String[]{"password", "username"}).where("id", SqlCompareIdentity.EQ).sql();
    49. System.out.println(sql_no_tbname);
    50. }
    51. @Test
    52. public void testInsertBuild() {
    53. String sql = SqlBuilder.build()
    54. .insert("tb_user", "username", "password", "addr", "gender")
    55. .sql();
    56. System.out.println(sql);
    57. }
    58. @Test
    59. public void testInsertBuildTbName() {
    60. String sql = SqlBuilder.build()
    61. .tbName("tb_user")
    62. .insert(new String[]{"username", "password", "addr", "gender"}).sql();
    63. System.out.println(sql);
    64. }
    65. @Test
    66. public void testDeleteBuild() {
    67. String sql_tb = SqlBuilder.build()
    68. .tbName("tb_user").delete()
    69. .where("id", SqlCompareIdentity.EQ)
    70. .sql();
    71. System.out.println(sql_tb);
    72. String sql = SqlBuilder.build().delete("tb_user")
    73. .where("id", SqlCompareIdentity.EQ).sql();
    74. System.out.println(sql);
    75. }
    76. }

    第三步 使用原生接口+sql构建器执行crud

    1. @Before
    2. public void before() {
    3. // 设置数据库属性
    4. JDBCUtils
    5. .setDataSource("jdbc:mysql://localhost:3306/test",
    6. "com.mysql.cj.jdbc.Driver", "root", "root");
    7. }
    8. @Test
    9. public void testSelectMulti() throws Exception {
    10. ArrayList<TbUser> list;
    11. String sql = SqlBuilder.build().select("tb_user").where("id", SqlCompareIdentity.NE).sql();
    12. list = Operate.getList(TbUser.class, sql, 1);
    13. System.out.println(list);
    14. String sql_cols = SqlBuilder.build().select("tb_user", "username", "gender", "addr").where("id", SqlCompareIdentity.NE).sql();
    15. list = Operate.getList(TbUser.class, sql_cols, 1);
    16. System.out.println(list);
    17. String sql_cols_option2 = SqlBuilder.build().select("tb_user", "username", "gender", "addr").where("id", SqlCompareIdentity.NE).where("gender", SqlCompareIdentity.NE).sql();
    18. list = Operate.getList(TbUser.class, sql_cols_option2, 1, "男");
    19. System.out.println(list);
    20. String sql_count = SqlBuilder.build().count("tb_user").where("id", SqlCompareIdentity.GT).sql();
    21. Long cnt = (Long) Operate.getValue(sql_count, 3);
    22. System.out.println(cnt);
    23. }
    24. @Test
    25. public void testUpdate() throws SQLException {
    26. int cnt = 0;
    27. String sql = SqlBuilder.build()
    28. .update("tb_user", "password", "username")
    29. .where("id", SqlCompareIdentity.EQ)
    30. .sql();
    31. cnt = Operate.update(sql, "O", "t50", "13");
    32. System.out.println("影响了" + cnt + "条数据");
    33. String sql_no_tbname = SqlBuilder.build()
    34. .tbName("tb_user")
    35. .update(new String[]{"password", "username"})
    36. .where("id", SqlCompareIdentity.EQ)
    37. .sql();
    38. cnt = Operate.update(sql_no_tbname, "Obu", "t50123", "13");
    39. System.out.println("影响了" + cnt + "条数据");
    40. }
    41. @Test
    42. public void testInsert() throws SQLException {
    43. int count = 0;
    44. String sql = SqlBuilder.build()
    45. .insert("tb_user", "username", "password", "addr", "gender")
    46. .sql();
    47. count = Operate.update(sql, "name", "pass", "cn", "男");
    48. System.out.println("影响了" + count + "条数据");
    49. }
    50. @Test
    51. public void testDelete() throws SQLException {
    52. int cnt = 0;
    53. String sql_tb = SqlBuilder.build()
    54. .tbName("tb_user")
    55. .delete()
    56. .where("id", SqlCompareIdentity.EQ).sql();
    57. cnt = Operate.update(sql_tb, 219);
    58. System.out.println("影响了" + cnt + "条数据");
    59. }

    这里的SqlCompareIdentity是为了方便管理,把一些比较关键字放到枚举里,然后在commons类里定义数组,根据enum的索引从数组里拿比较运算符来拼接

    SqlCompareIdentity
    1. public enum SqlCompareIdentity {
    2. GT,// >
    3. GE,// >=
    4. LT, // <
    5. LE,// <=
    6. EQ, // =
    7. NE, // !=
    8. }
    Common
    1. public class Common {
    2. public static final String[] Compares = new String[]{
    3. ">", ">=", "<", "<=", "=", "!=", "like", "in"
    4. };
    5. // baseCRUD方法的名称
    6. public static final String[] JOIN_TYPE = new String[]{
    7. "left","right","full","inner","left outer","right outer"
    8. };
    9. }
    SqlJoinType
    1. public enum SqlJoinType {
    2. // "left","right","full","inner","left outer","right outer"
    3. LEFT,
    4. RIGHT,
    5. FULL,
    6. INNER,
    7. LO,
    8. RO
    9. }

    2. 创建BaseCRUDRepository,实现动态代理

    思路:BaseCRUDRepository里定义基本curd方法,select方法的返回值由传入的泛型决定,因为方法名是死的,所以可以通过方法名来判断是哪个方法,我们可以在动态代理里根据不同方法提供不同的实现,用户只需要创建接口继承BaseCRUDRepository,就可以使用这些方法
    Repository是一个注解,主要是指定数据库表名用的
    1. package org.malred.annotations;
    2. import java.lang.annotation.ElementType;
    3. import java.lang.annotation.Retention;
    4. import java.lang.annotation.RetentionPolicy;
    5. import java.lang.annotation.Target;
    6. @Target(ElementType.TYPE)
    7. @Retention(RetentionPolicy.RUNTIME)
    8. public @interface Repository {
    9. String value(); // 表名
    10. }
    Operate
    这里的type1是执行jdbc方法需要的实体类类型,本来想通过泛型拿到,但是好像java不能反射获取类的泛型
    1. // 定义在Operate类里
    2. public static <T> T getMapper(Class<?> mapperClass, Class<?> type1) {
    3. // 使用JDK动态代理为Dao接口生成代理对象,并返回
    4. Object proxyInstance = Proxy.newProxyInstance(Operate.class.getClassLoader(), new Class[]{mapperClass}, new InvocationHandler() {
    5. @Override
    6. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    7. // 拿到表名
    8. Repository annotation = mapperClass.getAnnotation(Repository.class);
    9. String tbName = annotation.value();
    10. // 拼装sql
    11. // String sql = (String) Common.DefaultCRUDSql.get(method.getName());
    12. // System.out.println(sql);
    13. String sql = "";
    14. // 默认CRUD接口的代理方法
    15. switch (method.getName()) {
    16. case "findAll": {
    17. sql = SqlBuilder.build().tbName(tbName).select().sql();
    18. System.out.println("执行findAll方法");
    19. System.out.println("当前执行的sql语句: " + sql);
    20. return Operate.getList(type1, sql);
    21. }
    22. case "findById": {
    23. sql = SqlBuilder.build().tbName(tbName).select().where("id", SqlCompareIdentity.EQ).sql();
    24. System.out.println("执行findById方法");
    25. System.out.println("当前执行的sql语句: " + sql);
    26. return Operate.get(type1, sql, args);
    27. }
    28. case "update": {
    29. ParseClazz parseClazz = parseObjectArgs(args);
    30. String[] paramNames = new String[parseClazz.params.keySet().size()];
    31. for (int i = 0; i < parseClazz.params.keySet().toArray().length; i++) {
    32. paramNames[i] = parseClazz.params.keySet().toArray()[i].toString();
    33. }
    34. sql = SqlBuilder.build().update(tbName, paramNames).where(parseClazz.idName, SqlCompareIdentity.EQ).sql();
    35. System.out.println("执行update方法");
    36. System.out.println("当前执行的sql语句: " + sql);
    37. String[] paramVals = new String[parseClazz.params.values().size() + 1];
    38. for (int i = 0; i < parseClazz.params.values().toArray().length; i++) {
    39. paramVals[i] = parseClazz.params.values().toArray()[i].toString();
    40. // System.out.println(paramVals[i]);
    41. }
    42. paramVals[paramVals.length - 1] = parseClazz.idVal.toString();
    43. return Operate.update(sql, paramVals);
    44. }
    45. case "insert": {
    46. ParseClazz parseClazz = parseObjectArgs(args);
    47. String[] paramNames = new String[parseClazz.params.keySet().size()];
    48. for (int i = 0; i < parseClazz.params.keySet().toArray().length; i++) {
    49. paramNames[i] = parseClazz.params.keySet().toArray()[i].toString();
    50. }
    51. sql = SqlBuilder.build().tbName(tbName).insert(paramNames).sql();
    52. System.out.println("执行insert方法");
    53. System.out.println("当前执行的sql语句: " + sql);
    54. String[] paramVals = new String[parseClazz.params.values().size()];
    55. for (int i = 0; i < parseClazz.params.values().toArray().length; i++) {
    56. paramVals[i] = parseClazz.params.values().toArray()[i].toString();
    57. // System.out.println(paramVals[i]);
    58. }
    59. return update(sql, paramVals);
    60. }
    61. case "delete": {
    62. sql = SqlBuilder.build().tbName(tbName).delete().where("id", SqlCompareIdentity.EQ).sql();
    63. System.out.println("执行delete方法");
    64. System.out.println("当前执行的sql语句: " + sql);
    65. return update(sql, args[0]);
    66. }
    67. }
    68. return null;
    69. }
    70. });
    71. return (T) proxyInstance;
    72. }

    测试

    1. // 基本CRUD接口的代理实现测试
    2. @Test
    3. public void testSelectProxy() {
    4. UserRepository mapper = Operate.getMapper(UserRepository.class, TbUser.class);
    5. // UserRepository mapper = Operate.getMapper(UserRepository.class);
    6. List<TbUser> all = mapper.findAll();
    7. System.out.println(all);
    8. TbUser one = mapper.findById(1);
    9. System.out.println(one);
    10. }
    11. @Test
    12. public void testUpdateProxy() {
    13. int cnt = 0;
    14. // UserRepository mapper = Operate.getMapper(UserRepository.class, TbUser.class);
    15. UserRepository mapper = Operate.getMapper(UserRepository.class);
    16. cnt = mapper.update(new TbUser(212, "ema1n", "s1sap", "女", null));
    17. System.out.println("影响了" + cnt + "条数据");
    18. // cnt = mapper.update(new TbUser(211, "name", "pass", null, null));
    19. // System.out.println("影响了" + cnt + "条数据");
    20. }
    21. @Test
    22. public void testInsertProxy() {
    23. int cnt = 0;
    24. // UserRepository mapper = Operate.getMapper(UserRepository.class, TbUser.class);
    25. UserRepository mapper = Operate.getMapper(UserRepository.class);
    26. cnt = mapper.insert(new TbUser(0, "eman", "ssap", null, null));
    27. System.out.println("影响了" + cnt + "条数据");
    28. }
    29. @Test
    30. public void testDeleteProxy() {
    31. int cnt = 0;
    32. UserRepository mapper = Operate.getMapper(UserRepository.class, TbUser.class);
    33. cnt = mapper.delete(223);
    34. System.out.println("影响了" + cnt + "条数据");
    35. }

    3. 注解+接口方法

    思路,依然是走动态代理,只是sql的获取不再是框架构建,而是用户在注解中定义,框架只是把值填充,这里我们需要注意,如果是第二种方法和注解的混用,会有一个类型问题
    这个问题就是,如果传入了type1,那么所有被代理的方法如果都用type1作为调用jdbc时传入的实体类型,就无法使用其他类型(无法查询并封装到其他类型),而我们的解决方法是,注解的方法就根据注解方法的返回值来传递实体类型,base方法就用type1
    1. public static <T> T getMapper(Class<?> mapperClass, Class<?> type1) {
    2. // 使用JDK动态代理为Dao接口生成代理对象,并返回
    3. Object proxyInstance = Proxy.newProxyInstance(Operate.class.getClassLoader(), new Class[]{mapperClass}, new InvocationHandler() {
    4. @Override
    5. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    6. // 用户定义的方法的返回类型
    7. Class<?> type = null;
    8. //获取方法的返回值类型
    9. Type genericReturnType = method.getGenericReturnType();
    10. if (!method.getName().equals("findAll") && !method.getName().equals("findById")) {
    11. if (genericReturnType instanceof ParameterizedType) {
    12. Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
    13. for (Type parameterType : actualTypeArguments) {
    14. System.out.println(parameterType);
    15. type = (Class<?>) parameterType;
    16. }
    17. } else {
    18. type = (Class<?>) genericReturnType;
    19. }
    20. }
    21. // 拿到表名
    22. Repository annotation = mapperClass.getAnnotation(Repository.class);
    23. String tbName = annotation.value();
    24. // 拼装sql
    25. // String sql = (String) Common.DefaultCRUDSql.get(method.getName());
    26. // System.out.println(sql);
    27. String sql = "";
    28. // 默认CRUD接口的代理方法
    29. switch (method.getName()) {
    30. case "findAll": {
    31. sql = SqlBuilder.build().tbName(tbName).select().sql();
    32. System.out.println("执行findAll方法");
    33. System.out.println("当前执行的sql语句: " + sql);
    34. return Operate.getList(type1, sql);
    35. }
    36. case "findById": {
    37. sql = SqlBuilder.build().tbName(tbName).select().where("id", SqlCompareIdentity.EQ).sql();
    38. System.out.println("执行findById方法");
    39. System.out.println("当前执行的sql语句: " + sql);
    40. return Operate.get(type1, sql, args);
    41. }
    42. case "update": {
    43. ParseClazz parseClazz = parseObjectArgs(args);
    44. String[] paramNames = new String[parseClazz.params.keySet().size()];
    45. for (int i = 0; i < parseClazz.params.keySet().toArray().length; i++) {
    46. paramNames[i] = parseClazz.params.keySet().toArray()[i].toString();
    47. }
    48. sql = SqlBuilder.build().update(tbName, paramNames).where(parseClazz.idName, SqlCompareIdentity.EQ).sql();
    49. System.out.println("执行update方法");
    50. System.out.println("当前执行的sql语句: " + sql);
    51. String[] paramVals = new String[parseClazz.params.values().size() + 1];
    52. for (int i = 0; i < parseClazz.params.values().toArray().length; i++) {
    53. paramVals[i] = parseClazz.params.values().toArray()[i].toString();
    54. // System.out.println(paramVals[i]);
    55. }
    56. paramVals[paramVals.length - 1] = parseClazz.idVal.toString();
    57. return Operate.update(sql, paramVals);
    58. }
    59. case "insert": {
    60. ParseClazz parseClazz = parseObjectArgs(args);
    61. String[] paramNames = new String[parseClazz.params.keySet().size()];
    62. for (int i = 0; i < parseClazz.params.keySet().toArray().length; i++) {
    63. paramNames[i] = parseClazz.params.keySet().toArray()[i].toString();
    64. }
    65. sql = SqlBuilder.build().tbName(tbName).insert(paramNames).sql();
    66. System.out.println("执行insert方法");
    67. System.out.println("当前执行的sql语句: " + sql);
    68. String[] paramVals = new String[parseClazz.params.values().size()];
    69. for (int i = 0; i < parseClazz.params.values().toArray().length; i++) {
    70. paramVals[i] = parseClazz.params.values().toArray()[i].toString();
    71. // System.out.println(paramVals[i]);
    72. }
    73. return update(sql, paramVals);
    74. }
    75. case "delete": {
    76. sql = SqlBuilder.build().tbName(tbName).delete().where("id", SqlCompareIdentity.EQ).sql();
    77. System.out.println("执行delete方法");
    78. System.out.println("当前执行的sql语句: " + sql);
    79. return update(sql, args[0]);
    80. }
    81. }
    82. // 如果都不是上面的,就是用户自己定义的
    83. if (method.isAnnotationPresent(Select.class)) {
    84. Select selectAnno = method.getAnnotation(Select.class);
    85. sql = selectAnno.value();
    86. // 判断是查询单个还是多个(返回值类型是List之类的吗)
    87. // 这里只是简单判断一下
    88. // Type genericReturnType = method.getGenericReturnType();
    89. // 判断是否进行了泛型类型参数化(是否有泛型)
    90. if (genericReturnType instanceof ParameterizedType) {
    91. // if (x instanceof Collection< ? >){
    92. // }
    93. // if (x instanceof Map<?,?>){
    94. // }
    95. return Operate.getList(type, sql, args);
    96. }
    97. return Operate.get(type, sql, args);
    98. }
    99. if (method.isAnnotationPresent(Update.class)) {
    100. Update anno = method.getAnnotation(Update.class);
    101. sql = anno.value();
    102. return update(sql, args);
    103. }
    104. if (method.isAnnotationPresent(Delete.class)) {
    105. Delete anno = method.getAnnotation(Delete.class);
    106. sql = anno.value();
    107. return update(sql, args);
    108. }
    109. if (method.isAnnotationPresent(Insert.class)) {
    110. Insert anno = method.getAnnotation(Insert.class);
    111. sql = anno.value();
    112. return update(sql, args);
    113. }
    114. // 返回值
    115. return null;
    116. }
    117. });
    118. return (T) proxyInstance;
    119. }

    测试

    1. // 用户定义的注解的代理实现测试
    2. @Test
    3. public void testSelectAnnotation() {
    4. UserRepository mapper = Operate.getMapper(UserRepository.class);
    5. // UserRepository mapper = Operate.getMapper(UserRepository.class, TbUser.class);
    6. TbUser user = mapper.selectOneByUsername("张三");
    7. System.out.println(user);
    8. List<TbUser> tbUsers = mapper.selectOneByNEPassword("456");
    9. for (TbUser tbUser : tbUsers) {
    10. System.out.println(tbUser);
    11. }
    12. // 和因为在代理时写死传入的返回值类型,这里只能重新创建代理
    13. // 根据接口方法的返回值获取泛型类型,动态判断返回类型
    14. // UserRepository puMapper =
    15. // Operate.getMapper(UserRepository.class, ProductAndUser.class);
    16. // UserRepository puMapper = Operate.getMapper(UserRepository.class);
    17. List<ProductAndUser> userAndProductJoin = mapper.findUserAndProductJoin(7);
    18. System.out.println(userAndProductJoin);
    19. }
    20. @Test
    21. public void testUpdateAnnotation() {
    22. // UserRepository mapper =
    23. // Operate.getMapper(UserRepository.class, TbUser.class);
    24. UserRepository mapper = Operate.getMapper(UserRepository.class);
    25. int cnt = mapper.uptUser("哇哈哈1", 14);
    26. System.out.println("影响了" + cnt + "条数据");
    27. }
    28. @Test
    29. public void testDeleteAnnotation() {
    30. // UserRepository mapper = Operate.getMapper(UserRepository.class, TbUser.class);
    31. UserRepository mapper = Operate.getMapper(UserRepository.class);
    32. int cnt = mapper.delUser(218, "tpass");
    33. System.out.println("影响了" + cnt + "条数据");
    34. }
    35. @Test
    36. public void testInsertAnnotation() {
    37. // UserRepository mapper = Operate.getMapper(UserRepository.class, TbUser.class);
    38. UserRepository mapper = Operate.getMapper(UserRepository.class);
    39. int cnt = mapper.addUser("tttt", "tpass", "tttt2", "tpass2");
    40. System.out.println("影响了" + cnt + "条数据");
    41. }

    下一步: 接口代理的selectBy实体类属性名方法

    思路:实体类加上注解,指定表名,Operator的static里扫描注解并将key-表名:val-扫到的类注入到一个map,代理方法里通过Repository里的表名和map里的匹配,拿到实体类,拼接字符串selectByxxx,放入list,当执行方法,进入代理,就判断是否在list中包含,然后实现代理逻辑
  • 相关阅读:
    [模拟][模电][面试][运放]仪表放大器
    java写一个用于生成雪花id的工具类
    【UNR #6 A】面基之路(最短路)
    Lagent & AgentLego 智能体应用搭建——笔记
    即时编译器JIT
    优化|优化处理可再生希尔伯特核空间的非参数回归中的协变量偏移
    JWT一篇通
    【算法|贪心算法系列No.5】leetcode409. 最长回文串
    js中各种数据类型检测与判定
    美创科技勒索病毒“零信任”防护和数据安全治理体系的探索实践
  • 原文地址:https://blog.csdn.net/m0_60707623/article/details/133392772