(7)FPQuant JHipster_创建实体

本文是对JHipster开发文档的部分翻译,供个人学习之用。

原文链接:https://jhipster.github.io/creating-an-entity/


1.介绍

一旦你创建了应用,就会希望创建实体。

对于每个实体,你需要:

  • 一个数据库表
  • 一个Liquibase变化集合
  • 一个JPA实体
  • 一个Spring Data JPA Repository
  • 一个Spring MVC REST Controller,有基本的CRUD操作
  • 一个Angular router,一个component和一个service
  • 一个HTML页面
  • 集成的测试,来验证所有都像预计的一样运行
  • 性能测试,观察是否所有都顺利运行

如果已经有几个类,你可能想要他们彼此之间有关系,这就需要:

  • 一个数据库的外键
  • 特定的JavaScript和HTML代码来管理各种关系

实体生成器将会生成必要的文件,并且为每个实体提供一个CRUD的前后端。生成器可以用yo jhipster:entity <entityName> --[options]来调用,可选参数可以用yo jhipster:entity --help来查询,下面是一些选项:

  • --table-name <table-name>:默认情况下JHipster会生成一个名为实体名字的表,如果你想要一个不一样的表名,可以在这里传递参数。
  • --angular-suffix <suffix>:如果你想要所有的Angular路由都有一个自定义的后缀,可以在这里传递参数。
  • --regenerate:这会重新生成一个已存在的实体。
  • --skip-server:这会跳过服务器端的代码,只生成客户端的代码。
  • --skip-client:这会跳过客户端的代码,只生成服务器端的代码。

2.JHipster UML和JDL Studio

如果想要创建许多实体,可以使用图形工具

  • JHipster UML,让你可以使用一个UML编辑器
  • JDL Studio,使用JDL创建实体和关系的线上工具

如果使用JDL Studio

  • 执行yo jhipster:import-jdl your-jdl-file.jh,用import-jdl生成器从一个JDL文件生成实体
  • 运行npm install -g jhipster-uml,然后jhipster-uml yourFileName.jh,用JHipster UML来代替import-jdl生成器

3.实体域

可以对每种实体添加想要的域,只需要输入域的名字和他们的类型,JHipster就会生成从Angular HTML页面到Liquibase changelog所有需要的代码和配置。

这些域不能包含所用技术的保留关键字,如MySQL和Java的关键字。

4.域类型

JHipster支持许多域类型,这种支持依赖于你的数据库端,我们只要使用Java类型来描述即可。

  • String:Java字符串,默认大小取决于后端如JPA就是255,可以使用验证规则来修改
  • Integer:整型数
  • Long:长整型
  • Float:浮点数
  • Double:双精度浮点数
  • BigDecimal:java.math.BigDecimal对象,用于精确的数学计算
  • LocalDate:java.time.LocalDate对象,用于准确管理日期
  • ZoneDateTime:java.time.ZonedDateTime对象,用于准确管理日期和时间
  • Boolean:布尔值
  • Enumeration:java枚举类型,选择后,生成器会问你想要枚举类型中的什么值,并且会创建一个特殊的enum类来存储
  • Blob:Blob对象,用于存储一些二进制数据,选择后,生成器会问你是否想要存储一般的二进制数据,一个图像对象或一个CLOB(长文本)。图像将可以在Angular端特殊处理,他们将会显示在用户终端。

5.验证

每个域都可以进行验证,不同的域类型可以有不同的验证选项。

验证会自动生成:

  • HTML页面,使用AngularJS验证机制
  • Java领域对象,使用Bean验证

当领域对象用于以下场景时,将自动进行Bean验证:

  • Spring MVC REST控制器(使用@Valid注解)
  • Hibernate/JPA(实体在保存前自动验证)

验证信息也会用于生成更精确数据库列元数据:

  • Required域将标志为non-nullable
  • 有最大长度的域将有相同的列长度

验证有一些限制:

  • 不支持所有的AngularJS和Bean验证选项,只有两个API都有的。
  • 正则表达式在JavaScript和Java中不一样,所以设置一个后可能需要稍稍调整另一个生成的模式
  • JHipster生成一般实体的单元测试,却不知道你的验证规则,你需要更新单元测试中用到的示例值,这样他们才会传递验证规则

6.实体关系

实体关系只有当存在JPA时对SQL数据库有用,如果选择了Cassandra或MongoDB,就没有用。

关系可以存在于两个实体之间,JHipster将会生成下列代码:

在生成的实体中用JPA管理关系

对存在于数据库中的关系创建正确的Liquibase changelog

生成AngularJS前端,你可以在图像用户界面进行管理

双向的一对多关系:

Owner(1) <-----> (*)Car,一个主人可以有多辆车,一辆车只能有一个主人,并且通过车可以找到主人,通过主人可以找到所有的车。

先创建Owner:

Generating relationships with other entities
? Do you want to add a relationship to another entity? Yes
? What is the name of the other entity? Car
? What is the name of the relationship? car
? What is the type of the relationship? one-to-many
? What is the name of this relationship in the other entity? owner
再生成Car:

