首页 文章 精选 留言 我的

精选列表

搜索[boot],共8301篇文章
优秀的个人博客,低调大师

《Spring Boot + JPA 实战》第3招 Entity怎么建

## 01、痛点 早上被同事拉住,说订单服务升级后,数据库里订单状态从 0 变成了 2,历史数据全乱套了。我是老李。这类问题我们在过去踩过不止一次:有人把 Entity 只当成数据库表的 Java 翻译版本,Hibernate 自动建表后看都不看,结果字段类型、空值约束、枚举取值全都跑偏。现在公司准备做订单系统第一版,如果不把 Entity 当成领域对象和关系模型之间的持久化契约,后面每一次发版都可能变成救火。 很多团队一上来就让 Hibernate 自动生成 Schema,开发时很方便。但订单系统涉及金额、状态、地址等核心概念,一旦表结构被自动生成绑架,后期补约束、改字段类型、重建索引都非常痛苦。更麻烦的是,Enum 默认用 ordinal 存值,表面上没问题,一旦枚举顺序调整,数据库里的 0、1、2 就失去了业务含义。 ## 02、是什么 Entity 不是数据库表的 Java 翻译版本,而是领域对象和关系模型之间的持久化契约。这句话的重点是“契约”二字:领域对象有自己的业务语言,数据库表有自己的存储结构,二者之间不是简单的一一照搬,而是通过显式规则建立受控映射。 常用映射规则包括:@Entity 标记实体;@Table 明确表名、唯一约束和索引;@Id 定义主键;@GeneratedValue 定义主键生成策略;@Column 定义列名、长度、精度、可空性;@Enumerated 定义枚举持久化方式;@Embedded 与 @Embeddable 定义值对象;@MappedSuperclass 抽取公共映射字段。类型上,金额统一使用 BigDecimal,时间使用 LocalDateTime。这里的列定义还要考虑 NULL、默认值、唯一约束和数据库字段类型,而不是让框架猜。 比如 @Column 的 nullable、length、precision、scale、columnDefinition 可以直接影响数据库结构。@Table 的 uniqueConstraints 可以声明业务唯一键。@Enumerated 可以选择 STRING 或 ORDINAL,建议用 STRING。@Embedded 让 Address 作为值对象被 Customer 复用,而不是拆成多个外键。@MappedSuperclass 适合放 id、版本号、创建时间等公共字段,避免每个实体重复声明。 ## 03、为什么 为什么不能把 Schema 完全交给自动生成?因为自动生成只能根据 Java 类型粗略推导数据库类型,但业务约束、索引策略、字段精度、命名规范、历史兼容都需要人工决策。例如 BigDecimal 默认可能生成不同精度,LocalDateTime 的时区表达各数据库不同,String 可能生成超长字段或错误字符集。枚举 ordinal 的问题更隐蔽:ordinal 保存枚举声明顺序,一旦枚举中插入新值,旧的 0/1 含义可能错位,数据无法自解释。 为什么现在要讲?因为订单系统第一版是很多团队从单体拆微服务、从简单 CRUD 转领域建模的起点。这个阶段把 Entity 设计好,后面做查询、报表、分库分表、数据迁移都会轻松;反过来,如果一开始就放任映射,后续每一张表都可能成为隐性债务。 ## 04、架构 订单系统第一版可设计五个核心实体:Customer、Address、Product、Order、OrderItem。其中 Address 使用 Value Object,不单独建表,而是作为列嵌入 Customer;订单状态使用枚举,建议 @Enumerated(EnumType.STRING);金额统一使用 BigDecimal。 实体关系上,Customer 一对多 Order,Order 一对多 OrderItem,Product 一对多 OrderItem。Customer 与 Address 是组合关系,Address 生命周期跟随 Customer。公共字段可以用 @MappedSuperclass 放在 BaseEntity 中,比如主键 id、创建时间 createdAt、更新时间 updatedAt。这样五个实体不用重复声明主键。 映射架构可以用一句话概括:领域对象表达业务语义,关系模型表达存储约束,Entity 映射是两者之间的翻译层。这个翻译层越清晰,系统越不容易腐化。订单状态不要用 int 或 ordinal 直接映射,而要定义 OrderStatus 枚举,并强制 STRING 持久化。金额不要用 double 或 float,BigDecimal 配合 precision 和 scale 才能保证分毫不差。 ## 05、实战 下面给出订单系统第一版的关键代码骨架。先看 Address 值对象: ```java @Embeddable public class Address { @Column(nullable = false, length = 120) private String street; @Column(nullable = false, length = 40) private String city; @Column(length = 20) private String zipCode; } ``` Customer 实体: ```java @Entity @Table(name = "customers", uniqueConstraints = @UniqueConstraint(name = "uk_customer_email", columnNames = "email")) public class Customer extends BaseEntity { @Column(nullable = false, length = 80) private String name; @Column(nullable = false, unique = true, length = 120) private String email; @Embedded private Address address; } ``` 订单状态枚举: ```java public enum OrderStatus { PENDING, PAID, SHIPPED, COMPLETED, CANCELLED } ``` Order 实体: ```java @Entity @Table(name = "orders") public class Order extends BaseEntity { @ManyToOne(optional = false) @JoinColumn(name = "customer_id", nullable = false) private Customer customer; @Enumerated(EnumType.STRING) @Column(nullable = false, length = 20) private OrderStatus status; @Column(nullable = false, precision = 18, scale = 2) private BigDecimal totalAmount; } ``` OrderItem 实体: ```java @Entity @Table(name = "order_items") public class OrderItem extends BaseEntity { @ManyToOne(optional = false) @JoinColumn(name = "order_id", nullable = false) private Order order; @ManyToOne(optional = false) @JoinColumn(name = "product_id", nullable = false) private Product product; @Column(nullable = false) private Integer quantity; @Column(nullable = false, precision = 18, scale = 2) private BigDecimal unitPrice; } ``` Product 实体: ```java @Entity @Table(name = "products") public class Product extends BaseEntity { @Column(nullable = false, length = 120) private String name; @Column(nullable = false, precision = 18, scale = 2) private BigDecimal price; } ``` BaseEntity 示例: ```java @MappedSuperclass public abstract class BaseEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(nullable = false, updatable = false) private LocalDateTime createdAt; @Column(nullable = false) private LocalDateTime updatedAt; } ``` 这些代码写完后,不要立即提交,而是先用 Hibernate 生成 DDL,再人工审查生成的表、字段、约束和索引。重点检查:金额列是否都是 decimal(18,2);状态列是否是 varchar(20) 而不是 tinyint;唯一约束是否落在 email 上;外键是否都有索引;时间列是否与数据库时区策略一致。 ## 06、洞见 好的 Entity 设计首先表达业务语义,其次才是映射数据库。业务语义包括:Address 是不可变值对象,不应该有独立主键;OrderStatus 是业务状态机,不应该用 0/1/2 这种无意义数字;金额是精确十进制,不能因为 Java 类型方便就用 float 或 double。 为什么 Schema 不能完全交给 Hibernate 自动生成?因为自动生成解决的是“有没有表”,但解决不了“表合不合适”。Schema 不只是列和类型,它承载了业务约束。例如订单总额不能为负、邮箱不能重复、订单状态不能为空,这些规则应该显式出现在数据库层。自动生成通常会忽略默认值、检查约束、索引命名、字符集和排序规则,甚至在版本升级时直接删表重建或留下孤儿列。 为什么 Enum 不推荐直接使用 ordinal?ordinal 把枚举顺序当作持久化值,这意味着数据库里存的是 0、1、2。只要有人调整枚举顺序,比如在 PENDING 前插入 DRAFT,原来的 PAID 可能从 1 变成 2,历史数据全部错位。即便不调整顺序,数据库里的数字也完全失去可读性,排查问题要先翻代码。相比之下,EnumType.STRING 存储 PENDING、PAID 这样的字符串,可读性高,且不受顺序影响。如果担心空间,可以用数据库原生枚举或查找表,但绝不该用 ordinal。 人工审查 Schema 的目的,是把业务约束从代码层延伸到数据库层。代码可以改,数据库却很难改。好的持久化契约会在上生产前把字段长度、精度、空值、唯一键、外键索引都敲定下来,这样后续订单量增长时,不用半夜爬起来修表。 ## 07、行动 第一步,建立五个核心实体:Customer、Address、Product、Order、OrderItem。Address 用 @Embeddable,订单状态用 @Enumerated(EnumType.STRING),金额统一 BigDecimal。第二步,写 BaseEntity 抽取 id、createdAt、updatedAt。第三步,生成 DDL 后导出 SQL 脚本,逐表检查列类型、可空性、默认值和唯一约束。第四步,为外键和常用查询路径补索引,例如 orders.customer_id、order_items.order_id、order_items.product_id。第五步,用测试数据验证:插入正常订单、重复邮箱、空地址、非法状态,确认数据库约束生效。第六步,把最终 DDL 纳入版本管理,后续变更走迁移脚本,而不是继续依赖 Hibernate 自动更新。 另外建议团队成员在代码评审时,把 Entity 映射和 DDL 放在一起看。映射对了但数据库约束漏了,等于契约只签了一半。 ## 08、小结 这次聊了订单系统第一版的 Entity 设计。核心就一句话:Entity 不是数据库表的 Java 翻译版本,而是领域对象和关系模型之间的持久化契约。掌握 @Entity、@Table、@Id、@GeneratedValue、@Column、@Enumerated、@Embedded、@Embeddable、@MappedSuperclass 等常用映射,同时理解 NULL、默认值、唯一约束和数据库字段类型,才能避免自动生成带来的坑。金额用 BigDecimal,时间用 LocalDateTime,枚举用 STRING,Schema 要人工审查,这些都是订单系统上线前的基本功。 ## 09、链接与概念说明 - @Entity / @Table:JPA 实体与表映射核心注解,@Table 可声明唯一约束和索引。 - @Id / @GeneratedValue:主键与生成策略,GenerationType.IDENTITY 依赖数据库自增,适合单库;SEQUENCE 适合 PostgreSQL 等。 - @Column:列映射,可指定 name、nullable、length、precision、scale、columnDefinition。 - @Enumerated:枚举映射,EnumType.STRING 推荐;EnumType.ORDINAL 不推荐。 - @Embedded / @Embeddable:值对象映射,Address 不需要独立主键。 - @MappedSuperclass:公共字段抽取,不会生成单独表。 - BigDecimal:高精度十进制,订单金额等金融数据必须使用。 - LocalDateTime:日期时间,需注意数据库时区与驱动配置。 - 人工审查 DDL:使用数据库工具查看生成的表结构,确认字段类型、约束和索引。 参考可以查阅 Jakarta Persistence 规范、Hibernate ORM 官方文档、Spring Data JPA 官方文档。具体链接可搜索“Jakarta Persistence”“Hibernate ORM”“Spring Data JPA”获取。

