封装JDBC,实现简单ORM框架

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

代码仓库:malred/IFullORM

1. 原生JDBC+sql构建器

第一步: 封装jdbc

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

JDBCUtils
package org.malred.utils;import java.sql.*;public class JDBCUtils {static String url;// defaultUrl+dbName -> urlstatic String defaultUrl = "jdbc:mysql://localhost:3306/";static String dbName;static String driverName;static String defaultDriverName = "com.mysql.cj.jdbc.Driver";static String user;static String password;public static void setDataSource(String url, String driverName, String user, String password) {JDBCUtils.url = url;JDBCUtils.driverName = driverName;JDBCUtils.user = user;JDBCUtils.password = password;}public static void setDataSource(String dbName, String user, String password) {JDBCUtils.url = defaultUrl + dbName;JDBCUtils.driverName = defaultDriverName;JDBCUtils.user = user;JDBCUtils.password = password;}public static void setUrl(String url) {JDBCUtils.url = url;}public static void setDriverName(String driverName) {JDBCUtils.driverName = driverName;}public static void setUser(String user) {JDBCUtils.user = user;}public static void setPassword(String password) {JDBCUtils.password = password;}public static Connection getConn() {try {// 四要素 -> 让用户传
//            String url = "jdbc:mysql://localhost:3306/test";
//            String user = "root";
//            String password = "root";
//            String driverName = "com.mysql.cj.jdbc.Driver";//实例化驱动Class.forName(driverName);//获取连接Connection conn = DriverManager.getConnection(url, user, password);return conn;} catch (Exception e) {e.printStackTrace();return null;}}public static void close(Connection conn, PreparedStatement ps) {try {if (conn != null) conn.close();if (ps != null) ps.close();} catch (SQLException e) {e.printStackTrace();}}public static void close(Connection conn) {try {if (conn != null) conn.close();} catch (SQLException e) {e.printStackTrace();}}public static void close(PreparedStatement ps) {try {if (ps != null) ps.close();} catch (SQLException e) {e.printStackTrace();}}public static void close(Connection conn, PreparedStatement ps, ResultSet rs) {try {if (conn != null) conn.close();if (ps != null) ps.close();if (rs != null) rs.close();} catch (Exception e) {e.printStackTrace();}}
}
Operate

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

