手写orm框架

一、ORM介绍


ORM(Object-Relationl Mapping)对象关系映射,一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术。它的作用是在关系型数据库和对象之间作一个映射.。

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

 

二、建一个maven的java工程

在idea里面创建一个maven工程

工程结构

引入依赖

<dependencies>
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <version>8.0.33</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.8</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.26</version>
        </dependency>
    </dependencies>

创建数据源的属性文件

#配置数据源信息
driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
username=root
password=root

创建DbUtil工具类

package com.zql.util;

import com.alibaba.druid.pool.DruidDataSourceFactory;

import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;

public class DbUtil {
    //数据源对象
    private  static DataSource dataSource;
    static {
        try {
            //创建一个属性对象
            Properties properties = new Properties();
            //加载属性文件
            InputStream inputStream = DbUtil.class.getClassLoader().getResourceAsStream("db.properties");
            properties.load(inputStream);
           //获取连接池对象
            dataSource= DruidDataSourceFactory.createDataSource(properties);
        } catch (Exception e) {
           e.printStackTrace();
        }
    }
    /**
     * 获取数据库连接。
     *
     * 本方法通过调用dataSource对象的getConnection方法来获取一个数据库连接。
     * 使用者不需要直接与数据库连接细节交互,而是通过调用此方法来获取连接,
     * 降低了代码与特定数据库实现的耦合度,提高了代码的可维护性和可扩展性。
     *
     * @return Connection 返回一个数据库连接对象。
     * @throws Exception 如果获取连接过程中出现错误,将抛出异常。
     */
    public static  Connection getConnection() throws Exception{
        Connection coon= dataSource.getConnection();
        return  coon;
    }
    /**
     * 关闭数据库连接及相关资源。
     *
     * 该方法用于关闭 JDBC 中的 Connection、PreparedStatement 和 ResultSet 对象。
     * 它们在使用后应该被关闭,以释放与数据库的连接和资源。
     * 方法通过检查每个对象是否为 null,避免了空指针异常。
     * 如果关闭资源时发生 SQLException,方法会将其转换为 RuntimeException 抛出。
     * 这种做法是为了确保资源始终被关闭,同时将数据库层面的异常转换为运行时异常,
     * 以便调用者可以更方便地处理或记录异常。
     *
     * @param conn 需要关闭的数据库连接对象。
     * @param ps 需要关闭的 PreparedStatement 对象。
     * @param rs 需要关闭的 ResultSet 对象。
     */
    //释放资源
    public static void closeAll(Connection conn, PreparedStatement ps, ResultSet rs) {
        // 优先关闭 ResultSet,因为它可能依赖于 PreparedStatement 的状态。
        if(rs!=null){
            try {
                rs.close();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
        // 接着关闭 PreparedStatement。
        if(ps!=null){
            try {
                ps.close();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
        // 最后关闭 Connection。
        if(conn!=null){
            try {
                conn.close();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
    }

}

通用的添加/修改/删除

package com.zql.dao;

import com.zql.annotation.TableField;
import com.zql.annotation.TableId;
import com.zql.annotation.TableName;
import com.zql.util.DbUtil;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.util.ArrayList;
import java.util.List;

public class BaseDao<T>{
    /**
     * 在数据库中插入一个T类型的实体对象。
     * <p>
     * 此方法首先构造SQL插入语句,然后通过反射获取实体类的属性信息,
     * 包括表名、列名和对应的属性值。如果实体类的字段上有特定注解,
     * 则会根据注解调整列名。最后,执行SQL语句并返回影响行数。
     *
     * @param t 待插入的实体对象,必须是T类型的实例,其中T代表一个映射到数据库表的类。
     *          对象的属性将被用来构建SQL语句的列名和值。
     * @return 返回执行SQL语句后影响的行数,成功插入时通常为1。
     * @throws Exception 当数据库连接、预编译SQL语句或执行SQL语句时可能出现异常。
     */
    public int insert(T t) throws Exception {
        // 1. 构建SQL字符串,初始为"insert into"
        StringBuffer sql = new StringBuffer("insert into ");

        // 2. 获取实体类的反射类,用于后续获取表名和属性信息
        Class<?> aClass = t.getClass();

        // 3. 获取表名,优先考虑@TableName注解提供的值
        String tableName = aClass.getSimpleName(); // 默认使用类名作为表名
        TableName annotation = aClass.getAnnotation(TableName.class);
        if (annotation != null) {
            tableName = annotation.value();
        }
        sql.append(tableName);

        // 4-5. 遍历实体类的所有属性,收集列名和对应的值
        List<String> columnNames = new ArrayList<>();
        List<Object> values = new ArrayList<>();
        Field[] declaredFields = aClass.getDeclaredFields();
        for (Field field : declaredFields) {
            String name = field.getName(); // 获取属性名
            // 跳过@TableId注解的字段
            TableId tableIdAnnotation = field.getAnnotation(TableId.class);
            if (tableIdAnnotation != null) {
                continue;
            }
            // 根据@TableField注解调整列名
            TableField fieldAnnotation = field.getAnnotation(TableField.class);
            if (fieldAnnotation != null) {
                name = fieldAnnotation.value();
            }
            field.setAccessible(true);
            Object value = field.get(t); // 获取属性值
            values.add("'"+value+"'"); // 将值包装为字符串形式
            columnNames.add(name);
        }
        // 6. 将收集的列名和值转换为SQL语法格式
        String columns = columnNames.toString().replace("[", "(").replace("]", ")");
        String valuesStr = values.toString().replace("[", "(").replace("]", ")");

        // 7. 执行SQL语句
        Connection conn = DbUtil.getConnection();
        PreparedStatement ps = conn.prepareStatement(sql.append(columns).append("value").append(valuesStr).toString());
        int i = ps.executeUpdate();
        return i;
    }
    /**
     * 更新指定对象T在数据库中的记录。
     * 此函数首先构造SQL更新语句,通过反射获取对象的字段信息,
     * 并根据@TableField和@TableId注解来处理主键和列名。
     *
     * @param t 需要更新的对象,必须包含@Table和@TableField注解
     *           以指示对应的数据库表名和列名
     * @return 更新操作影响的行数,成功则返回大于0的值,失败或无影响则返回0
     * @throws Exception 可能抛出的异常,包括但不限于SQLException、IllegalAccessException
     */
    public int update(T t) throws Exception {
        // 构建SQL更新语句
        StringBuffer sql = new StringBuffer("update ");

        // 获取对象的类,从而得到表名
        Class<?> aClass = t.getClass();
        String tableName = aClass.getSimpleName();
        TableName annotation = aClass.getAnnotation(TableName.class);
        if (annotation != null) {
            tableName = annotation.value();
        }
        sql.append(tableName + " set ");

        // 添加WHERE子句的前缀
        String where = " where ";

        // 遍历对象的所有字段
        Field[] declaredFields = aClass.getDeclaredFields();
        for (Field field : declaredFields) {
            // 获取字段名
            String name = field.getName();

            // 处理@TableField注解,用于自定义列名
            TableField fieldAnnotation = field.getAnnotation(TableField.class);
            if (fieldAnnotation != null) {
                name = fieldAnnotation.value();
            }

            // 设置字段访问权限并获取字段值
            field.setAccessible(true);
            Object value = field.get(t);

            // 处理@TableId注解,用于主键条件
            TableId tableId = field.getAnnotation(TableId.class);
            if (tableId != null) {
                String tableIdName = tableId.value();
                where += tableIdName + "='" + value + "'";
                continue;
            }

            // 添加列名=值的组合到SQL语句中
            sql.append(name + "='" + value + "',");
        }

        // 删除SQL语句末尾多余的逗号
        sql.deleteCharAt(sql.length() - 1).append(where);

        // 获取数据库连接并准备执行SQL语句
        Connection conn = DbUtil.getConnection();
        PreparedStatement ps = conn.prepareStatement(sql.toString());

        // 执行更新操作并返回影响的行数
        return ps.executeUpdate();
    }

    //删除框架
    private Class<T> clazz;
    public BaseDao(){
        //this表示子类Dao对象
        Class<? extends BaseDao> aClass = this.getClass();
        //获取当前子类的父类的反射类
        ParameterizedType genericSuperclass =(ParameterizedType)aClass.getGenericSuperclass();
       //获取该反射类中的泛型类型
        Type actualTypeArguments = genericSuperclass.getActualTypeArguments()[0];
        clazz=(Class)actualTypeArguments;
    }
    //delete from 表名 where 主键列名=主键值
    public int delete(Object id) throws Exception{
        StringBuffer sql = new StringBuffer("delete from ");
        String tableName = clazz.getSimpleName();//实体类的名称
        TableName annotation = clazz.getAnnotation(TableName.class);
        if(annotation!=null){
            tableName = annotation.value();
        }
        sql.append(tableName+" where ");
        Field[] declaredFields = clazz.getDeclaredFields();
        for (Field field: declaredFields) {
            TableId tableId = field.getAnnotation(TableId.class);
            if(tableId!=null){
                sql.append(tableId.value()+"="+id);
                break;
            }
        }
        //执行sql语句
        Connection conn = DbUtil.getConnection();
        PreparedStatement ps = conn.prepareStatement(sql.toString());
        int i = ps.executeUpdate();
        return i;
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值