当前位置: 首页 > 后端技术 > Java

JPA注解整理

时间:2023-04-02 01:42:47 Java

一直在用myBatis。虽然有了Generator,但是还是觉得写SQL是一件很麻烦的事情。而且从实用的角度来说,我们总是习惯直接按照接口要求的格式来写SQL。这种后端没有任何存在感,就好像它只是一个前端数据库代理工具一样。再加上三层架构,Transaction脚本式代码,后端名副其实的实体化,名副其实的增删改查,哪里来的业务思维。我选择JPA是因为我想改变主意。拿到需求的时候,首先不是如何设计数据库,而是如何用代码准确表达业务意图,最后转化为数据库设计。归根结底,数据库只是一种存储解决方案。面对业务,我们或许应该忽略底层的存储。有了JPA,我们可以更方便地选择存储方案,关系型数据库还是非关系型数据库。并且同时不需要修改DAO层代码。这是myBatis做不到的。本文不深入介绍JPA原理,或者Hibernate原理。只是我这段时间使用JPA遇到的问题以及JPA的解决方法。一些有趣的配置enable_lazy_load_no_trans当我使用Springboot的异步任务(@Async标签方法),获取一个实体的children,或者直接使用JpaRepository操作时,程序总是抛出一个异常,我记得是哪个异常我也不知道。可能是当前session获取不到有效的数据库连接,也可能是当前线程的hibernatesession无效之类的提示。解决这个问题也很简单,加个配置spring.jpa.properties.hibernate.enable_lazy_load_no_trans=truedefault_constraint_modeJPA默认创建数据库表,它会为表之间的约束创建一个外键。这其实是对的,但是有时候涉及到表结构变化或者数据不完整的时候就很烦人了。有没有办法只创建逻辑外键?据说有两种方法。第一种方式@JoinColumn注解foreignKey设置ConstraintMode=NO_CONSTRAINT但是需要到处显式设置,太麻烦;b.第二种方式是全局配置。不过好像对JPA版本有要求,最低支持哪个版本。我个人项目的Springboot版本是2.4.3spring.jpa.properties.hibernate.hbm2ddl.default_constraint_mode=NO_CONSTRAINT一些有趣的注解@LazyCollection比如我有一个订单业务,前端在展示的时候,包含的物品数量订单需要显示。我该怎么办?两种方法:直接把所有的订单项找成一个集合,然后计算集合的大小。缺点是必须一次检查所有产品;@LazyCollection(LazyCollectionOption.EXTRA)使用此注解在实体关系中的Set属性上标记@Formula("selectcount(*)")。这个注解是在某个方法上的,但是我没有成功;没时间仔细研究,所以选择了第二种方法@DynamicInsert&@DynamicUpdate@DynamicInsertattribute:settotrue,表示插入对象时,将生成动态插入语句。如果该字段的值为null,则不会加入到insert语句中,默认为false。例如,当你想让数据库插入一个日期或时间戳字段时,如果对象字段为空,表字段可以自动填充当前的sysdate。@DynamicUpdate属性:设置为true,表示在生成更新对象时,生成动态更新语句。如果该字段的值为null,则不会添加到更新语句中。默认为假。比如我们只想更新某个属性,却要改变整个属性。这不是我们想要的结果。我们想要的结果是:我只需要更新我修改的字段即可。@Formula有时总价中需要计算这里的数据,比如商品实体,有单价和数量;但是我不想把它的总价存储在数据库中,而是倾向于根据单价*数量动态计算。一种常见的方式是提供一个返回计算结果的getter方法。一个推荐的方法是实现privatelonggrossIncome;私人inttaxInPercents;@Formula("grossIncome*taxInPercents/100")通过@Formula注解获取私人长税;@Formula还可以进行子查询、调用数据库函数、存储过程等。如果我们写JQL,Hibernate可以帮我们把它转换成SQL,从而实现SQL查询结果的定义。@Where如果需要全局过滤一个实体,比如在逻辑删除业务的上下文中,如果只想查询标记为'deleted'的数据,@Where可以全局过滤并标记在EntityClass上。如果要过滤OneToMany集合,还可以使用@Where标记。但是@Where的局限性在于条件是固定的,需要动态进行条件过滤时需要使用@Filter。@Any&@AnyMetaDef相对于@ManyToOne和@OneToOne,@Any表示属性对应的类型是任意的,从字面上理解。想一个场景,假设我有一条审批记录,可能来自三个不同的实体A,B,C,但是我只想用一张表来保存审批记录,同时保证每条审批A记录可以找到它对应的A,或B,或C。看下面的代码="ALLOCATION",targetEntity=B.class),@MetaValue(value="OTHER",targetEntity=C.class),})@EntitypublicclassInOutBoundApproveextendsBaseEntity{//对应数据库,sourceObjectType中存储的值,A,B,C,哪个值取决于sourceObject@Column(name="source_object_type",insertable=false,updatable=false)privateStringsourceObjectType;//对应数据库,存储的是外键id,即A、B、C的id。//同时必须指定Def,就是@AnyMetaDef的名字//@metaColumn指定用哪个字段来判断sourceObject的类类型@Any(fetch=FetchType.LAZY,metaDef="InOutBoundSourceObjectDef",metaColumn=@Column(name="source_object_type"))@JoinColumn(name="source_object_id")privateBaseEntitysourceObject;//基础实体。javapublicclassBaseEntity{privateIntegerid;//...其他属性}