public class Operate {//通用的更新数据库的方法:insert,update,delete 语句时public static int update(String sql) throws SQLException {//1、获取连接Connection conn = JDBCUtils.getConn();//2、获取 Statement 对象,这个对象是用来给服务器传 sql 并执行 sqlStatement st = conn.createStatement();//3、执行 sqlint len = st.executeUpdate(sql);//4、释放资源JDBCUtils.close(conn, (PreparedStatement) st);return len;}// 通用的更新数据库的方法:insert,update,delete 语句,允许 sql 带?public static int update(String sql, Object... args) throws SQLException {Connection conn = JDBCUtils.getConn();int len = update(conn, sql, args);JDBCUtils.close(conn);return len;}// 通用的更新数据库的方法:insert,update,delete 语句,允许 sql 带?public static int update(Connection conn, String sql, Object... args) throws SQLException {//2、获取 PreparedStatement 对象,这个对象是用来 sql 进行预编译PreparedStatement pst = conn.prepareStatement(sql);//3、设置 sql 中的?if (args != null && args.length > 0) {//数组的下标是从 0 开始,?的编号是 1 开始for (int i = 0; i < args.length; i++) {pst.setObject(i + 1, args[i]);}}//4、执行 sqlint len = pst.executeUpdate();//5、释放资源JDBCUtils.close(pst);return len;}//通用的查询方法之一:查询一行,即一个对象/*** 执行查询操作的 SQL 语句,SQL 可以带参数(?)** @param clazz Class 查询的结果需要封装的实体的 Class 类型,例如:学生 Student,商品 Goods,订单 Order* @param sql   String 执行查询操作的 SQL 语句* @param args  Object... 对应的每个?设置的值,顺序要与?对应* @return T 封装了查询结果的实体* @throws Exception*/public static <T> T get(Class<T> clazz, String sql, Object... args) throws Exception {//1、注册驱动//2、获取连接Connection conn = JDBCUtils.getConn();//3、对 sql 进行预编译PreparedStatement pst = conn.prepareStatement(sql);//4、设置?if (args != null && args.length > 0) {//数组的下标是从 0 开始,?的编号是 1 开始for (int i = 0; i < args.length; i++) {pst.setObject(i + 1, args[i]);}}//5、查询ResultSet rs = pst.executeQuery();//获取查询的结果集的元数据信息ResultSetMetaData rsmd = rs.getMetaData();//这是查询的结果集中,一共有几列int count = rsmd.getColumnCount();T t = clazz.newInstance();//要求这个 Javabean 类型必须有无参构造while (rs.next()) {/** 问题?* (1)sql 语句中查询了几列,每一列是什么属性* (2)怎么把这个值设置到 Javabean 的属性中*///循环每一行有几列for (int i = 0; i < count; i++) {//第几列的名称// String columnName = rsmd.getColumnName(i+1);//如果 sql 中没有取别名,那么就是列名,如果有别名,返回的是别名String fieldName = rsmd.getColumnLabel(i + 1);//该列的值// Object value = rs.getObject(columnName);Object value = rs.getObject(fieldName);//设置 obj 对象的某个属性中Field field = clazz.getDeclaredField(fieldName);//JavaBean 的属性名field.setAccessible(true);field.set(t, value);}}//5、释放资源JDBCUtils.close(conn, pst, rs);return t;}//通用的查询方法之二:查询多行,即多个对象//Class<T> clazz:用来创建实例对象,获取对象的属性,并设置属性值/*** 执行查询操作的 SQL 语句,SQL 可以带参数(?)** @param clazz Class 查询的结果需要封装的实体的 Class 类型,例如:学生 Student,商品 Goods,订单 Order* @param sql   String 执行查询操作的 SQL 语句* @param args  Object... 对应的每个?设置的值,顺序要与?对应* @return ArrayList<T> 封装了查询结果的集合* @throws Exception*/public static <T> ArrayList<T> getList(Class<T> clazz, String sql, Object... args) throws Exception {//1、注册驱动,不用了//2、获取连接Connection conn = JDBCUtils.getConn();//3、对 sql 进行预编译PreparedStatement pst = conn.prepareStatement(sql);//4、对?进行设置值if (args != null && args.length > 0) {for (int i = 0; i < args.length; i++) {pst.setObject(i + 1, args[i]);}}//5、执行 sqlResultSet rs = pst.executeQuery();//获取结果集的元数据ResultSetMetaData metaData = rs.getMetaData();//获取结果中总列数int count = metaData.getColumnCount();//创建集合对象ArrayList<T> list = new ArrayList<T>();while (rs.next()) {//遍历的行//1、每一行是一个对象T obj = clazz.newInstance();//2、每一行有很多列//for 的作用是为 obj 对象的每一个属性设置值for (int i = 0; i < count; i++) {//(1)每一列的名称String fieldName = metaData.getColumnLabel(i + 1);//获取第几列的名称,如果有别名获取别名,如果没有别名获取列名//(2)每一列的值Object value = rs.getObject(i + 1);//获取第几列的值//(3)获取属性对象Field field = clazz.getDeclaredField(fieldName);//(4)设置可见性field.setAccessible(true);//(5)设置属性值field.set(obj, value);}//3、把 obj 对象放到集合中list.add(obj);}//6、释放资源JDBCUtils.close(conn, pst, rs);//7、返回结果return list;}//通用的查询方法之三:查询单个值//单值:select max(salary) from employee; 一行一列//select count(*) from t_goods; 一共几件商品public static Object getValue(String sql, Object... args) throws Exception {//2、获取连接Connection conn = JDBCUtils.getConn();//3、对 sql 进行预编译PreparedStatement pst = conn.prepareStatement(sql);//4、对?进行设置值if (args != null && args.length > 0) {for (int i = 0; i < args.length; i++) {pst.setObject(i + 1, args[i]);}}//5、执行 sqlResultSet rs = pst.executeQuery();Object value = null;if (rs.next()) {//一行value = rs.getObject(1);//一列}//6、释放资源JDBCUtils.close(conn, pst, rs);return value;}
}

第二步 sql构建器 SqlBuilder 类

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

类的一些字段

public class SqlBuilder {String sql;String tbName;// 表名String joinTb;// 连接的表的名称private SqlBuilder() {}public static SqlBuilder build() {return new SqlBuilder();}public String sql() {return this.sql;}// 自定义基础sqlpublic SqlBuilder base(String U_sql) {this.sql = U_sql;return this;}public SqlBuilder tbName(String tbName) {this.tbName = tbName;return this;}
}

select

