当前位置: 首页 > news >正文

Java 反射与 MyBatis:ORM 框架的 “灵魂基石”

目录

一、ORM框架

1.1 什么是 ORM?       

1.2 反射在 ORM 中的角色

二、简化版 MyBatis:反射实现 ORM 核心逻辑

2.1 数据库实体类(与表映射)

2.2 数据库操作基础类(Dao)

2.3 反射核心:SqlSession类(模拟 MyBatis 的SqlSession)

2.4 测试类(Test)

2.5 运行结果

三、案例解析

3.1 类名与表名的映射

3.2 属性与字段的映射

3.3 动态 SQL 生成

3.4 解耦与扩展性


     在 Java 持久层框架中,MyBatis 作为经典的 ORM(Object-Relational Mapping,对象 - 关系映射)框架,让我们可以通过面向对象的方式操作数据库。而这一切的 “魔法”,离不开 Java 反射机制的支撑。本篇博客将结合简化版 MyBatis 案例,深入解析反射在 ORM 中的核心作用。

一、ORM框架

1.1 什么是 ORM?       

       ORM 的核心是 “对象 - 表映射”:将 Java 对象的属性与数据库表的字段一一对应,从而让开发者可以通过操作对象,间接完成对数据库的增删改查。简单来说,ORM框架的作用就是:

  • 将Java对象映射成数据库中的表Java 对象Emps → 对应数据库表t_emps

  • 将对象属性映射成数据库表的字段对象属性ename → 对应表字段ename

  • 将对象操作映射成SQL语句调用emp.setEname("张三") → 等价于执行UPDATE t_emps SET ename='张三'...

1.2 反射在 ORM 中的角色

        ORM 要实现 “对象→表” 的自动映射,必须在运行时动态获取对象的元数据(如类名、属性名、方法等),而这正是 Java 反射的核心能力:

  • 动态获取类名 → 推断数据库表名(如Emps → t_emps)。

  • 动态获取属性名 → 推断数据库字段名(如ename → ename)。

  • 动态调用 get/set 方法 → 读取 / 设置对象属性,进而生成 SQL 或封装查询结果。

二、简化版 MyBatis:反射实现 ORM 核心逻辑

下面通过一个简化案例,模拟 MyBatis 如何利用反射实现 “对象→SQL” 的自动转换。

2.1 数据库实体类(与表映射)

映射关系Emps类 → 数据库表t_emps;类属性 → 表字段(如ename → ename)。

package com.demo1;public class Emps {private int  eid;private String  ename;private String epwd;private String  ebirthday;private double  esalary;private String eaddress;private int  estate;public int getEid() {return eid;}public void setEid(int eid) {this.eid = eid;}public String getEname() {return ename;}public void setEname(String ename) {this.ename = ename;}public String getEpwd() {return epwd;}public void setEpwd(String epwd) {this.epwd = epwd;}public String getEbirthday() {return ebirthday;}public void setEbirthday(String ebirthday) {this.ebirthday = ebirthday;}public double getEsalary() {return esalary;}public void setEsalary(double esalary) {this.esalary = esalary;}public String getEaddress() {return eaddress;}public void setEaddress(String eaddress) {this.eaddress = eaddress;}public int getEstate() {return estate;}public void setEstate(int estate) {this.estate = estate;}}

