本文是对JHipster开发文档的部分翻译,供个人学习之用。
原文链接:https://jhipster.github.io/creating-an-entity/
一旦你创建了应用,就会希望创建实体。
对于每个实体,你需要:
一个数据库表一个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:这会跳过客户端的代码,只生成服务器端的代码。如果想要创建许多实体,可以使用图形工具
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生成器可以对每种实体添加想要的域,只需要输入域的名字和他们的类型,JHipster就会生成从Angular HTML页面到Liquibase changelog所有需要的代码和配置。
这些域不能包含所用技术的保留关键字,如MySQL和Java的关键字。
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端特殊处理,他们将会显示在用户终端。每个域都可以进行验证,不同的域类型可以有不同的验证选项。
验证会自动生成:
HTML页面,使用AngularJS验证机制Java领域对象,使用Bean验证当领域对象用于以下场景时,将自动进行Bean验证:
Spring MVC REST控制器(使用@Valid注解)Hibernate/JPA(实体在保存前自动验证)验证信息也会用于生成更精确数据库列元数据:
Required域将标志为non-nullable有最大长度的域将有相同的列长度验证有一些限制:
不支持所有的AngularJS和Bean验证选项,只有两个API都有的。正则表达式在JavaScript和Java中不一样,所以设置一个后可能需要稍稍调整另一个生成的模式JHipster生成一般实体的单元测试,却不知道你的验证规则,你需要更新单元测试中用到的示例值,这样他们才会传递验证规则实体关系只有当存在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
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? idJDL:
entity Owner entity Car relationship ManyToOne { Car{owner} to Owner }这样就不能从Owner实体中添加或移除车。Owner (1) ------> (*) Car,一个主人可以有多辆车,一辆车只能有一个主人,但是通过主人可以找到所有的车,通过车不能找到它的主人。
这种关系在JHipster中还不默认提供,有两种解决方法:
执行一个双向映射,不做修改地使用执行一个双向映射,将其修改为一个单向映射: 移除@OneToMany注解的mappedBy属性生成必要的连接表: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? idJDL: entity Person entity Car relationship OneToMany { Person{ownedCar} to Car{owner} } relationship OneToMany { Person{drivedCar} to Car{driver} }在客户端你可以在选择Person的owner和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
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? idJDL: 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? idJDL: entity Citizen entity Passport relationship OneToOne { Citizen{passport} to Passport }一个公民持有一个护照,但是护照中没有定义公民实例,在客户端,Citizen中可以下拉选择Passport。默认的JHipster实体不使用DTOs,但是他们是可选的
默认情况下,JHipster直接在REST终端使用领域对象——JPA实体,主要的好处就是让代码更易用、易理解、易扩展。
然而,对于复杂的用例,可能想用可以被REST终端暴露的数据转化对象(DTO)。这些对象在领域对象的顶层添加了一个额外的层,专门用于REST层。主要的好处就是可以集合多个领域对象。
当生成一个JHipster实体时,可以选择是否也生成DTO,如果选择:
生成一个DTO,映射到依赖的实体集合多对一的关系,只使用ID和用于展示的域。例如一个User实体的多对一关系会将userId域和userLogin域添加到DTO中。将忽略非所有者上的一对多、多对多关系,这符合实体的运作方式(他们对这些域使用@JsonIgnore注解)对于所有者的多对多关系,使用其他实体的DTO并将他们放在一个Set中使用,因此,只有当其他实体也使用DTO时才能支持。DTO看起来很像实体,经常需要将他们想办法相互映射。
JHipster选择的方式是使用MapStruct,这是一个注解处理器,作为Java编译器的插件,将自动生成必要的映射。
这简介又有效,并且似乎不使用反射(当在mapper中用到很多时,性能很受影响)
MapStruct需要设置为在IDE编译项目时自动运行
如果使用Maven,需要激活IDE的maven配置。
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); } }目前如果使用Cassandra来创建应用是不能进行分页的。
分页使用了和GitHub API中一样的Linker header。JHipster提供对服务器和客户端特定的自定义实现。
生成实体时,JHipster提供4中分页选项:
不分页简单分页,基于Bootstrap pager完全分页系统,基于Bootstrap分页组件有限滚动系统,基于有限滚动指令实体的配置保存在特定的.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
