自定义ORM框架

news/2024/7/4 15:02:01/文章来源:https://www.cnblogs.com/lsran/p/18280561

手撕ORM框架

1.创建Maven工程

image-20240702211254563

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表示子类 Dao对象Class<? extends BaseDao> aClass = this.getClass();// 获取当前子类的父类反射类ParameterizedType genericSuperclass = (ParameterizedType) aClass.getGenericSuperclass();// 获取该反射类中的泛型类型Type actualTypeArgument = genericSuperclass.getActualTypeArguments()[0];// 将获取得到的泛型类型经过强转后重新给 clazzclazz = (Class) actualTypeArgument;}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;}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;}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;}}// 执行 sqlConnection connection = DbUtil.getConnection();PreparedStatement preparedStatement = connection.prepareStatement(sql.toString());int i = preparedStatement.executeUpdate();return i;} catch (Exception e){e.printStackTrace();}return 0;}}

8.用户定义Dao使用BaseDao

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

9.测试

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/736512.html

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

相关文章

【esp32 学习笔记】将lvgl融入esp-idf项目中

lvgl科普 lvgl 主要特点:Github库整体了解版本号编排原则屏幕兼容性LVGL 问题处理: lvgl 与 FreeRTOS 由于esp-idf本身带了 FreeRTOS系统,因此需要关注一下操作系统相关的内容:void lvgl_thread(void) {while(1) {uint32_t time_till_next;time_till_next = lv_timer_hand…

QT6 CMake项目配置 (Visual Studio)

QT6 CMake项目配置 (Visual Studio) 上一节已经编译好了动态和静态的QT库,接下来在CMake中引入。 这边主要介绍使用Visual Studio的配置方法 测试环境 首先使用VS创建一个项目来测试CMake能否正常使用。 如果是首次打开VS会有个配置主题的界面,自己选一下就好了 首先我们来创…

ssrf+结合redis 写入crontab或者公钥(两种环境)

REDIS redis作为一种数据库 其实是会真的将数据写入到内存中的 我们利用ssrf请求 请求redis 实现服务器对自己的公钥或任务计划写入 实现无密码登录 或反弹bashredis监听所有地址时 才能被外部访问 否则只能127.0.0.1 本地访问 而且开启保护模式后会导致目标端口只能本地访问 这…

ffmpeg常用命令汇总

最近在学习ffmpeg,将基础命令做一次汇总,便于自己以后查阅: 1. ffmpeg 常用命令 ffmpeg 帮助信息查看 // 查看 ffmpeg 的基础信息。 ffmpeg --help// 查看高级参数部分。 ffmpeg --help long// 查看全部的帮助信息。 ffmpeg --help full转封装 // -hide_banner: 隐去 ffmpeg …

麻烦问一下xpath标签定位的这个索引是做什么用的?

大家好,我是Python进阶者。 一、前言 前几天在Python最强王者交流群【杨又串🍻】问了一个Python网络爬虫的问题,问题如下:老师,麻烦问一下xpath标签定位的这个索引是做什么用的,我听网课把这个知识点跳过了? 二、实现过程 后来【隔壁😼山楂】给了一个指导:这个过去出…

李沐动手学深度学习V2-chap_preliminaries

李沐动手学深度学习V2 文章内容说明 本文主要是自己学习过程中的随手笔记,需要自取 课程参考B站:https://space.bilibili.com/1567748478?spm_id_from=333.788.0.0 课件等信息原视频简介中有CSV文件修改读取成张量tensor 数据预处理 首先(创建一个人工数据集,并存储在CSV(…

C语言打印倒三角形,底边长n作为参数输入,从键盘输入

打印倒三角形,底边长n作为参数输入,从键盘输入。#include <stdio.h> int main(int argc, char const *argv[]) {int i, j, k, l, n;printf("请输入底边长: \n");scanf("%d", &n);while (getchar() != \n);printf("输出图形如下:\n"…

WebAPI项目框架JWT权限验证

JWT是什么?校验逻辑?授权过程?这里就不过多的阐述了,直接上代码 在appsettings.json中配置jwt参数的值 SecretKey必须大于16个字符1 {2 "Logging": {3 "LogLevel": {4 "Default": "Information",5 "Micros…

2.SpringBoot快速上手

2.SpringBoot快速上手 SpringBoot介绍javaEE的开发经常会涉及到3个框架Spring ,SpringMVC,MyBatis.但是这三个框架配置极其繁琐,有大量的xml文件,spring Boot对之前的配置进行极大的简化Spring Boot 是由Pivotal团队提供的基于Spring的全新框架,简化Spring应用的初始搭建和…

RTMP协议

RTMP(Real-Time Messaging Protocol)是一个综合性的协议,不仅可以传输音视频数据,还可以传输信令控制指令。RTMP 使用 TCP 作为传输协议,可以直接在 TCP 连接上传输音视频数据,也可以传输控制指令,实现了音视频流的实时传输和控制。 与RTSP 不同,RTSP(Real-Time S…

yarn install 时显示 node_modules\esbuild: Command failed

可以找一找你的nodejs安装路径是不是中文的,如果是中文的换成英文应该就可以了(记得系统变量里也要改掉)

阿里云个人账号 创建docker仓库

1、创建阿里云账号 2、搜索“容器镜像服务”,进入,点击“管理控制台” 3、选择个人实例,这一步可能会让你创建registry密码(如果没有创建过的话),后面登录需要 4、创建命名空间5、创建镜像仓库 输入信息,点击下一步,选择本地仓库 6、进入管理页面 执行相关命令

Django3在网页上生成二维码

1.安装依赖包pip install django-qr-code2.在django,你项目的settings中,安装app 打开你项目的setting,找到INSTALLED_APPS ,在这里新加一条qr_code INSTALLED_APPS = [...,rest_framework,qr_code,... ] 3.在你打算渲染的html文档中,导入模板 {% load qr_code %}ps:如果…

Hackthebox bagel.dll 代码审计

利用ilspy将bagel.dll打开关于此目录有可以说的内容 目录解析 最上方的bagel是组装名字(assemble name) bagel_server 是命令空间(namespace) 下一级分支是类如File,Base,Handler,Orders等(class) 反序列化导致的命令执行漏洞代码审计思路 首先看主程序Bagel 1.通过明显的英…

2024/7/2 T1

题意:分析: 记 \(S_{i}\) 表示目前第 \(i\) 个集合里的元素个数。 集合之间互不区分,强制钦定必须满足 \(S_{i} \le S_{i+1}(i<k)\)。 经搜索发现,这样的状态数量最多约为 \(1.8 \times 10^5\)。 极差可以这样处理:将 \(a\) 排序,\(S_{i}\) 第一次加入某个元素 \(x\),…

Eplan插件 - 矩形修订云线

前言 在 CAD 中,矩形云线一直是设计师们用于标注修订区域或突出重要部分的得力工具。然而,在 Eplan 中,没有直接绘制矩形云线的功能。为了填补这一空白,开发了专门用于Eplan的矩形修订云线插件。 这款插件保留了Eplan绘制的习惯,可以简洁快速的框选出需要修订或者重点关注…

Nginx配置以及热升级

目录Nginx详解1. Nginx关键特性2. Nginx配置2.1 event2.2 http2.2.1 log_format2.2.2 sendfile2.2.3 tcp_nopush2.2.4 tcp_nodelay2.2.5 keepalive_timeout2.2.6 include2.2.7 default_type2.2.8 server3. 配置Nginx虚拟主机3.1 基于端口3.2 基于IP3.3 基于域名4. Location4.1 …

Python3学习之路~3.4 作业之对员工信息表实现增删改查操作

有以下员工信息表当然此表你在文件存储时可以这样表示1,Alex Li,22,13651054608,IT,2013-04-01 现需要对这个员工信息文件,实现增删改查操作可进行模糊查询,语法至少支持下面3种: select name,age from staff_table where age > 22 select * from staff_table where dep…

Python3学习之路~2.11 补充:Python输出字符串加颜色or背景色

格式:\033[二位数;1m字符串\033[0m 二位数为31-37,40-47(其他数字无效),其中:十位数3表示字颜色,4表示背景色个位数0-7分别表示 黑 红 绿 黄 蓝 紫 浅蓝 灰 示例代码:# 加颜色:31-37 print("\033[31;1mHello Python-31-红色字\033[0m") print("\033[32;…

【触想智能】工业平板电脑在新能源领域上的应用分析

工业平板电脑是一种具有高性能和稳定性的计算机设备,适用于在恶劣环境下进行数据采集、运营管理和现场操作。随着新能源技术的快速发展,工业平板电脑不断地得到应用,并且已成为新能源领域中的重要工具之一。本文将从四个方面探讨工业平板电脑在新能源领域中的应用。一、智能…