自定义ORM框架

手撕ORM框架

1.创建Maven工程

(https://img2024.cnblogs.com/blog/3475901/202407/3475901-20240711102718483-1421959314.png)
)

2.导入依赖

    <dependencies>
        <!-- 引入 jdbc 的依赖 -->
        <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.8</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.24</version>
        </dependency>
    </dependencies>

3.设计操作数据库的工具类

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();
        }
    }

    public static Connection getConnection(){
        try {
         Connection connection = dataSource.getConnection();
         return connection;
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
        }
        return null;
    }

    public static void closeAll(Connection connection, PreparedStatement preparedStatement, ResultSet resultSet){
        if(resultSet != null){
            try {
                resultSet.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
        if(preparedStatement != null){
            try {
                preparedStatement.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
        if (connection != null){
            try {
                connection.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
    }
}

4.创建配置文件 db.properties

#数据源信息
url=jdbc:mysql://localhost:3306/demo?serverTimezone=Asia/Shanghai
username=root
password=123456
driverClass=com.mysql.cj.jdbc.Driver

5.设计自定义注解

// 修饰表名
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TableName {
    String value();
}
// 修饰主键Id
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TableId {
    String value() default "id";
}
// 修饰表字段
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TableField {
    String value();
}

6.实体类中使用自定义注解

@TableName("表名")
@Data
public class Student {
    @TableId("主键名")
    private Integer id;
    @TableField("列名")
    private String name;
    @TableField("列名")
    private Integer age;
}

7.设计BaseDao

public class BaseDao <T> {
    private Class<T> clazz;

    /**
     * 获取泛型的反射实例
     */
    public BaseDao(){
        // 获取this的反射
        Class<? extends BaseDao> aClass = this.getClass();
        // 获取当前子类的父类反射
        ParameterizedType genericSuperclass = (ParameterizedType) aClass.getGenericSuperclass();
        // 获取反射类中的泛型
        Type actualTypeArgument = genericSuperclass.getActualTypeArguments()[0];
        // 将获取到的泛型类型经过强转后给 clazz
        clazz = (Class) actualTypeArgument;
    }

    /**
     * 插入
     * @param t
     * @return
     */
    public int insert(T t) {
        try{
            // 设计通用的添加
            // 1.首先写出添加的 sql insert into 表名(字段名) values(属性名)
            // 这里注意 into 后面的空格
            StringBuffer sql = new StringBuffer("insert into ");
            // 2.获取实体类所对应的表名
            // 2.1自定义注解 @TableName 标识实体类所对应的表
            // 2.2拿到实体类的反射类,利用反射获取注解,进而获得表的名称
            Class clazz = t.getClass();
            // 实体类的名称
            String simpleName = clazz.getSimpleName();
            // 表的名称
            String tableName = "";
            // 获取类上的注解
            TableName annotation = (TableName) clazz.getAnnotation(TableName.class);
            if(annotation != null){
                tableName = annotation.value();
            }
            // 追加拿到的表名
            sql.append(tableName);
            // 3.获取所有的列名和对应的属性名
            // 3.1用来存储数据库中对应的列
            List<String> columnNames = new ArrayList<String>();
            // 3.2用来存储需要插入的值
            List<Object> values = new ArrayList<Object>();
            // 4.获取所有的属性对象
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                // 4.1获取类中的属性名
                String name = field.getName();
                // 4.2获取属性上的注解
                // 获取主键的注解
                TableId tableId = field.getAnnotation(TableId.class);
                // 获取成员的注解
                TableField tableField = field.getAnnotation(TableField.class);
                // 4.3根据拿到的值进行过滤
                if(tableId != null){
                    // 4.3.1 此时拿到的是主键的注解 退出本轮循环
                    continue;
                }
                if(tableField != null){
                    // 4.3.2 此时拿到的是成员的注解
                    // 覆盖掉类中的属性名 使用表中的字段名
                    name = tableField.value();
                }
                // 对私有属开启暴力反射
                field.setAccessible(true);
                // 获取对象属性对应的值
                Object value = field.get(t);
                // 将值经过添加引号后加入到值list中
                values.add("'"+value+"'");
                // 将属性列加入到列list中
                columnNames.add(name);
            }
            // 5.将列list转换成字符串,然后使用字符串替换方法将其转换成需要的格式
            String columnList = columnNames.toString().replace("[","(").replace("]", ")");
            // 6.将值list转换成字符串,然后使用字符串替换方法将其转换成需要的格式
            String valueList = values.toString().replace("[","(").replace("]",")");
            // 7.合并字符串
            sql.append(columnList + " values " + valueList);
            System.out.println(sql);
            // 8.执行插入语句
            Connection connection = DbUtil.getConnection();
            PreparedStatement preparedStatement = connection.prepareStatement(sql.toString());
            int i = preparedStatement.executeUpdate();
            return i;
        } catch (Exception e){
            e.printStackTrace();
        }
        return 0;
    }

    /**
     * 根据主键更新
     * @param t
     * @return
     */
    public int update(T t){
        try{
            //设计通用的修改
            // 1.设计 sql update 表名 set 属性名=值,...
            StringBuffer sql = new StringBuffer("update ");
            // 2.获取表名
            // 2.1获取对象的反射类实例
            Class clazz = t.getClass();
            // 2.2拿到对象对应的表名 类的简名
            String tableName = clazz.getSimpleName();
            TableName tableNameAnnotation = (TableName) clazz.getAnnotation(TableName.class);
            if (tableNameAnnotation != null){
                // 2.3拿到表的名称 使用注解的名称进行覆盖
                tableName = tableNameAnnotation.value();
            }
            // 3.将拿到的表名追加到sql后面
            sql.append(tableName + " set ");
            // 4.设置条件部分
            String where = "where ";
            // 5.获取所有的列对象
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                // 5.1获取所有的属性名
                String name = field.getName();
                // 5.2获取属性上的注解
                // 5.2.1获取主键的注解
                TableId tableId =(TableId) field.getAnnotation(TableId.class);
                // 5.2.2获取属性的注解
                TableField tableField =(TableField) field.getAnnotation(TableField.class);
                // 6.设置私有属性允许访问
                field.setAccessible(true);
                // 7.获取属性的值
                Object value = field.get(t);
                // 根据注解的种类进行选择性拼接
                if(tableId != null){
                    String id = tableId.value();
                    where += id + "='" + value + "'";
                    // 拼接完成退出本轮循环
                    continue;
                }
                if(tableField != null){
                    name = tableField.value();
                }
                sql.append(name + "='" + value + "',");
            }
            // 去除 sql 中最后一个 ,
            sql.deleteCharAt(sql.length() - 1).append(where);
            System.out.println(sql);
            // 执行 sql 语句
            Connection connection = DbUtil.getConnection();
            PreparedStatement preparedStatement = connection.prepareStatement(sql.toString());
            int i = preparedStatement.executeUpdate();
            return i;
        } catch (Exception e){
            e.printStackTrace();
        }
        return 0;
    }

    /**
     * 根据 Id 删除
     * @param id
     * @return
     */
    public int deleteById(Object id){
        try {
            // 设计删除的 sql 语句
            StringBuffer sql = new StringBuffer("delete from ");                       
            // 获取实体类的名称
            String tableName = clazz.getSimpleName();
            // 获取表名的注解
            TableName tableNameAnnotation = clazz.getAnnotation(TableName.class);
            // 判断是否添加了@TableName注解
            if(tableNameAnnotation != null){
                tableName = tableNameAnnotation.value();
            }
            // 追加 where 条件
            sql.append(tableName + " where ");                                        
            // 获取所有的属性对象
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                TableId tableId =(TableId) field.getAnnotation(TableId.class);
                if(tableId != null){
                    sql.append(tableId.value() + "=" + id);                           
                    // 跳出循环
                    break;
                }
            }
            // 执行 sql
            Connection connection = DbUtil.getConnection();
            PreparedStatement preparedStatement = connection.prepareStatement(sql.toString());
            int i = preparedStatement.executeUpdate();
            return i;
        } catch (Exception e){
            e.printStackTrace();
        }
        return 0;
    }

    /**
     * 根据 Id 查询
     * @param id
     * @return
     */
    public T selectById(Object id) {
        // 设计 sql
        StringBuffer sql = new StringBuffer("select * from ");
        // 获取表名
        // 获取类的简名
        String tableName = clazz.getSimpleName();
        // 获取TableName注解
        TableName tableNameAnnotation = clazz.getAnnotation(TableName.class);
        if(tableNameAnnotation != null){
            tableName = tableNameAnnotation.value();
        }
        // 拼接上表名
        sql.append(tableName + " where ");
        // 获取主键的列名
        Field[] declaredFields = clazz.getDeclaredFields();
        // 遍历
        for (Field declaredField : declaredFields) {
            TableId tableId = declaredField.getAnnotation(TableId.class);
            if(tableId != null){
                sql.append(tableId.value() + " = " + id);
                break;
            }
        }
        // 执行 sql 语句
        Connection connection = DbUtil.getConnection();
        // 获取 PreparedStatement 对象
        PreparedStatement preparedStatement = null;
        try {
            preparedStatement = connection.prepareStatement(sql.toString());
            // 获取结果集
            ResultSet resultSet = preparedStatement.executeQuery();
            // 封装到实体类
            while(resultSet.next()){
                T t = clazz.newInstance();
                for (Field field : declaredFields) {
                    field.setAccessible(true);
                    // 获取带有主键注解的列
                    TableId tableId = field.getAnnotation(TableId.class);
                    // 获取带有表属性的列
                    TableField tableField = field.getAnnotation(TableField.class);
                    // 获取数据库列名 如果没有注解就默认使用属性名字
                    String name = field.getName();
                    if(tableId != null){
                        name = tableId.value();
                    }
                    if(tableField != null){
                        name = tableField.value();
                    }
                    // 获取数据库中指定列的值
                    Object o = resultSet.getObject(name);
                    // 为指定对象的属性赋值
                    field.set(t,o);
                }
                return t;
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 查询所有
     * @return
     */
    public List<T> selectAll(){
        // 存储查到的信息
        List<T> list = new ArrayList<>();
        // 设计 sql
        StringBuffer sql = new StringBuffer("select * from ");
        // 获取表名
        // 获取类的简名
        String tableName = clazz.getSimpleName();
        TableName tableNameAnnotation = clazz.getAnnotation(TableName.class);
        if(tableNameAnnotation != null){
            tableName = tableNameAnnotation.value();
        }
        sql.append(tableName);

        // 执行 sql 语句
        Connection connection = DbUtil.getConnection();
        try {
            PreparedStatement preparedStatement = connection.prepareStatement(sql.toString());
            // 获取结果集
            ResultSet resultSet = preparedStatement.executeQuery();
            Field[] declaredFields = clazz.getDeclaredFields();
            // 封装数据
            while(resultSet.next()){
                T t = clazz.newInstance();
                // 为实体类属性赋值
                for (Field field : declaredFields) {
                    // 开启私有属性访问
                    field.setAccessible(true);
                    TableId tableId = field.getAnnotation(TableId.class);
                    TableField tableField = field.getAnnotation(TableField.class);
                    // 获取属性名
                    String name = field.getName();
                    if(tableId != null){
                        name = tableId.value();
                    }
                    if(tableField != null){
                        name = tableField.value();
                    }
                    // 获取数据库中指定列的值
                    Object o = resultSet.getObject(name);
                    // 为指定对象的属性赋值
                    field.set(t,o);
                }
                // 将封装好的对象加入到集合中
                list.add(t);
            }
            return list;
        } catch (SQLException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
        return list;
    }
}

8.用户定义Dao使用BaseDao

public class StudentDao extends BaseDao <Student>{}
public class TeacherDao extends BaseDao <Teacher>{}

9.测试

public class Test {
    
    @org.junit.Test
    public void test01(){
        StudentDao studentDao = new StudentDao();
        Student student = new Student();
        student.setId(1);
        student.setName("李白");
        student.setAge(18);
        studentDao.insert(student);
    }
    
    @org.junit.Test
    public void test02(){
        // 测试更新
        StudentDao studentDao = new StudentDao();
        Student student = new Student();
        student.setId(5);
        student.setName("李白");
        student.setAge(30);
        studentDao.update(student);
    }
    
    @org.junit.Test
    public void test03(){
        // 测试删除
        StudentDao studentDao = new StudentDao();
        Student student = new Student();
        student.setId(1);
        studentDao.deleteById(student.getId());
    }

    @org.junit.Test
    public void test04(){
        // 测试根据 Id 查询
        StudentDao studentDao = new StudentDao();
        System.out.println(studentDao.selectById(5));
    }

    @org.junit.Test
    public void test05(){
        // 测试查询全部
        StudentDao studentDao = new StudentDao();
        System.out.println(studentDao.selectAll());
    }
}

posted @ 2024-07-02 21:15  李松燃  阅读(3)  评论(0编辑  收藏  举报