【Java基础】——手撕ORM框架(泛型+注解+反射)

1.技术栈: java+mysql+反射+自定义注解+泛型+jdbc.

2.持久层框架: 与数据库交互的一层称为持久层(dao)

3.o:(Object对象) r:(relative关系) m:(mapping映射)。 实体类---数据库表 属性--表的字段 实体类对象--一条记录 集合---表中多条记录。

4.手撕持久层框架: 自己编写持久层框架 可以完成无需写sql语句即可完成对单表的CRUD操作。

1.创建一个maven的java工程

2.引入相关依赖以及配置数据源的属性文件

        引入依赖:

<dependencies>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.30</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.16</version>
        </dependency>
    </dependencies>

        配置数据源信息:

driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/your
username=your
password=your

3.创建DbUtil工具类

DBUtil:获取连接对象和关闭连接资源

public class DBUtil {
    /**
     * 数据源,用于管理数据库连接。
     */
    private static DataSource dataSource;

    /**
     * 静态代码块,在类加载时初始化数据源。
     * 通过读取db.properties文件配置数据库连接参数,并使用Druid数据源。
     */
    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();
        }
    }

    /**
     * 获取数据库连接。
     * 
     * @return 数据库连接对象。
     * @throws Exception 如果获取连接失败抛出异常。
     */
    public static Connection getConnection() throws Exception {
        return dataSource.getConnection();
    }

    /**
     * 关闭数据库连接及相关资源。
     * 
     * @param conn 数据库连接对象。
     * @param ps 预编译的SQL语句对象。
     * @param rs 查询结果集对象。
     */
    public static void closeAll(Connection conn, PreparedStatement ps, ResultSet rs) {
        try {
            if (rs != null) rs.close();
            if (ps != null) ps.close();
            if (conn != null) conn.close();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
}

4.自定义注解  用于实体类

TableName:表明和实体类名不一致时使用

TableField:列名和属性名不一致时使用

TableId:列名和属性名不一致时使用          

/**
 * 定义一个字段级别的注解,用于标记实体类中的字段对应数据库表中的列名。
 * 
 * @Target(ElementType.FIELD) 表示该注解只能用于字段上。
 * @Retention(RetentionPolicy.RUNTIME) 表示该注解在运行时仍然有效,可被反射访问。
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TableField {
    /**
     * 返回数据库表中对应的列名。
     * 
     * @return 列名字符串。
     */
    String value();
}

/**
 * 定义一个字段级别的注解,用于标记实体类中的主键字段。
 * 默认情况下,如果未指定主键名,则认为主键名为"id"。
 * 
 * @Target(ElementType.FIELD) 表示该注解只能用于字段上。
 * @Retention(RetentionPolicy.RUNTIME) 表示该注解在运行时仍然有效,可被反射访问。
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TableId {
    /**
     * 返回数据库表中的主键列名,默认值为"id"。
     * 
     * @return 主键列名字符串。
     */
    String value() default "id";
}

/**
 * 定义一个类型级别的注解,用于标记实体类对应数据库表的名称。
 * 
 * @Target(ElementType.TYPE) 表示该注解只能用于类上。
 * @Retention(RetentionPolicy.RUNTIME) 表示该注解在运行时仍然有效,可被反射访问。
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TableName {
    /**
     * 返回数据库表的名称。
     * 
     * @return 表名字符串。
     */
    String value();
}

5.BaseDao:父类

如何获取类名中泛型T:

// this表示子类Dao对象
Class<? extends BaseDao> aClass = this.getClass();
// 获取当前子类的父类的反射类
ParameterizedType genericSuperclass = (ParameterizedType) aClass.getGenericSuperclass();
// 获取该反射类型中的泛型类型
Type actualTypeArgument = genericSuperclass.getActualTypeArguments()[0];
clazz = (Class) actualTypeArgument;

接下来是BaseDao中的CRUD操作:

public class BaseDao<T> {

    private Class<T> clazz;

    public BaseDao() {
        // this表示子类Dao对象
        Class<? extends BaseDao> aClass = this.getClass();
        // 获取当前子类的父类的反射类
        ParameterizedType genericSuperclass = (ParameterizedType) aClass.getGenericSuperclass();
        // 获取该反射类型中的泛型类型
        Type actualTypeArgument = genericSuperclass.getActualTypeArguments()[0];
        clazz = (Class) actualTypeArgument;
    }