select可以拆分为 'select x,x,x from tbName ' + 'join joinTb' + ' on tbName.x=joinTb.x ' + 'where tbName.x=?'
为?的部分由用户调用原生接口时传入参数,jdbc填充,tbName和joinTb也需要用户传入,传入后在构建器内部保存这两个值,x是用户构建sql时要传入的,代表参数(字段)的名称,会写死在sql里
    public SqlBuilder select(String tbName, String... columns) {this.sql = "select ";for (int i = 0; i < columns.length; i++) {if (i == columns.length - 1) {this.sql += columns[i] + " ";break;}this.sql += columns[i] + ", ";}this.sql += " from " + tbName;return this;}public SqlBuilder select(String[] columns) {this.sql = "select ";for (int i = 0; i < columns.length; i++) {if (i == columns.length - 1) {this.sql += columns[i] + " ";break;}this.sql += columns[i] + ", ";}this.sql += " from " + tbName;return this;}public SqlBuilder select(String tbName) {this.sql = "select * from " + tbName;return this;}public SqlBuilder select() {this.sql = "select * from " + tbName;return this;}public SqlBuilder join(SqlJoinType type, String joinTb) {this.joinTb = joinTb;sql += " " + Common.JOIN_TYPE[type.ordinal()] + " join " + joinTb;return this;}public SqlBuilder on(String in_column, SqlCompareIdentity identity, String out_column) {sql += " on " + joinTb + "." + in_column +Common.Compares[identity.ordinal()]+ tbName + "." + out_column;return this;}public SqlBuilder count(String tbName) {this.sql = "select count(*) from " + tbName;return this;}public SqlBuilder count() {this.sql = "select count(*) from " + tbName;return this;}public SqlBuilder where(String column, SqlCompareIdentity join) {if (!sql.contains("where")) {this.sql += " where " + column + Common.Compares[join.ordinal()] + " ? ";return this;}this.sql += " and " + column + Common.Compares[join.ordinal()] + "? ";return this;}

update

update可以拆分为 'update tbName set x=?,x=?' + 'where x=?'
    public SqlBuilder update(String tbName, String... columns) {this.sql = "update " + tbName + " set ";for (int i = 0; i < columns.length; i++) {if (i == columns.length - 1) {this.sql += columns[i] + "=? ";break;}this.sql += columns[i] + "=?,";}return this;}public SqlBuilder update(String[] columns) {this.sql = "update " + tbName + " set ";for (int i = 0; i < columns.length; i++) {if (i == columns.length - 1) {this.sql += columns[i] + "=? ";break;}this.sql += columns[i] + "=?,";}return this;}

delete

delete => 'delete from tbName' + 'where x= ?'
    public SqlBuilder delete(String tbName) {sql = "delete from " + tbName;return this;}public SqlBuilder delete() {sql = "delete from " + tbName;return this;}

insert

insert => 'insert into tbName(x,x,x) values (?,?,?), (?,?,?)'
    public SqlBuilder insert(String tbName, String... params) {sql = "insert into " + tbName;sql += "(";for (int i = 0; i < params.length; i++) {if (i == params.length - 1) {sql += params[i] + ") ";break;}sql += params[i] + ",";}sql += "values (";for (int i = 0; i < params.length; i++) {if (i == params.length - 1) {sql += "?)";break;}sql += "?,";}return this;}public SqlBuilder insert(String[] params) {sql = "insert into " + tbName;sql += "(";for (int i = 0; i < params.length; i++) {if (i == params.length - 1) {sql += params[i] + ") ";break;}sql += params[i] + ",";}sql += "values (";for (int i = 0; i < params.length; i++) {if (i == params.length - 1) {sql += "?)";break;}sql += "?,";}return this;}

测试sqlBuilder

public class t {@Beforepublic void before() {// 设置数据库属性JDBCUtils.setDataSource("jdbc:mysql://localhost:3306/test","com.mysql.cj.jdbc.Driver", "root", "root");}// sqlbuilder+jdbc封装@Testpublic void testSelectBuild() {String sql = SqlBuilder.build().select("tb_user").where("id", SqlCompareIdentity.NE).sql();System.out.println(sql);String sql_cols = SqlBuilder.build().select("tb_user", "username", "gender", "addr").where("id", SqlCompareIdentity.NE).sql();System.out.println(sql_cols);String sql_cols_option2 = SqlBuilder.build().select("tb_user", "username", "gender", "addr").where("id", SqlCompareIdentity.NE).where("password", SqlCompareIdentity.NE).sql();System.out.println(sql_cols_option2);String sql_count = SqlBuilder.build().count("tb_user").where("id", SqlCompareIdentity.GT).sql();System.out.println(sql_count);}@Testpublic void testSelectBuildTbName() {// 直接设置build的tbName,然后使用不需要tbName的方法来构建String sql1 = SqlBuilder.build().tbName("tb_user").select().where("id", SqlCompareIdentity.NE).sql();System.out.println(sql1);String sql_cols1 = SqlBuilder.build().tbName("tb_user").select(new String[]{"username", "gender", "addr"}).where("id", SqlCompareIdentity.NE).sql();System.out.println(sql_cols1);String sql_cols_option21 = SqlBuilder.build().tbName("tb_user").select(new String[]{"username", "gender", "addr"}).where("id", SqlCompareIdentity.NE).where("password", SqlCompareIdentity.NE).sql();System.out.println(sql_cols_option21);String sql_count1 = SqlBuilder.build().tbName("tb_user").count().where("id", SqlCompareIdentity.GT).sql();System.out.println(sql_count1);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();System.out.println(sql_join);}@Testpublic void testUpdateBuild() {String sql = SqlBuilder.build().update("tb_user", "password", "username").where("id", SqlCompareIdentity.EQ).sql();System.out.println(sql);String sql_no_tbname = SqlBuilder.build().tbName("tb_user").update(new String[]{"password", "username"}).where("id", SqlCompareIdentity.EQ).sql();System.out.println(sql_no_tbname);}@Testpublic void testInsertBuild() {String sql = SqlBuilder.build().insert("tb_user", "username", "password", "addr", "gender").sql();System.out.println(sql);}@Testpublic void testInsertBuildTbName() {String sql = SqlBuilder.build().tbName("tb_user").insert(new String[]{"username", "password", "addr", "gender"}).sql();System.out.println(sql);}@Testpublic void testDeleteBuild() {String sql_tb = SqlBuilder.build().tbName("tb_user").delete().where("id", SqlCompareIdentity.EQ).sql();System.out.println(sql_tb);String sql = SqlBuilder.build().delete("tb_user").where("id", SqlCompareIdentity.EQ).sql();System.out.println(sql);}
}

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

