手写ORM持久层框架(针对单表)

技术要求:

  • 技术栈: (1)java+mysql+反射+自定义注解+泛型+jdbc.
  • 持久层框架: 与数据库交互的一层称为持久层(dao)。完成orm操作。
  • o:(Object对象) r:(relative关系) m:(mapping映射)。 实体类---数据库表 属性--表的字段 实体类对象--一条记录 集合---表中多条记录。
  • 手撕持久层框架: 自己编写持久层框架 可以完成无需写sql语句即可完成对单表的CRUD操作。

一、创建一个空的 maven 工程 及引入 相关的依赖

二、pom 的依赖

<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
  <version>8.0.33</version>
</dependency>

<dependency>
  <groupId>com.alibaba</groupId>
  <artifactId>druid</artifactId>
  <version>1.2.16</version>
</dependency>

<dependency>
  <groupId>org.projectlombok</groupId>
  <artifactId>lombok</artifactId>
  <version>1.18.28</version>
</dependency>

三、创建数据源的属性文件

四、创建 DbUtil 工具类(用于连接数据库)【这里使用静态方法方便在 dao 中直接调用】

public class DbUntil {
    /**
     * 数据源实例,用于获取数据库连接。
     */
    private static final DataSource dataSource;

    static {
        // 加载druid配置文件
        Properties props = new Properties();
        InputStream inputStream = DbUntil.class.getClassLoader().getResourceAsStream("druid.properties");
        try {
            props.load(inputStream);
            // 根据配置文件创建Druid数据源实例
            dataSource = DruidDataSourceFactory.createDataSource(props);
        } catch (Exception e) {
            // 配置文件加载或数据源创建失败时,抛出运行时异常
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取数据库连接。
     *
     * @return 数据库连接
     */
    public static Connection getConnection(){
        try {
            // 从数据源获取连接
            return dataSource.getConnection();
        } catch (Exception e) {
            // 获取连接失败时,抛出运行时异常
            throw new RuntimeException(e);
        }
    }

    /**
     * 关闭数据库连接和相关资源。
     */
    public static void close(Connection connection, PreparedStatement ps, ResultSet...rs){
        try {
            // 检查并关闭数据库连接
            if(connection != null){
                connection.close();
            }
            // 检查并关闭PreparedStatement
            if(ps != null){
                ps.close();
            }
            // 遍历并关闭所有的ResultSet实例
            for (ResultSet resultSet : rs) {
                if(resultSet != null){
                    resultSet.close();
                }
            }
        }catch (Exception e) {
            // 捕获并抛出运行时异常,以便调用者可以对其进行适当处理
            throw new RuntimeException(e);
        }
    }
}

五、操作

1、通用的添加操作

 /**
     * 插入数据方法
     * 通过反射机制获取对象的字段信息,构建SQL语句,然后执行SQL语句插入数据。
     *
     * @param t 要插入的数据对象
     * @return 执行SQL语句的结果
     */
    public int insert(T t) {
        // 构建SQL语句的起始部分
        StringBuilder sb = new StringBuilder("insert into ");

        // 这里掉用封装好的 获取表名方法
        sb.append(getTableName()+" ");

        // 用于存储字段名和字段值的列表
        ArrayList<String> fields = new ArrayList<>();
        ArrayList<String> values = new ArrayList<>();

        // 获取当前类的所有字段
        Field[] declaredFields = tClass.getDeclaredFields();
        for (Field f : declaredFields){
            // 设置字段可访问
            f.setAccessible(true);

            // 跳过注有ColumnsKey注解的字段
            if (f.getAnnotation(ColumnsKey.class) != null){
                continue;
            }

            // 处理字段名,优先使用ColumnsName注解指定的字段名,否则使用字段本身的名称
            if (f.getAnnotation(ColumnsName.class) != null){
                fields.add(f.getAnnotation(ColumnsName.class).value());
            }else {
                fields.add(f.getName());
            }

            try {
                // 将字段值转换为字符串形式,并添加到values列表中
                values.add("'"+f.get(t).toString()+"'");
            } catch (IllegalAccessException e) {
                // 抛出运行时异常
                throw new RuntimeException(e);
            }
        }

        // 将字段名和字段值列表转换为SQL语句中的格式
        sb.append(fields.toString().replace("[","(").replace("]", ")"));
        sb.append(" values ");
        sb.append(values.toString().replace("[","(").replace("]", ")"));

        // 输出构建的SQL语句
        System.out.println(sb);

        // 执行SQL语句,并返回执行结果
        return executeSql(sb.toString());
    }
    // 获取表名   这里使用的构造方法 不用传入 t 对象即可 获取到表名
    private String getTableName(){
        // 尝试获取tClass上的TableName注解,如果存在则返回注解中的表名
        TableName annotation = tClass.getAnnotation(TableName.class);
        if (annotation != null){
            return annotation.value();
        }else {
            // 如果没有TableName注解,则返回类的简单类名作为表名
            return tClass.getSimpleName();
        }
    }

    // 执行 sql 语句
    private int executeSql(String sql){
        Connection connection = DbUntil.getConnection();
        try {
            PreparedStatement ps = connection.prepareStatement(sql);
            return ps.executeUpdate();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

2、通用的修改操作

public int update(T t) {
        StringBuilder sb = new StringBuilder("update ");
        sb.append(getTableName()+" ");
        sb.append("set ");
        String key = null;
        String value = null;
        Field[] declaredFields = tClass.getDeclaredFields();
        for (Field f : declaredFields){
            f.setAccessible(true);
            if (f.getAnnotation(ColumnsKey.class) != null){
                key = f.getAnnotation(ColumnsKey.class).value();
                try {
                    value = f.get(t).toString();
                }catch (Exception e){
                    throw new RuntimeException(e);
                }
                continue;
            }
            if(f.getAnnotation(ColumnsName.class) != null){
                try {
                    sb.append(f.getAnnotation(ColumnsName.class).value()+" = '"+f.get(t)+"',");
                } catch (IllegalAccessException e) {
                    throw new RuntimeException(e);
                }
            }else {
                try {
                    sb.append(f.getName()+" = '"+f.get(t)+"',");
                }catch (IllegalAccessException e){
                    throw new RuntimeException(e);
                }
            }
        }
        System.out.println(sb);
        sb.deleteCharAt(sb.length()-1);
        sb.append(" where ");
        sb.append(key+" = '"+value+"'");
        System.out.println(sb);
        return executeSql(sb.toString());
    }

3、通用的删除操作

public int delete(int id) {
        StringBuilder sb = new StringBuilder("delete from ");
        sb.append(getTableName()+" ");
        sb.append("where ");
        Field[] declaredFields = tClass.getDeclaredFields();
        for (Field f : declaredFields){
            f.setAccessible(true);
            if (f.getAnnotation(ColumnsKey.class) != null){
                try {
                    sb.append(f.getName()+" = "+id);
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
        }
        System.out.println(sb);
        return executeSql(sb.toString());
    }

4、通用的查询全部操作

// 查询全部的方法
    public List<T> select(){
        try {
            // 这使用一个查询的工具类
            return selectUtil();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

5、通用的查询单条数据操作

// 条件查询啊全部的方法
    public T selectById(int id) {
        try {
            T t = tClass.newInstance();
            List<T> ts = selectUtil(id);
            for (T t1 :ts){
                t=t1;
            }
            return t;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

上面查询单条和多条数据都使用了一个工具方法

 // 查询 单条数据和 多条数据的 工具方法
    private List<T> selectUtil(Integer... id){
        StringBuilder sql = new StringBuilder("select * from ");
        // 获取 表名
        sql.append(getTableName());

        if (id != null && id.length > 0){
            sql.append(" where ");
            Field[] fields = tClass.getDeclaredFields();
            for (Field f : fields){
                f.setAccessible(true);
                if (f.getAnnotation(ColumnsKey.class) != null){
                    sql.append(f.getName()+"='" + id[0] + "'");
                }
            }
        }

        ResultSet resultSet = null;
        Connection connection = DbUntil.getConnection();
        PreparedStatement ps = null;
        // 用于存放结果的集合
        List<T> list = new ArrayList<>();
        try {
            ps = connection.prepareStatement(sql.toString());
            resultSet = ps.executeQuery();
            // 结果 循环 遍历装入到 集合中
            while (resultSet.next()){
                T tt = (T) tClass.newInstance();
                Field[] declaredFields = tClass.getDeclaredFields();
                for (Field f : declaredFields){
                    if (f.getAnnotation(ColumnsKey.class) != null){
                        String value = f.getAnnotation(ColumnsKey.class).value();
                        f.setAccessible(true);
                        f.set(tt,resultSet.getObject(value));
                    }else {
                        if (f.getAnnotation(ColumnsName.class) != null){
                            String value = f.getAnnotation(ColumnsName.class).value();
                            f.setAccessible(true);
                            f.set(tt,resultSet.getObject(value));
                        }else {
                            f.setAccessible(true);
                            f.set(tt,resultSet.getObject(f.getName()));
                        }
                    }
                }
                list.add(tt);
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            DbUntil.close(connection, ps, resultSet);
        }
        return list;
    }

思想:

通过是否传入参数来区别 是 查询全部 还是单条数据 这里 七 八 可以看成是一个中转的方法,来限制使用者只能选择传入一个 参数 或 使用查询全部的方法 并将此工具方法 私有化 使其不能被 继承

  • 15
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值