2.2 数据库操作基础类(Dao

负责 JDBC 连接与 SQL 执行:

package com.demo1;import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;import com.mysql.cj.xdevapi.PreparableStatement;public class Dao {Connection conn;public Dao() {try {Class.forName("com.mysql.cj.jdbc.Driver");conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/jk202508", "root", "152602");} catch (ClassNotFoundException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (SQLException e) {// TODO Auto-generated catch blocke.printStackTrace();}}public void closeDB() {if (null != conn) {try {conn.close();} catch (SQLException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}public void addData(String sql) {try {PreparedStatement pstmt = conn.prepareStatement(sql);pstmt.executeUpdate();} catch (SQLException e) {// TODO Auto-generated catch blocke.printStackTrace();} finally {this.closeDB();}}}

2.3 反射核心:SqlSession类(模拟 MyBatis 的SqlSession

通过反射自动生成 INSERT SQL,并执行:

反射关键操作

  • Class对象.getSimpleName():获取类名(如Emps)。

  • Class对象.getDeclaredMethods():获取所有方法(包括私有方法)。

  • method.invoke(obj):调用对象的方法,动态获取属性值。

package com.demo1;import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;public class SqlSession {/*** 自动生成INSERT SQL并执行* 参数为obj: 要插入的Java对象*/public void addObject(Object obj) throws Exception {// 核心的就是:反射String sql = "insert into ";// 获取表名,通过类名去获取 假设所有表明都以"t_"开头Class c = obj.getClass();String className = c.getSimpleName();System.out.println(className);sql += "t_" + className.toLowerCase() + "(";
//		System.out.println(sql);// 声明一个集合放入列名List<String> columns = new ArrayList<String>();// 声明一个集合,存放数据库表中的值List<Object> values = new ArrayList<Object>();// 获取列名,通过反射获取get方法,get方法可以获取列名,可以获取值Method[] ms = c.getDeclaredMethods();for (Method m : ms) {String methodName = m.getName();
//			System.out.println("所有的方法名为: " + methodName);if (methodName.startsWith("get")) {
//				System.out.println("方法名为: " + methodName);String columnName = methodName.substring(3).toLowerCase();// 假设id是自增长的,不需要去设置if (!columnName.contains("id")) {System.out.println(columnName);columns.add(columnName); // 把列名放入集合中Object abjValue = m.invoke(obj, null);    // 调用getter方法获取属性值if (abjValue instanceof String) {values.add("'" + abjValue + "'"); // 把值放入集合中}else {values.add(abjValue);}}}}// 把列名拼进sqlfor (int i = 0; i < columns.size(); i++) {if (i == columns.size() - 1) {sql += columns.get(i) + ")";} else {sql += columns.get(i) + ",";}}sql += " values(";// 把值拼进sqlfor (int i = 0; i < values.size(); i++) {if (i == values.size() - 1) {sql += values.get(i) + ")";} else {sql += values.get(i) + ",";}}System.out.println(sql);// 将sql语句传给Dao类去执行Dao dao = new Dao();dao.addData(sql);}}

2.4 测试类(Test

package com.demo1;public class Test {public static void main(String[] args) {Emps emp = new Emps();emp.setEname("花花");emp.setEpwd("123456");emp.setEbirthday("2004-9-9");emp.setEaddress("成都");emp.setEsalary(10000.90);emp.setEstate(1);// 一个对象和数据库的一张表对应SqlSession sqlSession = new SqlSession();try {sqlSession.addObject(emp);System.out.println("插入成功!");} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}}}

2.5 运行结果

三、案例解析

3.1 类名与表名的映射

        通过Class对象.getSimpleName()获取类名(如Emps),再约定 “表名 = t_ + 类名小写”,实现类→表的自动映射。

3.2 属性与字段的映射

        通过反射遍历所有get方法:

  • 方法名getEname → 截取后得到字段名ename

  • 调用getEname() → 动态获取属性值“花花”。

从而实现属性→字段的自动映射。

3.3 动态 SQL 生成

        基于反射收集的 “字段名” 和 “属性值”,自动拼接出完整的insert语句:

insert into t_emps(epwd,ename,esalary,eaddress,estate,ebirthday) values('123456','花花',10000.9,'成都',1,'2004-9-9')

3.4 解耦与扩展性

        如果新增一个Stus类(对应t_stus表),无需修改SqlSession代码,直接调用sqlSession.addObject(s)即可,体现了 ORM“配置化、可扩展” 的核心优势 —— 这一切都依赖反射的 “动态性”。

Stus类:

package com.demo1;public class Stus {private int sid;private String sname;private String saddress;public int getSid() {return sid;}//	public void setSid(int sid) {
//		this.sid = sid;
//	}public String getSname() {return sname;}public void setSname(String sname) {this.sname = sname;}public String getSaddress() {return saddress;}public void setSaddress(String saddress) {this.saddress = saddress;}}

Test类:

package com.demo1;public class Test {public static void main(String[] args) {Stus s = new Stus();s.setSname("QQ");s.setSaddress("南京");Emps emp = new Emps();emp.setEname("花花");emp.setEpwd("123456");emp.setEbirthday("2004-9-9");emp.setEaddress("成都");emp.setEsalary(10000.90);emp.setEstate(1);// 一个对象和数据库的一张表对应SqlSession sqlSession = new SqlSession();try {sqlSession.addObject(s);
//			sqlSession.addObject(emp);System.out.println("插入成功!");} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}}}

http://www.dtcms.com/a/411018.html

相关文章:

  • 网站主机教程wordpress 文章点赞插件
  • 网站图片展示形式中国最好的网站建设有哪些
  • 拉取postgresql 18.0 docker镜像并运行容器
  • java 动态代理
  • 24届-Java开发面经-华为od
  • 开源BI系统
  • article.2034672470
  • 洞头网站建设高端品牌女装特价网
  • 天津建设网站需要的费用宜宾住房与城乡建设部网站
  • 【译】Visual Studio 中针对 .NET MAUI 的 XAML 实时预览功能的增强
  • MySQL DML 与 DQL 基础语法详解:增删改查入门实战
  • 如何把AutoDL实例里的文件备份到Github仓库?
  • 化妆品网站制作兰州h5设计
  • 论批评与自我批评
  • 建网站公司浩森宇特深圳网站建设 华信科
  • Android 13 完整实现 USB 网卡支持与网络优先级配置(USB>WiFi>4G)
  • 加强网站建设的措施莱芜中医院网站
  • 衡水企业网站设计网站上怎么做推广
  • 狄利克雷卷积
  • 沈阳 教育 公司 网站建设种子搜索网站怎么做的
  • 矩阵置零--leetcode
  • 删除iCloud中的照片但保留iPhone上的照片的两种方法
  • 巴中市住房和城乡建设局官方网站广告牌子设计图片
  • 找学校的网站塔城网站seo
  • oracle linux 10 +pg18 源码安装要点
  • 如何快速排查服务器宕机故障
  • 分sheet写入excel
  • 怎么把网站黑了传统文化网站建设
  • 【开题答辩全过程】以 SpringBootVue的旅游租车管理系统为例,包含答辩的问题和答案
  • Windows配置PicGo+Gitee图床——解决你的MarkDown笔记分享无图片的问题