优秀的个人博客,低调大师

《Spring Boot + JPA 实战》第7招 JPQL还是SQL

最近复盘一个订单中心项目,同事在代码里写了一个方法名,长得像一列火车:findTop100ByCustomerCreatedAtAfterOrderByTotalAmountDesc。周会上大家盯着这个方法名沉默了五秒,最后还是没人敢说这个方法哪里有问题。我是老李,今天不聊高大上的概念,就聊聊复杂查询到底该怎么选型,为什么方法名不是万能钥匙,以及 JPQL、@Query 和 Native SQL 的边界到底在哪里。 ## 01、痛点 很多团队用 Spring Data JPA,第一步一定是方法名派生查询。它确实省事,findByStatus、findByMobile、findByCreatedAtBetween,简单查询几乎没有成本。可一旦碰到“近30天消费 Top100 客户”“批量更新订单状态”“按月汇总 GMV”这类真实业务,方法名就开始露怯。 首先,表达能力见底。方法名派生查询只是把方法名翻译成 where 条件,能表达属性过滤、排序、分页,表达不了日期计算、聚合、分组、Top N 加过滤再排序。你没法在方法名里写“近30天”这种相对时间,更没法写 sum、group by、having。 其次,@Query 写成一锅粥。很多人退一步用 @Query,但分不清 JPQL 和 Native SQL。同一个 Repository 里,有的方法写 JPQL,有的方法 nativeQuery=true,字段名一会儿是实体属性,一会儿是数据库列名。时间一长,维护的人根本不知道这段查询在操作什么,改一个字段要翻三处。 第三,复杂报表硬套 JPQL。报表要 join 五张表、做窗口函数、按数据库时区处理日期、再分页。JPQL 要么不支持,要么写出来又长又绕,最后生成的 SQL 还不可控。强行套用导致代码既不像对象查询,又不像数据库查询,两边不讨好。 更离谱的是,有人为了凑方法名,查全量数据再用 Java 流过滤,内存占用和响应时间都爆炸。慢查询多了,DBA 开始抓 SQL,发现一百行代码里一半是废操作。这些问题根源不是 JPA 不行,而是没有给查询分层。 ## 02、是什么 先分清几个概念。 JPQL 是 JPA 标准定义的面向对象查询语言,操作的是 Entity 和属性,不是表名和列名。比如查询客户,JPQL 写的是 select c from Customer c where c.createdAt >= :since,这里的 Customer 是实体类,createdAt 是属性。HQL 是 Hibernate 自己的查询语言,语法和 JPQL 高度相似,Spring Data JPA 里通常用 JPQL。 @Query 不是一种新语言,它只是 Spring Data JPA 提供的一个注解,用来在 Repository 方法上写 JPQL 或 Native SQL。当 nativeQuery=false(默认)时,@Query 的内容是 JPQL;当 nativeQuery=true 时,@Query 的内容是原生 SQL。 Native SQL 面向真实表、索引、数据库特性。它操作的是数据库里的表和列,如 orders、created_at,可以用 date_sub、date_format、窗口函数、索引提示等方言能力。Native SQL 强调数据库控制力,能精确利用执行计划、索引和方言函数。 DTO Projection 则是查询结果映射的一种方式,它和查询语言不是一个维度。你可以用 JPQL 查完映射到 DTO,也可以用 Native SQL 查完映射到 DTO。它解决的是“返回什么结构”,而不是“用什么语言查”。 很多开发者会把 HQL 和 JPQL 混为一谈。简单说,JPQL 是规范,HQL 是 Hibernate 方言实现。静态查询里两者几乎无差别,但一旦用到方言特性,HQL 可能扩展更多。Spring Data JPA 默认走 JPQL,背后由 Hibernate 执行。 两者的边界可以概括为:JPQL 面向模型可维护性,把实体关系、继承、懒加载等抽象保留下来;Native SQL 面向数据库控制力,把真实表结构、索引、方言特性暴露出来。 | 维度 | JPQL | Native SQL | | --- | --- | --- | | 操作对象 | 实体和属性 | 表和列 | | 可移植性 | 更强 | 较弱 | | 数据库特性 | 需要方言扩展 | 直接使用 | | 复杂聚合 | 可用但受限 | 充分支持 | | 维护成本 | 简单查询低,复杂查询高 | 简单查询高,复杂查询低 | ## 03、为什么 为什么复杂查询不能只靠方法名?因为方法名派生查询的设计初衷是简化简单查询,而不是替代 SQL。一旦查询维度变多,方法名首先会膨胀成不可读的长串;其次方法名无法表达聚合、子查询、计算字段、数据库函数,这些能力必须借助 JPQL 或 SQL。 JPQL 适合实体关系清晰、查询结果可以直接映射实体或少量字段的场景。它跟数据库解耦,切换数据库时更轻松。但代价是:复杂聚合、窗口函数、数据库特定函数、复杂分页时,JPQL 要么写不出来,要么依赖 Hibernate 方言扩展,可移植性优势反而变成可读性负担。 Native SQL 适合报表、批量更新、跨表复杂统计、需要数据库特性的场景。它的问题是和表结构强耦合,表改名、列改名、数据库迁移时成本高。所以 Native SQL 不是万能药,而是复杂查询的“最后一道门”。 举个例子,同样查某个客户最近30天购买金额,JPQL 要写日期参数传进来,Service 层算好 start 时间;Native SQL 可以直接用 date_sub。前者更可移植,后者更直接。复杂报表往往包含十几个这类计算,JPQL 的代码量会迅速超过 SQL,且更难调优。 这里有一个容易忽略的点:ORM 的价值是减少不必要的 SQL,并不是禁止写 SQL。很多人把 JPA 当成“不能写 SQL”的框架,结果在复杂查询上死磕 JPQL,写出又长又慢的查询。正确姿势是根据查询复杂度逐级选择工具,而不是用一把锤子敲所有钉子。 ## 04、架构 我把查询升级路线整理成四层: 第一层:Derived Query,也就是方法名派生查询。适合字段等值过滤、简单排序、分页,比如 findByStatusOrderByCreatedAtDesc。这层能保证可读性和维护成本都很低。 第二层:JPQL,通过 @Query 写面向实体的查询。适合多条件动态过滤、简单聚合、关联查询,结果映射到实体或 DTO。这层兼顾可维护性和表达能力。 第三层:Specification,配合 JpaSpecificationExecutor,适合动态拼接查询条件,比如后台筛选条件很多、条件之间可选。它仍然是 JPQL 层面的抽象,但能避免大量的 if 分支拼 JPQL。 第四层:Native SQL,通过 @Query(nativeQuery=true) 或自定义 Repository 实现。适合报表统计、复杂 join、窗口函数、批量更新、数据库特性依赖强的场景。这层追求数据库控制力和执行效率。 升级原则是:能用简单方式表达的,不要上复杂方式;但一旦复杂查询已经超出当前层的能力,就果断升级。最怕的是在 JPQL 层拼命模拟 SQL 功能,或者在 Native SQL 层写简单查询,增加维护成本。判断标准是业务意图是否清晰、性能是否可控、维护成本是否最低。 这四层不是教条,而是决策顺序。有人问 Specification 和 JPQL 是不是重复?其实 Specification 解决的是动态条件组合,JPQL 解决的是静态复杂查询。动态条件多时用 Specification,能保持 Repository 方法简洁。 ## 05、实战 下面用三个真实场景分别说明。 场景一:近30天消费 Top100 客户。 这个查询既要过滤近30天,又要按客户聚合,还要按消费金额排序并取前100。方法名写不出来,我们尝试 JPQL。 JPQL 可以这样写: ```sql select c.id, c.name, sum(o.amount) from Order o join o.customer c where o.createdAt >= :start group by c.id, c.name order by sum(o.amount) desc ``` 配合 setMaxResults(100) 限制结果。这个 JPQL 能工作,但要注意几点:group by 里必须包含 select 中出现的实体属性;sum(o.amount) 在 order by 中需要重复;如果要映射到 DTO,还需要写全限定构造器表达式,例如 new com.example.dto.CustomerTopDto(c.id, c.name, sum(o.amount))。DTO Projection 在这里能避免返回 Object[],让结果结构更清晰。不过要注意 DTO 构造器参数顺序必须和 select 一致,否则运行时报错很难查。 如果近30天还要按自然日过滤,并且金额需要数据库做精度处理,JPQL 生成的 SQL 不一定符合索引优化。此时可以降级到 Native SQL: ```sql select c.id as customer_id, c.name as customer_name, sum(o.amount) as total_amount from orders o join customers c on o.customer_id = c.id where o.created_at >= date_sub(now(), interval 30 day) group by c.id, c.name order by total_amount desc limit 100 ``` SQL 里可以直接使用 date_sub 和 limit,执行计划也更容易控制。 场景二:批量更新订单状态。 这个场景是更新操作,不是查询聚合。JPQL 支持批量更新: ```sql update Order o set o.status = :newStatus where o.id in :ids ``` 配合 @Modifying 和事务可以完成。但要注意 @Modifying 要加 @Transactional,批量更新后要 clearAutomatically,避免一级缓存脏数据。 如果更新条件复杂,比如“更新最近7天未支付且金额大于100的订单状态”,JPQL 仍然能写,但一旦涉及子查询、join 更新,JPQL 批量更新表达能力很有限。很多数据库的 update 可以 join,JPQL 不支持 update 里 join,只能用子查询绕,生成 SQL 后性能可能很差。 Native SQL 则可以: ```sql update orders o join ( select id from orders where status = 'UNPAID' and created_at 100 ) t on o.id = t.id set o.status = 'EXPIRED' ``` 这种写法直接面向表,能利用数据库的 update join 特性,执行路径更可控。批量更新属于数据库控制力优先的场景,Native SQL 往往是更合理的选择。 场景三:按月汇总 GMV。 按月汇总需要把 created_at 转成月份。JPQL 本身没有统一的日期格式化函数,可以写 function('date_format', o.createdAt, '%Y-%m'),但这种写法依赖 Hibernate 方言,换数据库可能报错。强行用 JPQL 时,通常还要在 Java 里再做一次格式化,或者用数据库无关的 function 注册,维护成本不低。 Native SQL 直接写: ```sql select date_format(created_at, '%Y-%m') as month, sum(amount) as gmv from orders where created_at >= :start group by month order by month ``` 数据库方言的 date_format、to_char、extract 各不一样,但至少在特定数据库里写得清楚,执行计划也透明。如果团队确定数据库栈,Native SQL 是更务实的选择。注意 Native SQL 参数用 :start 时,Spring Data JPA 会按名称绑定,方法里要加 @Param。 通过这三个场景可以看出:查询类型不同,适合的语言也不同。Top100 可以用 JPQL 加 DTO Projection,批量更新和按月汇总更偏向 Native SQL。 ## 06、洞见 为什么复杂报表强行使用 JPQL 有时比直接 SQL 更难维护? 第一,JPQL 的抽象层次在复杂报表里会变成障碍。报表通常需要多表 join、聚合、分组、过滤、排序、分页、计算字段,JPQL 要表达这些,往往需要写很长的 group by、重复表达式,还要小心实体关系导致的隐式 join。可读性下降得很快。 第二,JPQL 生成的 SQL 不可控。JPQL 由持久化框架翻译成 SQL,翻译规则对开发者不完全透明。复杂查询里,你很难预测它会不会产生笛卡尔积、有没有用上索引、子查询是否被优化。出了性能问题,开发者要先去抓生成的 SQL,再反向理解 JPQL 为什么生成这样,排查链路更长。 第三,数据库特性被屏蔽。报表常用窗口函数、CTE、索引提示、日期函数、排序规则,这些在 JPQL 里要么不支持,要么依赖方言扩展。强行用 JPQL 模拟数据库特性,等于绕远路。 第四,维护成本错位。复杂报表往往和业务口径紧密相关,口径变化频繁。JPQL 里改一个聚合字段,可能涉及 select、group by、order by 多处同步修改。Native SQL 虽然与表结构耦合,但复杂逻辑写得更直白,反而更容易改。 我曾经遇到过一个报表查询,JPQL 写了 80 行,生成的 SQL 执行 15 秒;改成 Native SQL 后 40 行,执行 2 秒。原因不是 JPQL 语言差,而是它不适合这个场景。 所以,边界感很重要。JPQL 负责把简单查询和实体操作变简洁,Native SQL 负责把复杂查询和数据库特性变透明。两者不是对立,而是互补。 ## 07、行动 我建议团队形成一条明确的查询升级路线: 第一步,先问是否能用 Derived Query 表达。如果只是等值过滤、简单排序、分页,直接写方法名,别上 @Query。 第二步,如果方法名不够,再写 JPQL。保持查询面向实体,结果尽量映射到 DTO,少返回 Object[]。DTO Projection 能让查询结果结构化,避免后续取值错位。 第三步,如果查询条件动态变化很多,引入 Specification 做动态拼接,不要在 Repository 里写一堆 if 拼 JPQL 字符串。 第四步,如果碰到复杂报表、批量更新、窗口函数、方言函数,果断上 Native SQL。写 Native SQL 时,单独放在 Repository 接口或专门的查询类里,注释清楚表结构和业务口径。 同时建议加一条团队规范:每次代码评审时,如果看到 JPQL 里出现 function、复杂子查询、重复 group by 表达式,就要问一句“这个查询是不是该用 Native SQL”。如果看到 Native SQL 只是简单等值过滤,也要问一句“这个是不是该用 JPQL”。 升级不是单向的,有时也可以降级。核心标准是:哪个层次的代码更能表达业务意图、更容易维护、性能更可控,就用哪个。 ## 08、小结 复杂查询不能只靠方法名,更不能把 JPQL 当成唯一选择。JPQL 面向实体和属性,Native SQL 面向真实表、索引和数据库特性,二者分别强调模型可维护性和数据库控制力。真正成熟的团队,不是避免写 SQL,而是知道什么时候写什么 SQL。记住一句话:ORM 的价值是减少不必要的 SQL,并不是禁止写 SQL。 ## 09、链接与概念说明 - Derived Query:Spring Data JPA 根据方法名自动生成查询的实现方式,适用于简单查询。 - JPQL:JPA 标准查询语言,操作 Entity 和属性,与数据库方言解耦。 - HQL:Hibernate 查询语言,语法与 JPQL 类似。 - @Query:Spring Data JPA 注解,可用于声明 JPQL 或 Native SQL。 - Native SQL:数据库原生 SQL,直接操作表和列,可利用数据库特性。 - DTO Projection:将查询结果映射到 DTO 对象,避免返回 Object[]。 - Specification:JPA 动态查询接口,适合多条件组合查询。

资源下载

更多资源
优质分享App

优质分享App

近一个月的开发和优化,本站点的第一个app全新上线。该app采用极致压缩,本体才4.36MB。系统里面做了大量数据访问、缓存优化。方便用户在手机上查看文章。后续会推出HarmonyOS的适配版本。

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

用户登录
用户注册