    @Beforepublic void before() {// 设置数据库属性JDBCUtils.setDataSource("jdbc:mysql://localhost:3306/test","com.mysql.cj.jdbc.Driver", "root", "root");}@Testpublic void testSelectMulti() throws Exception {ArrayList<TbUser> list;String sql = SqlBuilder.build().select("tb_user").where("id", SqlCompareIdentity.NE).sql();list = Operate.getList(TbUser.class, sql, 1);System.out.println(list);String sql_cols = SqlBuilder.build().select("tb_user", "username", "gender", "addr").where("id", SqlCompareIdentity.NE).sql();list = Operate.getList(TbUser.class, sql_cols, 1);System.out.println(list);String sql_cols_option2 = SqlBuilder.build().select("tb_user", "username", "gender", "addr").where("id", SqlCompareIdentity.NE).where("gender", SqlCompareIdentity.NE).sql();list = Operate.getList(TbUser.class, sql_cols_option2, 1, "男");System.out.println(list);String sql_count = SqlBuilder.build().count("tb_user").where("id", SqlCompareIdentity.GT).sql();Long cnt = (Long) Operate.getValue(sql_count, 3);System.out.println(cnt);}@Testpublic void testUpdate() throws SQLException {int cnt = 0;String sql = SqlBuilder.build().update("tb_user", "password", "username").where("id", SqlCompareIdentity.EQ).sql();cnt = Operate.update(sql, "O", "t50", "13");System.out.println("影响了" + cnt + "条数据");String sql_no_tbname = SqlBuilder.build().tbName("tb_user").update(new String[]{"password", "username"}).where("id", SqlCompareIdentity.EQ).sql();cnt = Operate.update(sql_no_tbname, "Obu", "t50123", "13");System.out.println("影响了" + cnt + "条数据");}@Testpublic void testInsert() throws SQLException {int count = 0;String sql = SqlBuilder.build().insert("tb_user", "username", "password", "addr", "gender").sql();count = Operate.update(sql, "name", "pass", "cn", "男");System.out.println("影响了" + count + "条数据");}@Testpublic void testDelete() throws SQLException {int cnt = 0;String sql_tb = SqlBuilder.build().tbName("tb_user").delete().where("id", SqlCompareIdentity.EQ).sql();cnt = Operate.update(sql_tb, 219);System.out.println("影响了" + cnt + "条数据");}

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

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

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

思路:BaseCRUDRepository<T>里定义基本curd方法,select方法的返回值由传入的泛型决定,因为方法名是死的,所以可以通过方法名来判断是哪个方法,我们可以在动态代理里根据不同方法提供不同的实现,用户只需要创建接口继承BaseCRUDRepository,就可以使用这些方法
Repository是一个注解,主要是指定数据库表名用的
package org.malred.annotations;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Repository {String value(); // 表名
}
Operate
这里的type1是执行jdbc方法需要的实体类类型,本来想通过泛型拿到,但是好像java不能反射获取类的泛型
    // 定义在Operate类里public static <T> T getMapper(Class<?> mapperClass, Class<?> type1) {// 使用JDK动态代理为Dao接口生成代理对象,并返回Object proxyInstance = Proxy.newProxyInstance(Operate.class.getClassLoader(), new Class[]{mapperClass}, new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 拿到表名Repository annotation = mapperClass.getAnnotation(Repository.class);String tbName = annotation.value();// 拼装sql
//                        String sql = (String) Common.DefaultCRUDSql.get(method.getName());
//                        System.out.println(sql);String sql = "";// 默认CRUD接口的代理方法switch (method.getName()) {case "findAll": {sql = SqlBuilder.build().tbName(tbName).select().sql();System.out.println("执行findAll方法");System.out.println("当前执行的sql语句: " + sql);return Operate.getList(type1, sql);}case "findById": {sql = SqlBuilder.build().tbName(tbName).select().where("id", SqlCompareIdentity.EQ).sql();System.out.println("执行findById方法");System.out.println("当前执行的sql语句: " + sql);return Operate.get(type1, sql, args);}case "update": {ParseClazz parseClazz = parseObjectArgs(args);String[] paramNames = new String[parseClazz.params.keySet().size()];for (int i = 0; i < parseClazz.params.keySet().toArray().length; i++) {paramNames[i] = parseClazz.params.keySet().toArray()[i].toString();}sql = SqlBuilder.build().update(tbName, paramNames).where(parseClazz.idName, SqlCompareIdentity.EQ).sql();System.out.println("执行update方法");System.out.println("当前执行的sql语句: " + sql);String[] paramVals = new String[parseClazz.params.values().size() + 1];for (int i = 0; i < parseClazz.params.values().toArray().length; i++) {paramVals[i] = parseClazz.params.values().toArray()[i].toString();
//                                    System.out.println(paramVals[i]);}paramVals[paramVals.length - 1] = parseClazz.idVal.toString();return Operate.update(sql, paramVals);}case "insert": {ParseClazz parseClazz = parseObjectArgs(args);String[] paramNames = new String[parseClazz.params.keySet().size()];for (int i = 0; i < parseClazz.params.keySet().toArray().length; i++) {paramNames[i] = parseClazz.params.keySet().toArray()[i].toString();}sql = SqlBuilder.build().tbName(tbName).insert(paramNames).sql();System.out.println("执行insert方法");System.out.println("当前执行的sql语句: " + sql);String[] paramVals = new String[parseClazz.params.values().size()];for (int i = 0; i < parseClazz.params.values().toArray().length; i++) {paramVals[i] = parseClazz.params.values().toArray()[i].toString();
//                                    System.out.println(paramVals[i]);}return update(sql, paramVals);}case "delete": {sql = SqlBuilder.build().tbName(tbName).delete().where("id", SqlCompareIdentity.EQ).sql();System.out.println("执行delete方法");System.out.println("当前执行的sql语句: " + sql);return update(sql, args[0]);}}return null;}});return (T) proxyInstance;}

测试

    // 基本CRUD接口的代理实现测试@Testpublic void testSelectProxy() {UserRepository mapper = Operate.getMapper(UserRepository.class, TbUser.class);
//        UserRepository mapper = Operate.getMapper(UserRepository.class);List<TbUser> all = mapper.findAll();System.out.println(all);TbUser one = mapper.findById(1);System.out.println(one);}@Testpublic void testUpdateProxy() {int cnt = 0;
//        UserRepository mapper = Operate.getMapper(UserRepository.class, TbUser.class);UserRepository mapper = Operate.getMapper(UserRepository.class);cnt = mapper.update(new TbUser(212, "ema1n", "s1sap", "女", null));System.out.println("影响了" + cnt + "条数据");//        cnt = mapper.update(new TbUser(211, "name", "pass", null, null));
//        System.out.println("影响了" + cnt + "条数据");}@Testpublic void testInsertProxy() {int cnt = 0;
//        UserRepository mapper = Operate.getMapper(UserRepository.class, TbUser.class);UserRepository mapper = Operate.getMapper(UserRepository.class);cnt = mapper.insert(new TbUser(0, "eman", "ssap", null, null));System.out.println("影响了" + cnt + "条数据");}@Testpublic void testDeleteProxy() {int cnt = 0;UserRepository mapper = Operate.getMapper(UserRepository.class, TbUser.class);cnt = mapper.delete(223);System.out.println("影响了" + cnt + "条数据");}

3. 注解+接口方法

思路,依然是走动态代理,只是sql的获取不再是框架构建,而是用户在注解中定义,框架只是把值填充,这里我们需要注意,如果是第二种方法和注解的混用,会有一个类型问题
这个问题就是,如果传入了type1,那么所有被代理的方法如果都用type1作为调用jdbc时传入的实体类型,就无法使用其他类型(无法查询并封装到其他类型),而我们的解决方法是,注解的方法就根据注解方法的返回值来传递实体类型,base方法就用type1
    public static <T> T getMapper(Class<?> mapperClass, Class<?> type1) {// 使用JDK动态代理为Dao接口生成代理对象,并返回Object proxyInstance = Proxy.newProxyInstance(Operate.class.getClassLoader(), new Class[]{mapperClass}, new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 用户定义的方法的返回类型Class<?> type = null;//获取方法的返回值类型Type genericReturnType = method.getGenericReturnType();if (!method.getName().equals("findAll") && !method.getName().equals("findById")) {if (genericReturnType instanceof ParameterizedType) {Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();for (Type parameterType : actualTypeArguments) {System.out.println(parameterType);type = (Class<?>) parameterType;}} else {type = (Class<?>) genericReturnType;}}// 拿到表名Repository annotation = mapperClass.getAnnotation(Repository.class);String tbName = annotation.value();// 拼装sql
//                        String sql = (String) Common.DefaultCRUDSql.get(method.getName());
//                        System.out.println(sql);String sql = "";// 默认CRUD接口的代理方法switch (method.getName()) {case "findAll": {sql = SqlBuilder.build().tbName(tbName).select().sql();System.out.println("执行findAll方法");System.out.println("当前执行的sql语句: " + sql);return Operate.getList(type1, sql);}case "findById": {sql = SqlBuilder.build().tbName(tbName).select().where("id", SqlCompareIdentity.EQ).sql();System.out.println("执行findById方法");System.out.println("当前执行的sql语句: " + sql);return Operate.get(type1, sql, args);}case "update": {ParseClazz parseClazz = parseObjectArgs(args);String[] paramNames = new String[parseClazz.params.keySet().size()];for (int i = 0; i < parseClazz.params.keySet().toArray().length; i++) {paramNames[i] = parseClazz.params.keySet().toArray()[i].toString();}sql = SqlBuilder.build().update(tbName, paramNames).where(parseClazz.idName, SqlCompareIdentity.EQ).sql();System.out.println("执行update方法");System.out.println("当前执行的sql语句: " + sql);String[] paramVals = new String[parseClazz.params.values().size() + 1];for (int i = 0; i < parseClazz.params.values().toArray().length; i++) {paramVals[i] = parseClazz.params.values().toArray()[i].toString();
//                                    System.out.println(paramVals[i]);}paramVals[paramVals.length - 1] = parseClazz.idVal.toString();return Operate.update(sql, paramVals);}case "insert": {ParseClazz parseClazz = parseObjectArgs(args);String[] paramNames = new String[parseClazz.params.keySet().size()];for (int i = 0; i < parseClazz.params.keySet().toArray().length; i++) {paramNames[i] = parseClazz.params.keySet().toArray()[i].toString();}sql = SqlBuilder.build().tbName(tbName).insert(paramNames).sql();System.out.println("执行insert方法");System.out.println("当前执行的sql语句: " + sql);String[] paramVals = new String[parseClazz.params.values().size()];for (int i = 0; i < parseClazz.params.values().toArray().length; i++) {paramVals[i] = parseClazz.params.values().toArray()[i].toString();
//                                    System.out.println(paramVals[i]);}return update(sql, paramVals);}case "delete": {sql = SqlBuilder.build().tbName(tbName).delete().where("id", SqlCompareIdentity.EQ).sql();System.out.println("执行delete方法");System.out.println("当前执行的sql语句: " + sql);return update(sql, args[0]);}}// 如果都不是上面的,就是用户自己定义的if (method.isAnnotationPresent(Select.class)) {Select selectAnno = method.getAnnotation(Select.class);sql = selectAnno.value();// 判断是查询单个还是多个(返回值类型是List之类的吗)// 这里只是简单判断一下
//                            Type genericReturnType = method.getGenericReturnType();// 判断是否进行了泛型类型参数化(是否有泛型)if (genericReturnType instanceof ParameterizedType) {
//                            if (x instanceof Collection< ? >){
//                            }
//                            if (x instanceof Map<?,?>){
//                            }return Operate.getList(type, sql, args);}return Operate.get(type, sql, args);}if (method.isAnnotationPresent(Update.class)) {Update anno = method.getAnnotation(Update.class);sql = anno.value();return update(sql, args);}if (method.isAnnotationPresent(Delete.class)) {Delete anno = method.getAnnotation(Delete.class);sql = anno.value();return update(sql, args);}if (method.isAnnotationPresent(Insert.class)) {Insert anno = method.getAnnotation(Insert.class);sql = anno.value();return update(sql, args);}// 返回值return null;}});return (T) proxyInstance;}

测试

    // 用户定义的注解的代理实现测试@Testpublic void testSelectAnnotation() {UserRepository mapper = Operate.getMapper(UserRepository.class);
//        UserRepository mapper = Operate.getMapper(UserRepository.class, TbUser.class);TbUser user = mapper.selectOneByUsername("张三");System.out.println(user);List<TbUser> tbUsers = mapper.selectOneByNEPassword("456");for (TbUser tbUser : tbUsers) {System.out.println(tbUser);}// 和因为在代理时写死传入的返回值类型,这里只能重新创建代理// 根据接口方法的返回值获取泛型类型,动态判断返回类型
//        UserRepository puMapper =
//                Operate.getMapper(UserRepository.class, ProductAndUser.class);
//        UserRepository puMapper = Operate.getMapper(UserRepository.class);List<ProductAndUser> userAndProductJoin = mapper.findUserAndProductJoin(7);System.out.println(userAndProductJoin);}@Testpublic void testUpdateAnnotation() {
//        UserRepository mapper =
//                Operate.getMapper(UserRepository.class, TbUser.class);UserRepository mapper = Operate.getMapper(UserRepository.class);int cnt = mapper.uptUser("哇哈哈1", 14);System.out.println("影响了" + cnt + "条数据");}@Testpublic void testDeleteAnnotation() {
//        UserRepository mapper = Operate.getMapper(UserRepository.class, TbUser.class);UserRepository mapper = Operate.getMapper(UserRepository.class);int cnt = mapper.delUser(218, "tpass");System.out.println("影响了" + cnt + "条数据");}@Testpublic void testInsertAnnotation() {
//        UserRepository mapper = Operate.getMapper(UserRepository.class, TbUser.class);UserRepository mapper = Operate.getMapper(UserRepository.class);int cnt = mapper.addUser("tttt", "tpass", "tttt2", "tpass2");System.out.println("影响了" + cnt + "条数据");}

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

思路:实体类加上注解,指定表名,Operator的static里扫描注解并将key-表名:val-扫到的类注入到一个map,代理方法里通过Repository里的表名和map里的匹配,拿到实体类,拼接字符串selectByxxx,放入list,当执行方法,进入代理,就判断是否在list中包含,然后实现代理逻辑

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/144136.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

【VUE复习·4】计算属性computed:原理、完整写法(不常用)、与 methods 的区别、简写(最常用)、应用案例!

总览 1.简介计算属性 2.computed 与 methods 的区别 3.computed 的简写&#xff08;不修改计算属性&#xff0c;只显示&#xff09; 4.经典应用场景 一、计算属性 1.为什么需要计算属性&#xff1f; 首先&#xff0c;如果我们要写一个插值语法&#xff0c;而 {{ }} 内的内容…

如何选择米尔基于STM32MP1系列核心板和开发板

一款合适的处理器&#xff0c;是每个工程师在开发设计前期调研必须面对的难题。而如何挑选一款符合产品开发的处理器呢&#xff1f;今天我们就以ST公司的STM32MP1系列处理器进行分析比较。 ST公司目前已经发布了两款不同类型的MPU芯片&#xff0c;分别是STM32MP15系列和STM32M…

AI算法+视频技术助力构建智慧城管解决方案,实现城市管理精细化

一、背景分析 物联网、大数据、移动互联网等技术的日新月异&#xff0c;城市管理对信息资源需求的日益提升&#xff0c;广大市民对政府服务新的诉求&#xff0c; 智慧城管正面临千载难逢的发展机遇。 发展历程&#xff1a; 1&#xff09;数字城管&#xff1a;城市管理机制的…

OpenShift 介绍

OpenShift 1. OpenShift 简介1.1 OpenShift 核心功能1.2 OpenShift 特性1.3 OCP和OKD介绍 2. OpenShift 架构2.1 OpenShift 架构概述2.2 Master和Nodes 3. 管理 OpenShift3.1 OpenShift 项目及应用3.2 使用Source-to-image构建映像3.3 管理OpenShift资源 4. OpenShift 网络/持久…

mac有必要用清理软件吗?有哪些免费的清理工具

当我们谈到Mac电脑时&#xff0c;很多人都会觉得它比Windows系统更加稳定和高效&#xff0c;也更不容易积累垃圾文件。但实际上&#xff0c;任何长时间使用的操作系统都会逐渐积累不必要的文件和缓存。那么&#xff0c;对于Mac用户来说&#xff0c;有必要使用专门的清理软件吗&…

Nature Communications | 张阳实验室:端到端深度学习实现高精度RNA结构预测

RNA分子是基因转录的主要执行者&#xff0c;也是细胞运作的隐形功臣。它们在基因表达调控、支架构建以及催化活性等多个生命过程中都扮演着关键角色。虽然RNA如此重要&#xff0c;但由于实验数据的缺乏&#xff0c;准确预测RNA 的三维空间结构仍然是目前计算生物学面临的重大挑…

货物寄到英国选择什么物流比较划算?

随着全球化的发展&#xff0c;越来越多的企业开始将产品销售到海外市场&#xff0c;其中英国作为一个重要的贸易伙伴&#xff0c;吸引了大量的中国企业的关注。然而&#xff0c;如何将货物安全、快速地运送到英国&#xff0c;成为了众多企业面临的一个问题。那么&#xff0c;货…

XML文件反序列化读取

原始XML文件 <?xml version"1.0" encoding"utf-8" ?> <School headmaster"王校长"><Grade grade"12" teacher"张老师"><Student name"小米" age"18"/><Student name&quo…

C++简单实现红黑树

目录 一、概念 二、红黑树的性质 三、红黑树的定义 四、红黑树的插入操作 情况一&#xff08;叔叔节点存在且为红色&#xff09;——变色向上调整&#xff1a; 情况二&#xff08;叔叔节点不存在或为黑色&#xff09;——旋转变色&#xff1a; 2.1叔叔节点不存在 2.2叔叔…

智能网联驾驶测试与评价工业和信息化部重点实验室“车载智能计算基础平台参考架构2.0专家研讨会”圆满结束

近日&#xff0c;智能网联驾驶测试与评价工业和信息化部重点实验室在北京市召开“车载智能计算基础平台参考架构2.0专家研讨会”&#xff0c;本次会议由智能网联驾驶测试与评价工业和信息化部重点实验室、中国软件评测中心&#xff08;工业和信息化部软件与集成电路促进中心&am…

ChatGPT 在机器学习中的应用

办公室里一个机器人坐在人类旁边&#xff0c;Artstation 上的流行趋势&#xff0c;美丽的色彩&#xff0c;4k&#xff0c;充满活力&#xff0c;蓝色和黄色&#xff0c; DreamStudio出品 一、介绍 大家都知道ChatGPT。它在解释机器学习和深度学习概念方面也非常高效&#xff0c;…

SpringCloud Alibaba - Sentinel

接上文SpringCloud Alibaba - Nacos 1.Sentinel 流量防卫兵 1.1 安装与部署 和Nacos一样&#xff0c;它是独立安装和部署的&#xff0c;下载地址https://github.com/alibaba/Sentinel/releases 下载后的jar放到目录 然后配置 启动并访问,用户名密码都是 sentinel 此时就…

祝贺莱佛士学生在ASDA2023设计大赛中获得最高奖项

莱佛士一直主张学生们积极参与各种国际知名的设计大赛&#xff0c;也会竭尽所能为学生们的参赛提供途径与指导&#xff0c;本次的American Standard Design Award&#xff08;ASDA&#xff09;2023设计大赛也不例外。 ASDA2023设计大赛&#xff0c;推广以用户为中心的设计理念…

为什么要选择Spring cloud Sentinel

为什么要选择Spring cloud Sentinel &#x1f34e;对比Hystrix&#x1f342;雪崩问题及解决方案&#x1f342;雪崩问题&#x1f342;.超时处理&#x1f342;仓壁模式&#x1f342;断路器&#x1f342;限流&#x1f342;总结 &#x1f34e;对比Hystrix 在SpringCloud当中支持多…

CSS滚动条详解(::-webkit-scrollbar )

滚动条出现的事件&#xff1a; 当设置定宽或者定高的元素添加overflow:scroll属性&#xff0c;会出现滚动条&#xff0c;但是原生样式的会比较丑影响美观。 <div class"content"><div class"contain"></div> </div>.content {wid…

第十四届蓝桥杯大赛软件赛决赛 C/C++ 大学 B 组 试题 A: 子 2023

[蓝桥杯 2023 国 B] 子 2023 试题 A: 子 2023 【问题描述】 小蓝在黑板上连续写下从 1 1 1 到 2023 2023 2023 之间所有的整数&#xff0c;得到了一个数字序列&#xff1a; S 12345678910111213 ⋯ 20222023 S 12345678910111213\cdots 20222023 S12345678910111213⋯2…

IntelliJ IDEA学习总结(3)—— IntelliJ IDEA 常用快捷键(带动图演示)

一、构建/编译 Ctrl + F9:构建项目 该快捷键,等同于菜单【Build】—>【Build Project】 执行该命令后,IntelliJ IDEA 会编译项目中所有类,并将编译结果输出到out目录中。IntelliJ IDEA 支持增量构建,会在上次构建的基础上,仅编译修改的类。 Ctrl + Shift + F9:重新编…

【论文阅读 09】融合门控自注意力机制的生成对抗网络视频异常检测

2021年 中国图象图形学报 摘 要 背景&#xff1a; 视频异常行为检测是智能监控技术的研究重点&#xff0c;广泛应用于社会安防领域。当前的挑战之一是如何提高异常检测的准确性&#xff0c;这需要有效地建模视频数据的空间维度和时间维度信息。生成对抗网络&#xff08;GANs&…

【2251. 花期内花的数目】

来源&#xff1a;力扣&#xff08;LeetCode&#xff09; 描述&#xff1a; 给你一个下标从 0 开始的二维整数数组 flowers &#xff0c;其中 flowers[i] [starti, endi] 表示第 i 朵花的 花期 从 starti 到 endi &#xff08;都 包含&#xff09;。同时给你一个下标从 0 开始…

数据结构和算法(8):搜索树(二叉搜索树和AVL树)

查找 所谓的查找或搜索&#xff0c;指从一组数据对象中找出符合特定条件者&#xff0c;这是构建算法的一种基本而重要的操作。其中的数据对象&#xff0c;统一地表示和实现为 词条&#xff08;entry&#xff09; 的形式&#xff1b;不同词条之间&#xff0c;依照各自的 关键码…