    // 通用删除方法
    public int delete(Object id) throws Exception {
        //1.创建一个sql字符串
        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;
            }
        }
        Connection connection = DBUtil.getConnection();
        PreparedStatement ps = connection.prepareStatement(sql.toString());
        int i = ps.executeUpdate();
        return i;
    }

    // 通用添加方法
    public int insert(T t) throws Exception {
        //1.创建一个sql字符串
        StringBuffer sql = new StringBuffer("insert into ");
        //2. 获取实体类的反射类.
        Class<?> aClass = t.getClass();
        //3.获取表名
        String tableName = aClass.getSimpleName();//实体类的名称
        TableName annotation = aClass.getAnnotation(TableName.class);
        if (annotation != null) {
            tableName = annotation.value();
        }
        sql.append(tableName);
        //4.获取所有的列名--对于属性名
        List<String> columnNames = new ArrayList<>();
        List<Object> values = new ArrayList<>();
        //5. 获取所有的属性对象
        Field[] declaredFields = aClass.getDeclaredFields();
        for (Field field : declaredFields) {
            //获取属性名
            String name = field.getName();
            //获取属性上的注解
            TableId tableIdAnnotation = field.getAnnotation(TableId.class);
            TableField fieldAnnotation = field.getAnnotation(TableField.class);
            if (tableIdAnnotation != null) {
                continue;
            }
            if (fieldAnnotation != null) {
                name = fieldAnnotation.value();
            }
            field.setAccessible(true);
            //对象属性的值
            Object value = field.get(t);
            values.add("'" + value + "'");
            columnNames.add(name);
        }

        String replace = columnNames.toString().replace("[", "(").replace("]", ")");
        String replace1 = values.toString().replace("[", "(").replace("]", ")");
        sql.append(replace + " values " + replace1);
        //执行sql语句
        Connection conn = DBUtil.getConnection();
        PreparedStatement ps = conn.prepareStatement(sql.toString());
        int i = ps.executeUpdate();
        return i;
    }

    // 通用修改方法
    public int update(T t) throws Exception {
        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).append(" set ");
        String where = " where ";
        // 获取所有列对象
        Field[] declaredFields = aClass.getDeclaredFields();
        boolean hasId = false;
        for (Field field : declaredFields) {
            // 属性名
            String name = field.getName();
            TableField fieldAnnotation = field.getAnnotation(TableField.class);
            TableId tableId = field.getAnnotation(TableId.class);
            field.setAccessible(true);
            Object value = field.get(t);
            // 如果为主键注解
            if (tableId != null) {
                String tableIdName = tableId.value();
                where += tableIdName + "='" + value + "'";
                hasId = true;
                continue;
            }
            if (fieldAnnotation != null) {
                name = fieldAnnotation.value();
            }
            sql.append(name).append("='").append(value).append("',");
        }
        sql.deleteCharAt(sql.length() - 1).append(where);
        // 执行 SQL 语句
        Connection conn = DBUtil.getConnection();
        PreparedStatement ps = conn.prepareStatement(sql.toString());
        int i = ps.executeUpdate();
        return i;
    }

    // 根据ID查询
    public T findById(Object id) throws Exception {
        StringBuffer sql = new StringBuffer("select * from ");
        String tableName = clazz.getSimpleName();
        TableName annotation = clazz.getAnnotation(TableName.class);
        if (annotation != null) {
            tableName = annotation.value();
        }
        sql.append(tableName).append(" where ");
        Field[] declaredFields = clazz.getDeclaredFields();
        for (Field field : declaredFields) {
            TableId tableId = field.getAnnotation(TableId.class);
            if (tableId != null) {
                sql.append(tableId.value()).append("=").append(id);
                break;
            }
        }
        Connection conn = DBUtil.getConnection();
        PreparedStatement ps = conn.prepareStatement(sql.toString());
        ResultSet rs = ps.executeQuery();
        if (rs.next()) {
            T t = clazz.getDeclaredConstructor().newInstance();
            for (Field field : declaredFields) {
                field.setAccessible(true);
                Object value = rs.getObject(field.getName());
                field.set(t, value);
            }
            return t;
        }
        return null;
    }

    // 查询全部
    public List<T> findAll() throws Exception {
        List<T> resultList = new ArrayList<>();
        StringBuffer sql = new StringBuffer("select * from ");
        String tableName = clazz.getSimpleName();
        TableName annotation = clazz.getAnnotation(TableName.class);
        if (annotation != null) {
            tableName = annotation.value();
        }
        sql.append(tableName);
        Connection conn = DBUtil.getConnection();
        PreparedStatement ps = conn.prepareStatement(sql.toString());
        ResultSet rs = ps.executeQuery();
        Field[] declaredFields = clazz.getDeclaredFields();
        while (rs.next()) {
            T t = clazz.getDeclaredConstructor().newInstance();
            for (Field field : declaredFields) {
                field.setAccessible(true);
                Object value = rs.getObject(field.getName());
                field.set(t, value);
            }
            resultList.add(t);
        }
        return resultList;
    }
}

6.entity实体类

这里因为使用Lombok所以就没有getter和setter方法了,它可以通过注解自动生成 getter、setter、构造函数、equals 方法、hashCode 方法等常用方法。

@TableName(value = "tb_dept")
@Data
public class Dept {
    @TableId(value = "id")
    private Integer id;
    @TableField(value = "name")
    private String name;
}


@TableName(value = "tb_emp")
@Data
public class Emp {
    private Integer id;
    private String name;
    private Integer age;
    private String job;
    private Double salary;
    private Date entrydate;
    private Integer managerid;
    private Integer deptid;
    private String head;
}

7.测试

这里就偷了个懒,都写在一起了,接下来应该还有把项目打成jar,下期再写吧!

public class Test {

    public static void main(String[] args) throws Exception {
        DeptDao deptDao = new DeptDao();
        Dept dept = new Dept();

        List<Dept> result = deptDao.findAll();
        System.out.println("查询结果: " + result);

        Dept result2 = deptDao.findById(1);
        System.out.println("查询结果: " + result2);

        dept.setId(12);
        dept.setName("测试");
        int result3 = deptDao.insert(dept);
        System.out.println("插入结果: " + result3);

        dept.setId(12);
        dept.setName("测试2");
        int result4 = deptDao.update(dept);
        System.out.println("更新结果: " + result4);

        int result5 = deptDao.delete(12);
        System.out.println("删除结果: " + result5);
    }

}

欢迎大家评论区指出不足,一起学习进步!

  • 8
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值