如何使用JPA的@Formula注解

背景描述

我们经常会在项目中用到一些数据字典,在存储和传输时使用Code,在前端展示时使用Name,这样做的好处是便于系统维护,比如项目中用到了"医院"这个名称,如果后期需求发生变化不叫"医院"了,改成"医疗机构",假如不使用数据字典,那么我们代码中、数据库中所有用到"医院"的地方都要修改,麻烦不说,漏掉一个就是一个小Bug。在处理这个Code/Name的转化的时候,我思考了几种处理方式,第一种,使用@ManyToOne注解关联字典查询,这样是最容易想到的方式,但是这种方式得到的结果是字典对象整体包含在查询到的实体中,我们所需要的只是字典里的name,所以我尝试寻找一种直接将字典表里的name映射到实体对象上的方式。第二种,使用HQL关联查询映射到自定义对象,这种方式可以达到我的预期,但是HQL写起来很麻烦,尤其是当需要关联查询的字典特别多的时候。并且我的项目中动态用的是JPA的Specification,如果使用这种方式,那么项目中的动态查询需要改写,也是不小的工作量。第三种,使用@Formula注解的方式,下面重点说说这种方式。

使用介绍

@Formula的作用是计算出一个临时的属性值,我们可以利用它,去关联查询其他表中的某个字段为对象的属性赋值,而这个属性是不持久化到数据库中的。

用代码描述一下会比较直观:

建立两个实体类

班级字典

@Data
@Entity
public class DictClass {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    @Column(nullable = false)
    private String classCode;//班级编号
    private String className;//班级名称
}

学生类

@Data
@Entity
public class Student {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    @Column(nullable = false)
    private String name; //名字
    @Column(name = "CLASS_CODE")
    private String classCode;//班级编号
    @Formula("(select d.class_name from dict_class as d where d.class_code = class_code)")
    private String className;//班级名称

}

@Formula("(select d.class_name from dict_class as d where d.class_code = class_code)")意思就是从数据库的dict_class表中查询到class_name字段的数据,赋值到className属性上。"="后面的class_code对应的时@Column里的class_code

持久层

public interface StudentRepo extends JpaRepository<Student, Long> {
    //很持久
}

控制层

@RestController
@RequestMapping("/formula")
@Api(tags = "formula测试接口")
public class FormulaController {
    @Autowired
    StudentRepo studentRepo;
    @GetMapping
    public List<Student> findAll(){
        return studentRepo.findAll();
    }
}

测试一下,获取成功

注意事项

虽然看起来很简单,不过还是有好些个需要注意的地方,一言不合就失效。

1.网上好多人说@Formula必须用在属性上,其实不是的,@Formula 要与@Id注解同时用在属性上,或者同时用在在get方法上,否则@Formula失效。

2.如果查询中用到了where,那么需要给表起一个别名,否则@Formula失效。

3.@Formula与@Transient不能同时使用,否则@Formula失效。

4.使用@Formula注解的属性不需要在数据库表中建立与之对应的字段,并且即使建立了也没有作用,加上@Column注解也不行。

使用过程中遇到的坑

当持久层使用原生sql查询时,会造成NPE异常。

在持久层添加

@Query(nativeQuery = true,value = "select * from student")
List<Student> findByNative();

测试一下

这似乎是因为@Formula屏蔽了className字段,框架获取@Column对应的name时拿到null导致的,我未能找到具体原因,所以用起来还是很不随心,不知道大家是否有好的处理方案,欢迎指教!

原文地址:https://www.cnblogs.com/hanstrovsky/p/12977008.html