当前位置: 首页 > 科技观察

对JPA的迟到的赞美,AbstractEntity需要准备什么?

时间:2023-03-14 20:37:35 科技观察

本文转载自微信公众号“味姐小姐姐”,作者02号狗,转载请联系味姐公众号。本文是代码分析系列之一,主要内容是JPA的基本父类设计。参考代码:https://github.com/xjjdog/bcMall/blob/master/bc-utils/src/main/java/cn/xjjdog/bcmall/utils/db/AbstractEntity.java关系型数据库其实很烦人,尤其是如果您使用数据库驱动的开发模型。需要先创建表,然后用代码生成器逆向生成一堆几乎不可读的代码。场上有异动,又是一次折腾。这方面的典型例子就是MyBatis,于是一个更简洁的MyBatisPlus就诞生了。了解到一些大公司(阿里巴巴、腾讯、抖音等)越来越广泛地使用JPA,包括我们公司。这是将正确的工具放在正确的位置。如果想快速开发,JPA无疑是更好的选择。不需要关注数据库表的结构,使用代码驱动即可完成工作,不管背后是MySQL还是Oracle。JPA弱化数据库相关知识,让你专注于业务开发。我个人曾经排斥JPA这个削弱SQL的工具,因为我对早期Hibernate版本的误解。但是在尝试了mybatis、spring-data-jdbc、jooq之后,发现这个东西真的很香!迟来的对JPA的赞美。这非常适合一些管理系统。因为性能不是这些系统的主要痛点,业务复杂性才是。本文将介绍一个简单的实体类,需要准备哪些基本字段。这些字段在代码中的使用方式。1.基本字段介绍我们先来看看我们的基本定义类。不是很多代码,而是很多信息。我们一行一行来分析。@DataData注解属于lombok类。Lombok是地球人都知道的代码简化工具,提供了很多注解。如果不想背太多注解,直接加一个Data是最懒的选择。@MappedSuperclass这个注解是JPA的,用来标识父类。标记为@MappedSuperclass的类不会是一个完整的实体类,不会映射到数据库表,但它的属性会映射到子类的数据库字段。放在这儿再合适不过了。@EntityListeners(AuditingEntityListener.class)开启自动审计功能,配合下面两个日期字段,后面会介绍。@JsonIgnoreProperties(value={"hibernateLazyInitializer","handler"})//直接使用bean时,避免json序列号失效有时候,我们想在controller层直接使用JPA实体。但是JPA内部其实还有很多额外的变量,比如hibernateLazyInitializer。为了让实体在json序列化的时候能够正常进行,需要忽略这两个字段。所以这个注解属于jacksonjson。2.自定义ID生成器JPA其实提供了很多ID生成策略。但是在互联网应用中,雪花算法的应用更为广泛,因为它的扩展性好,在数据迁移的时候不会有很多冲突。为了指定雪花算法,我们需要以下几行代码。staticfinalStringID_GEN="cn.xjjdog.bcmall.utils.db.DistributedId";@Id@GenericGenerator(name="IdGen",strategy=ID_GEN)@GeneratedValue(generator="IdGen")其中一个关键是使用我们的名字叫做IdGen的ID生成器。这里的代码有点遗憾。由于JVM类加载,我们不能直接使用注解中的类名(*.class.getName())来获取它的包路径,这里只能写成字符串。我们来看看这个ID生成器的处理过程。publicclassDistributedIdimplementsIdentifierGenerator{@OverridepublicSerializablegenerate(SharedSessionContractImplementorsharedSessionContractImplementor,Objectobj)throwsHibernateException{if(obj==null)thrownewHibernateException(newNullPointerException());如果((((AbstractEntity)obj).getId())==null){returnString.valueOf(SnowflakeOf(SnowflakeOf)createId());}else{return((AbstractEntity)obj).getId();}}}代码如上。在直接使用之前,我们也做了一点处理。当我们判断实体的ID为空时,我们使用雪花算法构造一个新的ID;否则,我们使用实体最初设置的ID并保持不变。为什么要这样做?因为有它的需要。对于订单这样的业务,需要先生成一个订单号,然后更新一些数据库信息,发布一些消息等;而不是在保存操作开始时生成一个。如果不做以上代码的处理。JPA每次保存都会自动生成一个,覆盖掉你原来的。我这里吃亏了,只能通过debug代码修复。3.自动填写字段上面提到的createdDate和lastModifiedDate字段,其实在使用的时候不需要手动设置值。这两个值会由审计函数自动完成。@EntityListeners(AuditingEntityListener.class)当然,我们还需要使用唯一的注解来标识这两个字段。/***创建日期*/@CreatedDateprivateDatecreatedDate;/***更新日期*/@LastModifiedDateprivateDatelastModifiedDate;最后别忘了在全局配置中通过Config开启这个功能。@Configuration@EnableJpaAuditingpublicclassJpaConfig{}当然,没有用户就无法进行审计。所以这个系列还有@CreatedBy注解来标明是谁创建的。需要将它们组装成代码,比如下面的代码,就是从SpringSecurity获取用户信息。@Configuration@Slf4jpublicclassUserAuditorimplementsAuditorAware{@OverridepublicOptionalgetCurrentAuditor(){UserDetailsuser;try{user=(UserDetails)SecurityContextHolder.getContext().getAuthentication().getPrincipal();returnOptional.ofNullable().User;}catch(Exceptione){returnOptional.empty();}}}4.EndJPA编写管理系统真是神器。当你不需要考虑最终的代码效率时,这是一个非常好的选择。看最近的MyBatis版本,包括MyBatisPlus的设计,很多东西都越来越像JPA了。因为在设计上,JPA是最接近面向对象编程思想的。B端复杂业务的技术栈不需要和C端技术栈一样。显然,JPA可以用很少的代码和约定搞定事情,让开发者真正专注于业务开发。在后面的文章中,我们还将使用MyBatis和MyBatisPlus。届时,我们将详细分析它们的使用场景。作者简介:品味小姐姐(xjjdog),一个不允许程序员走弯路的公众号。专注于基础架构和Linux。十年架构,每天百亿流量,与你探讨高并发世界,给你不一样的滋味。我的个人微信xjjdog0,欢迎加好友进一步交流。