Java 反射与 MyBatis:ORM 框架的 “灵魂基石”
目录
2.3 反射核心:SqlSession类(模拟 MyBatis 的SqlSession)
在 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 block
e.printStackTrace();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void closeDB() {
if (null != conn) {
try {
conn.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public void addData(String sql) {
try {
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.executeUpdate();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.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);
}
}
}
}
// 把列名拼进sql
for (int i = 0; i < columns.size(); i++) {
if (i == columns.size() - 1) {
sql += columns.get(i) + ")";
} else {
sql += columns.get(i) + ",";
}
}
sql += " values(";
// 把值拼进sql
for (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 block
e.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 block
e.printStackTrace();
}
}
}
更多推荐
所有评论(0)