Generating relationships with other entities
? Do you want to add a relationship to another entity? Yes
? What is the name of the other entity? Owner
? What is the name of the relationship? owner
? What is the type of the relationship? many-to-one
? When you display this relationship with AngularJS, which field from 'Owner' do you want to use? id
同样在JDL中可以:

entity Owner
entity Car

relationship OneToMany {
  Owner{car} to Car{owner}
}
这样在AngularJS客户界面上当选择Owner时就有一个下拉的Car
单向的多对一关系:
Owner(1) <------- (*)Car,一个主人可以有多辆车,一辆车只能有一个主人,但是通过主人不能找到车,通过车可以找到他的主人。

先生成Owner

Generating relationships with other entities
? Do you want to add a relationship to another entity? No

再生成Car

Generating relationships with other entities
? Do you want to add a relationship to another entity? Yes
? What is the name of the other entity? Owner
? What is the name of the relationship? owner
? What is the type of the relationship? many-to-one
? When you display this relationship with AngularJS, which field from 'Owner' do you want to use? id

JDL:

entity Owner
entity Car

relationship ManyToOne {
  Car{owner} to Owner
}
这样就不能从Owner实体中添加或移除车。

单向的一对多关系:

Owner (1) ------> (*) Car,一个主人可以有多辆车,一辆车只能有一个主人,但是通过主人可以找到所有的车,通过车不能找到它的主人。

这种关系在JHipster中还不默认提供,有两种解决方法:

  • 执行一个双向映射,不做修改地使用
  • 执行一个双向映射,将其修改为一个单向映射:

    1. 移除@OneToMany注解的mappedBy属性
    2. 生成必要的连接表:mvn liquibase:diff来生成

JDL不支持。

两对相同实体上的两个一对多关系:

Person (1) <---owns-----> (*) Car

Person (1) <---drives----> (*) Car

先生成Person实体:

Generating relationships with other entities
? Do you want to add a relationship to another entity? Yes
? What is the name of the other entity? Car
? What is the name of the relationship? ownedCar
? What is the type of the relationship? one-to-many
? What is the name of this relationship in the other entity? owner
...
Generating relationships with other entities
? Do you want to add a relationship to another entity? Yes
? What is the name of the other entity? Car
? What is the name of the relationship? drivedCar
? What is the type of the relationship? one-to-many
? What is the name of this relationship in the other entity? driver
再生成Car实体,使用在Person中配置的关系名称:

Generating relationships with other entities
? Do you want to add a relationship to another entity? Yes
? What is the name of the other entity? Person
? What is the name of the relationship? owner
? What is the type of the relationship? many-to-one
? When you display this relationship with AngularJS, which field from 'Person' do you want to use? id
...
Generating relationships with other entities
? Do you want to add a relationship to another entity? Yes
? What is the name of the other entity? Person
? What is the name of the relationship? driver
? What is the type of the relationship? many-to-one
? When you display this relationship with AngularJS, which field from 'Person' do you want to use? id
JDL:

entity Person
entity Car

relationship OneToMany {
  Person{ownedCar} to Car{owner}
}

relationship OneToMany {
  Person{drivedCar} to Car{driver}
}
在客户端你可以在选择Person的owner和driver域时有Car的下拉列表

多对多关系:
Driver (*) <--------> (*) Car,一个司机可以开多辆车,一辆车可以有多个司机。

在数据库层,意味着在Driver和Car之间有一个连接表。

对于JPA,两个实体中的一个需要管理这个关系,我们这里让Car来管理,负责添加或删除Driver。

先生成Driver:

Generating relationships with other entities
? Do you want to add a relationship to another entity? Yes
? What is the name of the other entity? Car
? What is the name of the relationship? car
? What is the type of the relationship? many-to-many
? Is this entity the owner of the relationship? No
? What is the name of this relationship in the other entity? driver
再生成Car,是关系的所有者:

Generating relationships with other entities
? Do you want to add a relationship to another entity? Yes
? What is the name of the other entity? Driver
? What is the name of the relationship? driver
? What is the type of the relationship? many-to-many
? Is this entity the owner of the relationship? Yes
? When you display this relationship with AngularJS, which field from 'Driver' do you want to use? id
JDL:

entity Driver
entity Car

relationship ManyToMany {
  Car{driver} to Driver{car}
}
在客户端,因为Car是所有者,可以在Car中下拉选择Driver。

一对一关系:
Driver (1) <---------->(1) Car,一辆车只有一个司机,一个司机只开一辆车。

先生成Driver

Generating relationships with other entities
? Do you want to add a relationship to another entity? Yes
? What is the name of the other entity? Car
? What is the name of the relationship? car
? What is the type of the relationship? one-to-one
? Is this entity the owner of the relationship? No
? What is the name of this relationship in the other entity? driver
再生成Car

