封装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.mzph.cn/news/89966.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

26551-2011 畜牧机械 粗饲料切碎机

声明 本文是学习GB-T 26551-2011 畜牧机械 粗饲料切碎机. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 1 范围 本标准规定了粗饲料切碎机的产品型号、技术要求、试验方法、检验规则、标志、包装、运输与贮存。 本标准适用于加工农作物秸秆等粗饲料…

Docker(一)、linux环境安装Docker

linux环境安装Docker 1、自动下载安装&#xff1a;2、指定yum的安装源3、安装docker社区版4、启动docker5、验证6、demo:从国外自动抽取下载hello-world镜像&#xff0c;并启动6、卸载docker7、安装的时候&#xff0c;会出现Error: Failed to download metadata for repo base:…

Python 3.10.9 Mac m1 无法安装grpc的解决办法

pip install airavata-mft-cli python3.10 -m venv venv source venv/bin/activate pip install grpcio1.47.0rc1 pip install grpcio-tools1.47.0rc1 然后就成了 python -m grpc_tools.protoc -I --python_out. --grpc_python_out. message.proto 参考文章&#xff1a; …

Mysql生产随笔

目录 1. Mysql批量Kill删除processlist 1.1查看进程、拼接、导出、执行 1.2常见错误解决方案 2.关于时区 3.内存占用优化 记录一下生产过程中的一些场景和命令使用方法&#xff0c;不定期进行更新 1. Mysql批量Kill删除processlist 1.1查看进程、拼接、导出、执行 sho…

【AIPOD案例操作教程】斜流风扇轮毂优化

AIPOD是由天洑软件自主研发的一款通用的智能优化设计软件&#xff0c;致力于解决能耗更少、成本更低、重量更轻、散热更好、速度更快等目标的工程设计寻优问题。针对工业设计领域的自动化程度低、数值模拟计算成本高等痛点&#xff0c;基于人工智能技术、自研先进的智能代理学习…

Tableau/Power BI 是什么

目录 1. Tableau 是什么?1.1. Tableau 组件1.2. Tableau 的优劣势 2. Power BI2.1. Power BI 的组成部分2.2. Power BI 概念2.2.1. 容量2.2.2. 工作区2.2.3. 数据集2.2.4. 共享数据集2.2.5. 报表2.2.6. 仪表板2.2.7. 模板应用 1. Tableau 是什么? Tableau 是 Tableau 公司推…

前端架构师之01_ES6_基础

1 初识ES6 简单来说&#xff0c;ECMAScript是JavaScript语言的国际标准&#xff0c;JavaScript是实现ECMAScript标准的脚本语言。 2011年&#xff0c;ECMA国际标准化组织在发布ECMAScript 5.1版本之后&#xff0c;就开始着手制定第6版规范。 存在的问题&#xff1a;这个版本…

linux部署页面内容

/bin&#xff1a;该目录包含了常用的二进制可执行文件&#xff0c;如ls、cp、mv、rm等等。 /boot&#xff1a;该目录包含了启动Linux系统所需的文件&#xff0c;如内核文件和引导加载程序。 /dev&#xff1a;该目录包含了所有设备文件&#xff0c;如硬盘、光驱、鼠标、键盘等等…

新手十分钟也能完成的Unity小游戏打砖块

由Siki学院打砖块游戏启发完成一个非常非常简单&#xff0c;纯新手也能十分钟做出来的小游戏——打砖块。 一.搭建场景 首先我们先在一个空白的3D项目中创建一个Plane平面&#xff0c;将其放置于世界中央位置&#xff0c;长宽设置为2&#xff0c;并为其添加一个材质Material&am…

二十,镜面IBL--打印BRDF积分贴图

比起以往&#xff0c;这节应该是最轻松的了&#xff0c; 运行结果如下 代码如下&#xff1a; #include <osg/TextureCubeMap> #include <osg/TexGen> #include <osg/TexEnvCombine> #include <osgUtil/ReflectionMapGenerator> #include <osgDB/Re…

