java反射读取@table和@column注解需显式声明@retention(retentionpolicy.runtime),通过field.getannotation()获取,注意private字段调用setaccessible(true),拼接sql须用preparedstatement占位符并校验字段与表结构一致性。

Java反射获取类的@Table和@Column注解信息
反射读取注解本身不难,难在默认不保留运行时可见性。如果没加@Retention(RetentionPolicy.RUNTIME),getAnnotation()永远返回null。
实操建议:
-
@Table和@Column必须显式声明@Retention(RetentionPolicy.RUNTIME),否则反射拿不到 - 字段级注解要调用
Field.getAnnotation(Column.class),别误用Class.getAnnotation() - 注意继承:默认
getDeclaredFields()不包含父类字段,需手动向上遍历getSuperclass() - 字段可能被
private修饰,记得调用setAccessible(true),否则IllegalAccessException会炸在后续赋值阶段
用反射拼接INSERT SQL时绕不开的PreparedStatement参数绑定
直接字符串拼接VALUES字段是典型反模式——SQL注入、类型错位、空值处理全崩。必须走PreparedStatement占位符路线。
实操建议:
- 先用反射收集所有
@Column标记的非transient字段,按声明顺序生成?占位符串 - 绑定参数时,用
PreparedStatement.setObject(int, Object)比按类型分设更稳妥(如setString/setLong),避免NullPointerException或SQLException: Invalid column type - 主键自增场景下,别把
@Id字段塞进VALUES;用executeUpdate()后立即调getGeneratedKeys()取回ID - 注意
java.time.LocalDateTime等新时间类型,JDBC 4.2+才原生支持,老驱动得转Timestamp.from(…)
执行保存前必须校验字段与表结构的映射一致性
开发期看着没问题,上线一跑就报SQLException: Column 'xxx' not found in table 'yyy'——八成是注解写错了,或者数据库字段名和@Column(name = "...")对不上。
实操建议:
- 首次加载实体类时,用
Connection.getMetaData().getColumns()查真实表结构,对比@Column.name()值,不一致立刻抛IllegalStateException并打印差异 - 别依赖默认字段名推导(比如把
userName自动转user_name),显式写@Column(name = "user_name")更可控 - 忽略大小写陷阱:MySQL默认不区分,PostgreSQL默认区分,校验时统一转小写比对
- 字段类型也要粗略检查,比如
@Column标了length=10但DB里是VARCHAR(255)可放行,但标了length=10而DB是INT就得告警
为什么save()方法不能直接new一个Connection就完事
硬编码DriverManager.getConnection()看似简单,实际会导致连接泄漏、事务无法传播、线程不安全。ORM不是玩具,得考虑真实部署环境。
实操建议:
- 接口层只定义
save(Object entity),内部通过ThreadLocal<connection></connection>或依赖注入传入连接,别自己new - 如果没上Spring,至少封装个简易连接池(哪怕就用
ConcurrentLinkedQueue缓存几个Connection),避免频繁握手开销 - 事务控制点必须外移:
save()自身不做commit/rollback,由调用方统一管理,否则嵌套调用必乱 - 反射+JDBC组合天生不支持批量插入(
addBatch()需同SQL模板),想高效写多条?得另写saveAll(List<t>)</t>专用路径,别强塞进单条逻辑
最常被跳过的其实是异常恢复:反射失败、SQL执行失败、类型转换失败,这三类错误堆栈长得几乎一样,得靠getCause()一层层剥开才能定位到是注解缺失、字段为空还是驱动版本太低。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











