自学内容网 自学内容网

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

入门示例

使用 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 {


    @Resource
    private 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 {
    @Id
    private 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 可以在缓存中保留这些对象,并假定它们不会发生变化,从而减少了缓存失效的风险,提高了缓存命中率。


原文地址:https://blog.csdn.net/u010454030/article/details/143946365

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!