ORM映射规范
说个大概,
首先就是不要直接Select * 根据具体需要的字段查询,1)增加查询分析器解析成本。2)增减字段容易与 resultMap 配置不一致。3)无用字段增加网络消耗,尤其是 text 类型的字段。
不能使用${} 这种方式,容易出现 SQL 注入问题
更新数据表记录时,必须同时更新记录对应的 update_time 字段值为当前时间
还有其他等等
\1. 【强制】在表查询中,一律不要使用 * 作为查询的字段列表,需要哪些字段必须明确写明。
说明:1)增加查询分析器解析成本。2)增减字段容易与 resultMap 配置不一致。3)无用字段增加网络消耗,尤其是 text 类型的字段。
-
降低查询分析器解析成本:使用
*
时,查询分析器需要额外去解析表的所有字段结构来确定最终要返回的数据内容,尤其在复杂的多表查询或者包含大量字段的表查询场景下,增加了不必要的解析工作量。而明确写出具体字段,分析器可直接针对这些指定字段进行针对性的查询处理,简化了其工作流程,提升查询解析的效率。 -
保持与 resultMap 配置一致性:如果使用
*
,当表结构发生变化(增减字段)时,很容易出现查询返回的字段与预先配置的resultMap
(用于数据库字段和实体类属性映射)不一致的情况。例如新增了一个字段,使用*
会把新字段也查出来,但resultMap
里可能没配置对应的映射关系,就容易导致后续数据处理出错。明确字段可让开发人员及时根据字段变化更新resultMap
,保证二者匹配。 -
减少网络消耗:当查询包含一些大文本类型(如
text
类型)等无用字段时,会增加网络传输的数据量,拖慢查询响应速度,尤其是在大量数据查询或者网络带宽有限的场景下,影响更为明显。只查询需要的字段,能有效避免传输不必要的数据,节省网络资源,提升整体性能。
\2. 【强制】POJO 类的布尔属性不能加 is,而数据库字段必须加 is_,要求在 resultMap 中进行字段与属性之间的映射。
说明:参见定义 POJO 类以及数据库字段定义规定,在 sql.xml 增加映射,是必须的。
-
遵循统一的命名和映射规则:规定 POJO 类的布尔属性不加
is
,而数据库字段加is_
,并在resultMap
中进行映射,这样在整个项目中建立了清晰明确的数据库字段与实体类属性之间的对应关系。开发人员可以依据这个规则清晰地知道如何进行数据的转换和赋值,便于代码的编写、理解以及后续的维护工作,避免因命名不一致造成的混淆和错误。 -
方便不同层之间的数据交互处理:通过在
sql.xml
里配置这种映射关系,确保了从数据库查询出的数据能准确无误地映射到对应的 POJO 类属性上,在持久层与业务逻辑层等不同层级之间进行数据传递时,能够按照既定的规则进行转换,保证数据的准确性和完整性,提升了整个系统的数据处理可靠性。
\3. 【强制】不要用 resultClass 当返回参数,即使所有类属性名与数据库字段一一对应,也需要定义<resultMap>;反过来,每一个表也必然有一个<resultMap>与之对应。
说明:配置映射关系,使字段与 DO 类解耦,方便维护。
-
实现字段与 DO 类解耦:即使类属性名与数据库字段一一对应,使用
<resultMap>
能将数据库的表结构变化与业务代码中的实体类(DO 类)隔离开。例如,当数据库表中某个字段改名或者数据类型改变时,只要在<resultMap>
里调整相应的映射配置,就不会影响到依赖该实体类的其他业务逻辑代码,方便对数据库结构和业务代码分别进行维护和扩展,提高了系统的可维护性和灵活性。 -
增强映射关系的可配置性和可控性:
<resultMap>
提供了更灵活的配置方式,可以处理各种复杂的映射情况,比如字段别名、类型转换、关联查询结果的映射等。而单纯依赖resultClass
,在面对一些非标准的、复杂的映射需求时,就很难准确地将数据库数据映射到实体类中,使用<resultMap>
可以根据具体业务需求精确地定制映射关系,确保数据的正确映射和使用。
\4. 【强制】sql.xml 配置参数使用:#{},#param# 不要使用${} 此种方式容易出现 SQL 注入。
-
防止 SQL 注入攻击:使用
${}
这种方式是将参数直接拼接到 SQL 语句中,恶意用户可能会通过构造特殊的参数值,将恶意的 SQL 语句片段注入到原本合法的 SQL 语句里,从而实现非法的数据查询、篡改甚至删除等操作,严重威胁数据库的安全性。而#{}
方式会对传入的参数进行预编译处理,将参数作为一个值来处理,而不是 SQL 语句的一部分,有效避免了 SQL 注入风险,保障了数据库系统的安全稳定运行。
\5. 【强制】iBATIS 自带的 queryForList(String statementName,int start,int size)不推荐使用。
说明:其实现方式是在数据库取到 statementName 对应的 SQL 语句的所有记录,再通过 subList 取start,size 的子集合。
正例:
Map<String, Object> map = new HashMap<>(16);
map.put("start", start);
map.put("size", size);
-
优化查询性能:该方法的实现方式是先获取对应 SQL 语句的所有记录,再从中截取指定范围的子集合。当表中数据量很大时,一次性获取所有记录会消耗大量的数据库资源(如内存、网络带宽等),而且可能导致数据库连接长时间占用等性能问题。而通过自定义方式传入参数(如示例中使用
Map
传递起始和数量参数),可以让数据库直接按照指定范围去查询数据,减少不必要的数据读取和资源消耗,提升查询的性能和效率,尤其适用于分页查询等大数据量查询场景。
\6. 【强制】不允许直接拿 HashMap 与 Hashtable 作为查询结果集的输出。
反例:某同学为避免写一个<resultMap>xxx</resultMap>,直接使用 HashTable 来接收数据库返回结果,结果出现日常是把 bigint 转成 Long 值,而线上由于数据库版本不一样,解析成 BigInteger,导致线上问题。
-
保证数据解析的一致性和稳定性:不同的数据库版本对数据类型的解析规则可能存在差异,直接使用
HashTable
接收结果,容易出现像线上线下数据类型解析不一致的情况(如bigint
类型数据在线下解析成Long
值,在线上由于数据库版本不同解析成BigInteger
),导致程序运行出现错误,影响系统的正常运行。通过使用规范的映射方式(如<resultMap>
)来处理结果集,可以确保数据类型的准确转换和一致性,避免因数据库版本等因素造成的此类兼容性问题。
\7. 【强制】更新数据表记录时,必须同时更新记录对应的 update_time 字段值为当前时间。
-
便于数据追踪与审计:记录更新时间可以方便后续查看数据的变更历史,了解每条记录最后一次被修改的时间点,对于数据的追踪、审计以及问题排查等工作非常有帮助。例如,在出现数据异常或者需要查看某个业务操作对应的记录修改情况时,可以依据
update_time
快速定位相关记录,分析问题产生的时间范围和原因。 -
符合数据时效性管理需求:确保数据的时效性信息准确反映在表中,让使用数据的其他业务逻辑能够基于最新的更新时间来判断数据的新旧程度,合理地使用和处理数据,有助于提高整个业务系统对数据的利用效率和准确性。
\8. 【推荐】不要写一个大而全的数据更新接口。传入为 POJO 类,不管是不是自己的目标更新字段,都进行 update table set c1=value1, c2=value2, c3=value3; 这是不对的。执行 SQL 时,不要更新无改动的字段,一是易出错;二是效率低;三是增加 binlog 存储。
-
降低出错风险:如果不管字段是否有改动都进行更新,容易误操作一些本不应修改的数据,比如可能覆盖掉其他业务刚正确更新的数据,或者因不小心传入错误的更新值导致数据错误。只更新有改动的字段,可以精准地对数据进行操作,减少因不必要的更新带来的各种数据错误风险,保证数据的准确性和完整性。
-
提升执行效率:更新无改动的字段会增加数据库执行更新操作的工作量,尤其在表数据量较大或者频繁更新的场景下,会浪费数据库资源,拖慢更新操作的执行速度。针对性地更新有变化的字段,能减少不必要的数据库写操作,提高更新的效率,优化系统性能。
-
减少 binlog 存储压力:数据库的
binlog
(二进制日志)用于记录数据的变更情况,无意义的字段更新也会被记录到binlog
中,增加了binlog
的存储量,长期来看可能导致binlog
占用过多磁盘空间,影响数据库的运维管理(如备份恢复等操作会依赖binlog
)。限制只更新有改动的字段,可以有效控制binlog
的数据增长,便于数据库的维护和管理。
\9. 【参考】@Transactional 事务不要滥用。事务会影响数据库的 QPS,另外使用事务的地方需要考虑各方面的回滚方案,包括缓存回滚、搜索引擎回滚、消息补偿、统计修正等。
-
保障数据库性能(QPS):事务的开启和执行会涉及到数据库资源的占用,比如锁机制的运用等,过多使用事务会增加数据库的负担,影响数据库每秒能够处理的查询次数(QPS),导致系统整体的性能下降,尤其在高并发场景下,性能影响更为明显。合理使用事务,只在确实需要保证一组操作的原子性的场景下启用,能避免不必要的性能损耗,提升系统响应速度。
-
确保完善的回滚方案:使用事务的地方需要考虑各方面的回滚情况,因为事务涉及的数据操作可能不仅仅局限于数据库本身,还可能影响到缓存、搜索引擎、消息队列等相关组件。例如,事务回滚时,如果只处理了数据库层面的回滚,而没有对缓存中已更新的数据进行相应回滚,就会导致数据不一致的情况出现。谨慎使用事务并提前规划好全面的回滚方案,有助于保证整个业务系统在出现异常情况时数据的一致性和系统的稳定性。
10.【参考】<isEqual>中的 compareValue 是与属性值对比的常量,一般是数字,表示相等时带上此条件;<isNotEmpty>表示不为空且不为 null 时执行;<isNotNull>表示不为 null 值时执行。
-
准确控制 SQL 语句执行条件:清晰地定义这些标签中参数的含义和对比规则(如
<isEqual>
中compareValue
与属性值对比、<isNotEmpty>
判断不为空且不为null
时执行、<isNotNull>
判断不为null
值时执行),能让开发人员在编写动态 SQL 语句(如根据不同条件拼接 SQL)时,准确地根据业务需求控制 SQL 语句的执行条件。例如,在根据用户输入的不同筛选条件动态生成查询 SQL 时,通过这些标签可以精确地决定哪些条件应该加入到 SQL 语句中,使生成的 SQL 语句更符合实际业务场景的查询需求,提高 SQL 语句的灵活性和准确性。
通过遵循这些 ORM 映射规范,可以在数据库操作、数据映射、系统性能、安全性以及代码维护等多个方面带来好处,有助于构建更加稳定、高效、安全且易于维护的业务系统。