Generating relationships with other entities
? Do you want to add a relationship to another entity? Yes
? What is the name of the other entity? Driver
? What is the name of the relationship? driver
? What is the type of the relationship? one-to-one
? Is this entity the owner of the relationship? Yes
? What is the name of this relationship in the other entity? car
? When you display this relationship with AngularJS, which field from 'Driver' do you want to use? id
JDL:
entity Driver
entity Car

relationship OneToOne {
  Car{driver} to Driver{car}
}
因为Car是关系的所有者,可以在Car中下拉选择Driver。

单向的一对一关系:

Citizen(1) <--------->(1)Passport,一个公民只能有一张护照,但是一个护照实例不能有主人。

先生成Passport:

Generating relationships with other entities
? Do you want to add a relationship to another entity? No
再生成Citizen:

Generating relationships with other entities
? Do you want to add a relationship to another entity? Yes
? What is the name of the other entity? Passport
? What is the name of the relationship? passport
? What is the type of the relationship? one-to-one
? Is this entity the owner of the relationship? Yes
? What is the name of this relationship in the other entity? citizen
? When you display this relationship with AngularJS, which field from 'Passport' do you want to use? id
JDL:

entity Citizen
entity Passport

relationship OneToOne {
  Citizen{passport} to Passport
}
一个公民持有一个护照,但是护照中没有定义公民实例,在客户端,Citizen中可以下拉选择Passport。

7.数据传输类型

默认的JHipster实体不使用DTOs,但是他们是可选的

默认情况下,JHipster直接在REST终端使用领域对象——JPA实体,主要的好处就是让代码更易用、易理解、易扩展。

然而,对于复杂的用例,可能想用可以被REST终端暴露的数据转化对象(DTO)。这些对象在领域对象的顶层添加了一个额外的层,专门用于REST层。主要的好处就是可以集合多个领域对象。

DTO的使用原理:

当生成一个JHipster实体时,可以选择是否也生成DTO,如果选择:

  • 生成一个DTO,映射到依赖的实体
  • 集合多对一的关系,只使用ID和用于展示的域。例如一个User实体的多对一关系会将userId域和userLogin域添加到DTO中。
  • 将忽略非所有者上的一对多、多对多关系,这符合实体的运作方式(他们对这些域使用@JsonIgnore注解)
  • 对于所有者的多对多关系,使用其他实体的DTO并将他们放在一个Set中使用,因此,只有当其他实体也使用DTO时才能支持。

使用MapStruct映射DTO和实体:

DTO看起来很像实体,经常需要将他们想办法相互映射。

JHipster选择的方式是使用MapStruct,这是一个注解处理器,作为Java编译器的插件,将自动生成必要的映射。

这简介又有效,并且似乎不使用反射(当在mapper中用到很多时,性能很受影响)

为MapStruct配置IDE:

MapStruct需要设置为在IDE编译项目时自动运行

如果使用Maven,需要激活IDE的maven配置。

MapStruct的进一步用法:

MapStruct的mapper被配置为Spring Beans,并且支持独立注入。

建议将一个Repository注入一个mapper,这样就可以从mapper通过它的ID获取一个可控的JPA实体。

示例:

@Mapper
public abstract class CarMapper {

    @Inject
    private UserRepository userRepository;

    @Mapping(source = "user.id", target = "userId")
    @Mapping(source = "user.login", target = "userLogin")
    public abstract CarDTO carToCarDTO(Car car);

    @Mapping(source = "userId", target = "user")
    public abstract Car carDTOToCar(CarDTO carDTO);

    public User userFromId(Long id) {
        if (id == null) {
            return null;
        }
        return userRepository.findOne(id);
    }
}

8.分页

目前如果使用Cassandra来创建应用是不能进行分页的。

分页使用了和GitHub API中一样的Linker header。JHipster提供对服务器和客户端特定的自定义实现。

生成实体时,JHipster提供4中分页选项:

  • 不分页
  • 简单分页,基于Bootstrap pager
  • 完全分页系统,基于Bootstrap分页组件
  • 有限滚动系统,基于有限滚动指令

9.更新已存在的实体

实体的配置保存在特定的.jhipster目录下的.json文件中。如果你再运行一次实体生成器,使用已存在的实体名称,你可以更新或重新生成这个实体。

此时有四种选项:

  • 重新生成实体(可以在运行生成器时传递一个--regenerate标志来强制执行)
  • 添加域和关系
  • 去除域和关系
  • 退出

你可能因为下列原因想要更新实体:

  • 对已存在的实体添加/移除域和关系
  • 重新设置实体的代码到原始状态
  • 更新了JHipster,想要用新的模板来生成实体
  • 更改了.json配置文件,有一个实体的新的版本
  • 复制粘贴了.json文件,想要一个新的与拷贝实体很接近的实体

为了立即重新生成实体,可以使用下面的命令,如果更改了文件询问问题时,可以将--force去除

Linux/Mac:for f in `ls .jhipster`;do yo jhipster:entity ${f%.*} --force; done

Windows:for %f in (.jhipster/*) do yo jhipster:entity %~nf --force

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值