spring boot jpa中 Hibernate 注解 @Immutable 的使用场景

news/2024/11/26 10:46:33/

入门示例

使用 spring boot jpa 来操作数据库的增删改查是非常方便的,定义完 model 之后,直接定义JPA
即可,后续操作就很丝滑了:

@Table(name = "host_spec_price")
@Data
@Entity
public class BudgetHost {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)@Column(name = "id", insertable = false, nullable = false)private Long id;@Column(name = "host_spec")private String hostSpec;@Column(name = "host_type")private String hostType;@Column(name = "start_date")private String startDate;@Column(name = "create_time")private Timestamp createTime;@Column(name = "update_time")private Timestamp updateTime;}

定义 JPA 接口:

import org.springframework.data.jpa.repository.JpaRepository;public interface BudgetHostDao extends JpaRepository<BudgetHost, Long> {}

然后在 Controller 里面就可以用了:

@RestController
@Slf4j
public class BudgetController {@Resourceprivate BudgetHostDao budgetHostDao;@GetMapping("/budget/cloudHostBudget")public Result<List<BudgetHost>> getCloudHostBudget(String userName) {List<BudgetHost> list = budgetHostDao.findAll();log.info("userName = {} execute getCloudHostBudget, query size={}", userName, list.size());return Result.success(list);}}

高级场景

入门例子已经能涵盖CURD 80%的需求了,但有时候我们定义的实体类并不会只是简单的 select,可能还要分组聚合,所以你需要使用 JPA 的 Native Query ,并配合一个新对象或接口来接受聚合场景下的对象返回封装。

这里我总结有两种情况:

  • 使用接口接受新聚合结果
  • 使用类接受聚合结果

使用接口接受聚合结果

假设我们按表里面 day 字段分组,求 count,那么我就 key 定义一个简单的接口接受结果,然后返会给前端

public interface GroupStatistics {String getName();int getValue();
}