挂件板死机刷固件

用ESP32-DevKitC_V4刷固件的工具flash_download_tool_3.9.5.exe 挂件板子端口接线依次为V&#xff08;接3V3&#xff09;、R&#xff08;接TXD&#xff09;、T&#xff08;接RXD&#xff09;、G&#xff08;接GND&#xff09;、L&#xff08;悬空&#xff09; 1.选择ESP8266&…

Redis hash 命令总结

redis hash命令总结 命令 描述 示例 注意 HSET HSET用于将散列表key中的field的值设置为value。 HSET key field value 返回值&#xff1a;如果散列表key中的field不存在并且设置成功&#xff0c;则返回1&#xff1b;如果散列表key中的field已经存在并且新值覆盖了旧值&…

关于TUM数据集

2、验证回环检测算法&#xff0c;需要有人工标记回环的数据集。然而人工标记回环是很不方便的&#xff0c;我们会考虑根据标准轨迹计算回环。即&#xff0c;如果轨迹中有两个帧的位姿非常相近&#xff0c;就认为它们是回环。请根据TUM数据集给出的标准轨迹&#xff0c;计算出一…

MySQL学习笔记19

MySQL日志文件&#xff1a;MySQL中我们需要了解哪些日志&#xff1f; 常见日志文件&#xff1a; 我们需要掌握错误日志、二进制日志、中继日志、慢查询日志。 错误日志&#xff1a; 作用&#xff1a;存放数据库的启动、停止和运行时的错误信息。 场景&#xff1a;用于数据库的…

BACnet/IP协议采集网关支持modbus RTU采集

楼宇自动化在现代建筑中扮演着重要的角色&#xff0c;它可以集成和控制各种设备和系统&#xff0c;提高建筑的能效和舒适性。然而&#xff0c;不同的设备和系统通常使用不同的通信协议&#xff0c;这给楼宇自动化的实施带来了一定的挑战。为了解决这个问题&#xff0c;BACnet和…

面试问到MySQL模块划分与架构体系怎么办

面试问到Mysql模块划分与架构体系怎么办 文章目录 1. 应用层连接管理器&#xff08;Connection Manager&#xff09;安全性和权限模块&#xff08;Security and Privilege Module&#xff09; 2. MySQL服务器层2.1. 服务支持和工具集2.2. SQL Interface2.3. 解析器举个解析器 …

vuepress+gitee免费搭建个人在线博客(无保留版)

文章目录 最终效果&#xff0c;一睹为快&#xff01;一、工具选型二、什么是VuePress三、准备工作3.1 node 安装3.2 Git安装3.3 Gitee账号注册 四、搭建步骤4.1 初始化VuePress4.2 安装VuePress4.3 初始化目录4.4 编写文章 五、部署到Gitee5.1 创建仓库5.2 个人空间地址设置4.3…

1.vue3脚手架在vscode下面建立

一、下载安装node.js Node.js (nodejs.org) 二、安装vue3脚手架 #添加项目脚手架 npm install -g vue/cli 三、建立项目 #项目建立 vue create {appname} 测试项目安装成功&#xff1a;运行npm run serve命令 npm run serve 证明脚手架、项目环境已配置好 四、添加配件&#x…

高光时刻丨极智嘉斩获2023中国物流与采购联合会科学技术一等奖

不久前&#xff0c;中国物流与采购联合会宣布了2022年度科学技术奖获奖名单&#xff0c;其中包括了一项令人瞩目的成就。这项成就源自于极智嘉与国药物流、南京医药、九州通医药以及多所高校的合作&#xff0c;他们共同努力&#xff0c;成功研究并应用了一项关键技术&#xff0…

26531-2011 地理标志产品 永春老醋

声明 本文是学习GB-T 26531-2011 地理标志产品 永春老醋. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 1 范围 本标准规定了永春老醋地理标志产品的术语和定义、保护范围、产品分级、要求、试验方法、检验规 则和标签、标志、包装、运输、贮存的要…