JPA 类的定义:

    @Query(nativeQuery = true,value = " select day as name, count(*) as value from table group by day
where create_time between :startDate and :endDate  and user like CONCAT('%',:user,'%')          
")List<GroupStatistics> findApprovalStatistics(@Param("user") String user, @Param("startDate") String startDate,@Param("endDate") String endDate);

ok,看起来还是非常简单的,但是这种用法有一个缺点,就是说如果用接口定义,你拿到返回的数据后,就没办法去再次修改返回结果了,因为这是接口不是类,没办法新增属性和修改属性,基于这种情况下,我们再来看使用类接受定制的查询结果

使用类接受聚合结果

model 定义

import lombok.Data;
import org.hibernate.annotations.Immutable;import javax.persistence.*;@NamedNativeQuery(name = "CloudHostBudgetDTO.budgetQuery",
query = "select SUBSTRING_INDEX(a.tag, ':', -1)  as label, " +"                     count(*) as hostCount, " +"                     round(sum(b.real_price), 0) as hostPrice, " +"                     0 as cbsPrice, " +"                     0 as cbsCount " +"              from cloud_host_info a " +"                       left join host_spec_price b " +"                                 on a.host_spec = b.host_spec " +"              group by label " +"              order by hostPrice desc;",resultSetMapping = "cloudHostBudgetDto"
)
@SqlResultSetMapping(name = "cloudHostBudgetDto",entities = {@EntityResult(entityClass = CloudHostBudgetDTO.class,fields = {@FieldResult(name = "label", column = "label"),@FieldResult(name = "hostCount", column = "hostCount"),@FieldResult(name = "hostPrice", column = "hostPrice"),@FieldResult(name = "cbsPrice", column = "cbsPrice"),@FieldResult(name = "cbsCount", column = "cbsCount")})}
)
@Data
@Entity
@Immutable //注意这个注解,该 model 只为查询用,它本身的变更不会同步数据库表
public class CloudHostBudgetDTO {@Idprivate String label;private Integer hostCount;private Integer hostPrice;private Integer cbsPrice;private Integer cbsCount;
}

基于类的封装,我们用的是NamedNativeQuery和SqlResultSetMapping,虽然看着复杂点,但是其灵活性更好,可以预留多个额外字段,方便对有些时候从程序中动态计算结果的赋值,因为有些字段可能并不能从数据库里面全部计算好,所以还需要程序补充一部分,这个时候基于类的封装就很好了,你 Controller 方法里面拿到结果之后,可以继续遍历结果对某些字段进行额外的业务逻辑处理。

Immutable 注解解释

在 Hibernate 中,@Immutable 注解用于标识某个实体类是不可变的,即该实体的对象在持久化之后,其状态不应该被修改。这个注解告诉 Hibernate,不会对该实体进行任何更新或删除操作,比如上面我们的查询结果,就很明显只是为了接受聚合的结果用的,其 model 本身并不会对应数据库里面的任何一个实体表,所以这里必须用Immutable注解标记,明确告诉 Hibernate,当我们修改 model 字段属性时,不要去更新数据库里面的表,因为这个表并不存在,也不需要存在,如果你不加 Immutable 还会 model 属性进行了修改,那么这个时候就会报错,这一点需要注意。

@Immutable 的作用

优化性能

由于标记了 @Immutable 的实体对象不允许修改,Hibernate 在处理这些对象时可以进行一些性能上的优化。例如,Hibernate 不会再为这些对象生成更新语句(UPDATE),也不会尝试为它们执行任何删除操作。这样可以减少数据库交互的次数,从而提高性能,尤其在读取频繁且不修改的场景中。

数据一致性保证

@Immutable 可以确保实体对象的状态不被意外修改。如果有代码尝试对这些对象执行更新操作,Hibernate 会抛出异常。这样可以确保业务逻辑层的一致性,避免不小心对本应保持不变的数据进行修改。

与缓存结合

在 Hibernate 的一级缓存(Session 缓存)和二级缓存中,@Immutable 标记的对象可能会有不同的处理。Hibernate 可以在缓存中保留这些对象,并假定它们不会发生变化,从而减少了缓存失效的风险,提高了缓存命中率。


http://www.ppmy.cn/news/1550037.html

相关文章

后端开发详细学习框架与路线

&#x1f680; 作者 &#xff1a;“码上有前” &#x1f680; 文章简介 &#xff1a;后端开发 &#x1f680; 欢迎小伙伴们 点赞&#x1f44d;、收藏⭐、留言&#x1f4ac; 为帮助你合理安排时间&#xff0c;以下是结合上述学习内容的阶段划分与时间分配建议。时间安排灵活&a…

python Flask指定IP和端口

from flask import Flask, request import uuidimport json import osapp Flask(__name__)app.route(/) def hello_world():return Hello, World!if __name__ __main__:app.run(host0.0.0.0, port5000)

【c++】模板详解(2)

&#x1f31f;&#x1f31f;作者主页&#xff1a;ephemerals__ &#x1f31f;&#x1f31f;所属专栏&#xff1a;C 目录 前言 一、非类型模板参数 二、模板的特化 1. 概念 2. 场景举例 3. 函数模板的特化 4. 类模板的特化 全特化 偏特化 1. 部分特化 2. 对参数的…

气膜场馆照明设计:科技与环保的完美结合—轻空间

气膜场馆的照明设计&#xff0c;选用高效节能的400瓦LED灯具&#xff0c;结合现代节能技术&#xff0c;提供强大而均匀的光照。LED灯具在光效和寿命方面优势显著&#xff0c;不仅降低运营能耗&#xff0c;还有效减少碳排放&#xff0c;为绿色场馆建设贡献力量。 科学分布&…

【Python爬虫五十个小案例】爬取豆瓣电影Top250

博客主页&#xff1a;小馒头学python 本文专栏: Python爬虫五十个小案例 专栏简介&#xff1a;分享五十个Python爬虫小案例 &#x1fab2;前言 在这篇博客中&#xff0c;我们将学习如何使用Python爬取豆瓣电影Top250的数据。我们将使用requests库来发送HTTP请求&#xff0c;…

java脚手架系列16-AI大模型集成

之所以想写这一系列&#xff0c;是因为之前工作过程中有几次项目是从零开始搭建的&#xff0c;而且项目涉及的内容还不少。在这过程中&#xff0c;遇到了很多棘手的非业务问题&#xff0c;在不断实践过程中慢慢积累出一些基本的实践经验&#xff0c;认为这些与业务无关的基本的…

优化Docker镜像:提升部署效率与降低资源消耗

目录 1. 最小化镜像层 2. 使用轻量级基础镜像 3. 多阶段构建 4. 清理不必要的文件和依赖 5. 使用.dockerignore文件 6. 压缩和优化文件系统 7. 外部化配置和数据 8. 限制容器资源 9. 定期清理未使用的镜像和容器 结论 在云计算和微服务架构的浪潮中&#xff0c;Docke…

Spire.PDF for .NET【页面设置】演示:打开 PDF 时自动显示书签或缩略图

用户打开 PDF 文档时&#xff0c;他们会看到 PDF 的初始视图。默认情况下&#xff0c;打开 PDF 时不会显示书签面板或缩略图面板。在本文中&#xff0c;我们将演示如何设置文档属性&#xff0c;以便每次启动文件时都会打开书签面板或缩略图面板。 Spire.PDF for .NET 是一款独…