JHipster域语言 (JDL)

JDL是一种JHipster特定的域语言,您可以使用简单且用户友好的语法在单个文件(或多个文件)中描述所有应用程序、部署、实体及其关系。

您可以使用我们的在线JDL-StudioJHipster IDE 插件/扩展, 可用于 Eclipse, VS Code and Atom, 创建JDL文件及其UML可视化。您也可以创建、导出或共享JDL模型的URL。

您可以通过运行 jhipster import-jdl your-jdl-file.jdl的JDL文件生成应用, 部署和实体,

如果您有一个现有的项目(使用jhipster import-jdl创建或使用jhipster命令行生成),则可以通过运行jhipster import-jdl your-jdl-file.jdl为该项目生成实体。确保在JHipster项目下执行此命令。

您还可以生成应用程序、实体,通过使用JHipster UML, 在生成的JHipster应用的根目录运行 jhipster-uml your-xmi-file.xmi --to-jdl。要了解有关JHipster UML的更多信息并安装它,请转到 JHipster UML documentation.

这可以作为使用 entity sub-generator 的替代方法,并且是建议的方法。其理念是,使用可视化工具 manage relationships 比使用经典的Yeoman问答更容易。

JDL项目available on GitHub, 它是一个开源项目,如jhipster(Apache 2.0许可证)。它还可以用作节点库来进行JDL解析。

如果您喜欢 JHipster Domain Language, the JDL Studio or the JHipster IDE 不要忘记点赞 GitHub - 谢谢!


JDL 文档:

  1. JDL Sample
  2. How to use it
    1. Importing a JDL file
    2. Exporting to a JDL file
  3. The language
    1. Application Declaration
    2. Entity Declaration
    3. Relationship Declaration
    4. Enumerations
    5. Blobs
    6. Option declaration
    7. Microservice-related options
    8. Annotations
    9. Deployment declaration
  4. Commenting
  5. All the relationships
  6. Constants
  7. Workflows
  8. Annexes
    1. Available application options
    2. Available deployment options
    3. Available field types and constraints
    4. Available options
  9. Issues and bugs

JDL Sample

The Oracle “Human Resources” sample application has been translated into JDL, and is available here. The same application is loaded by default in JDL-Studio and JHipster IDE as well.

If you’re looking for more samples, there is a repository for that right here.

How to use it

Importing a JDL file

You can then use JDL files to generate entities:

  • Simply create a file with the extension ‘.jh’ or ‘.jdl’,
  • Declare your applications, deployments, entities and relationships or create and download the file with JDL-Studio or JHipster IDE,
  • If you are creating only entities in then run jhipster import-jdl my_file.jdl in your JHipster application’s root folder.
  • If you are creating applications then just run jhipster import-jdl my_file.jdl in a folder.

and Voilà, you are done!

If you work in a team, perhaps you would like to have multiple files instead of one. We added this option so that you don’t manually concatenate all the files into one, you just have to run.

jhipster import-jdl my_file1.jdl my_file2.jdl

If you do not want to regenerate your entities while importing a JDL, you can use the --json-only flag to skip the entity creation part and create only the json files in .jhipster folder.

jhipster import-jdl ./my-jdl-file.jdl --json-only

By default import-jdl regenerates only entities that have changed, if you want all your entities to be regenerated then pass in the --force flag. Please note that this will overwrite all your local changes to the entity files

jhipster import-jdl ./my-jdl-file.jdl --force

If you want to use it in your project, you can add do so by doing:

  • NPM: npm install jhipster-core --save
  • Yarn: yarn add jhipster-core

to install it locally, and save it in your package.json file.

Exporting to a JDL file

If you already have entities in your application and wish to have a JDL file, fear not! You don’t have to write it from scratch as there’s a sub-generator that does that for you.

Simply do jhipster export-jdl <FILE_NAME> in your app’s root folder and you’ll have all your entities, relationships and options exporting in a single JDL file. Note: you can also not provide a file name to the sub-generation, a default one will be chosen.


The language

We tried to keep the syntax as friendly as we can for developers. You can do these things with it:

  • Declare applications with their options and entities,
  • Declare entities with their attributes,
  • Declare the relationships between them,
  • And declare some JHipster specific options.

If you wish to view the JDL’s grammar, there is an HTML file available here.

Application declaration

As of v2.0.0, application declaration is possible (compatible with JHipster v5).

To import one or several applications, you needn’t be in a JHipster application folder.

The most basic declaration is done as follows:

application {
  config {}
}

A JHipster application has a config with default values and using the previous syntax will ensure your application use the default values (as if you didn’t make any specific choice). The resulting application will have:

  • baseName: jhipster
  • applicationType: monolith
  • databaseType: sql
  • etc.

Now, if you want some custom options:

application {
  config {
    baseName myapp
    applicationType microservice
    prodDatabaseType postgresql
    buildTool gradle
  }
}

Those options are only a sample of what’s available in the JDL. The complete list of options is available in the annexes, here.

If you want more than one application, here’s how you do it:

application { // will be generated under the 'myFirstApp' folder
  config {
    baseName myFirstApp
  }
}

application { // will be generated under the 'mySecondApp' folder
  config {
    baseName mySecondApp
    applicationType microservice
  }
}

You can have as many application as you want in as many file as you wish: there’s no limitation.

Declaring entities is the most basic thing available and now you can set what entity should be generated in the applications you want.

Let’s improve the previous example:

application {
  config {
    baseName myMonolith
    applicationType monolith
  }
  entities * except C, D
}

application {
  config {
    baseName myGateway
    applicationType gateway
    serverPort 9042
  }
  entities * except A, B
}

application {
  config {
    baseName microserviceA
    applicationType microservice
  }
  entities C
}

application {
  config {
    baseName microserviceB
    applicationType microservice
    serverPort 8082
  }
  entities D
}

entity A
entity B
entity C
entity D

dto * with mapstruct
service * with serviceClass
paginate D with pager

Now, several things will happen when generating these applications and folders:

  • Four applications will be created:
    • myMonolith in ./myMonolith, with the server port 8080
    • myGateway in ./myGateway, with the server port 9042
    • microserviceA in ./microserviceA, with the server port 8081
      • Even though we didn’t specify a server port, JHipster sets one by default.
      • For microservices, the default one is 8081
      • For gateways and monoliths, it’s 8080
      • For UAA apps, it’s 9999
    • microserviceB in ./microserviceB with the server port 8082
  • Four entities will be generated
    • A and B in the monolith
    • C and D both in the gateway
      • C in the first microservice
      • D in the second microservice
  • The microservice option is implicit for C and D
    • Because they get generated on the two microservices, this option will be set by default.
  • Options work the same way as before

Note that the generator sets default values if they aren’t present (like the databaseType). JHipster Core does the exact same things.

Entity declaration

The entity declaration is done as follows:

entity <entity name> {
  <field name> <type> [<validation>*]
}
  • <entity name> is the name of the entity,
  • <field name> the name of one field of the entity,
  • <type> the JHipster supported type of the field,
  • and as an option <validation> the validations for the field.

The possible types and validations are those described here, if the validation requires a value, simply add (<value>) right after the name of the validation.

Here’s an example of a JDL code:

entity A
entity B
entity C
entity D {
  name String required
  address String required maxlength(100)
  age Integer required min(18)
}

Regexes are a bit special as they are used like this (from v1.3.6):

entity A {
  myString String required minlength(1) maxlength(42) pattern(/[A-Z]+/)
}

If you’re using the generator prior to v4.9.X, you’d need to use patterns like this pattern('[A-Z]+').

Because the JDL was made to be simple to use and read, if your entity is empty (no field), you can just declare an entity with entity A or entity A {}.

Note that JHipster adds a default id field so that you needn’t worry about it.

Relationship declaration

The relationships declaration is done as follows:

relationship (OneToMany | ManyToOne | OneToOne | ManyToMany) {
  <from entity>[{<relationship name>[(<display field>)]}] to <to entity>[{<relationship name>[(<display field>)]}]
}
  • (OneToMany | ManyToOne| OneToOne | ManyToMany) is the type of your relationship,
  • <from entity> is the name of the entity owner of the relationship: the source,
  • <to entity> is the name of the entity where the relationship goes to: the destination,
  • <relationship name> is the name of the field having the other end as type,
  • <display field> is the name of the field that should show up in select boxes (default: id),
  • required whether the injected field is required.
  • with jpaDerivedIdentifier whether @MapsId is used for the association (applicable only for one-to-one)

Here’s a simple example:

entity Book
entity Author

relationship ManyToOne {
  Book to Author
}

Declaring an injected field is optional, as one (or both) is set by default as needed. The previous example is equivalent to this one:

entity Book
entity Author

relationship ManyToOne {
  Book{author} to Author
}

Let’s make it more complex. A Book has one required Author, an Author has several Books.

entity Book
entity Author {
  name String required
}

relationship OneToMany {
  Author{book} to Book{writer(name) required}
}

Here, the Book class will have a required field named writer that will be linked through the name field of Author.

Of course, in real cases, you’d have a lot of relationships and always writing the same three lines could be tedious. That’s why you can declare something like:

entity A
entity B
entity C
entity D

relationship OneToOne {
  A{b} to B{a}
  B{c} to C
}
relationship ManyToMany {
  A{d} to D{a}
  C{d} to D{c}
}

The join is always done using the id field which is also the default field shown when editing a relation in the front-end. If another field should be shown instead, you can specify it like this:

entity A {
  name String required
}
entity B


relationship OneToOne {
  A{b} to B{a(name)}
}

JPA Derived Identifier - @MapsId can be declared as following which is currently only supported for one-to-one:

entity A {
  name String required
}
entity B


relationship OneToOne {
  A{b} to B{a(name)} with jpaDerivedIdentifier
}

This makes JHipster generate a REST resource that returns both the id and name of the linked entity to the front-end, so the name can be shown to the user instead.

Enumerations

To make Enums with JDL just do as follows:

  • Declare an Enum where you want in the file:

      enum Language {
        FRENCH, ENGLISH, SPANISH
      }
    
  • In an entity, add fields with the Enum as a type:

      entity Book {
        title String required,
        description String,
        language Language
      }
    

Blob (byte[])

JHipster gives a great choice as one can choose between an image type or any binary type. JDL lets you do the same. Just create a custom type (see DataType) with the editor, name it according to these conventions:

  • AnyBlob or just Blob to create a field of the “any” binary type;
  • ImageBlob to create a field meant to be an image.
  • TextBlob to create a field for a CLOB (long text).

And you can create as many DataTypes as you like.

Option declaration

In JHipster, you can specify options for your entities such as pagination or DTO. You can do the same with the JDL:

entity A {
  name String required
}
entity B
entity C

dto A, B with mapstruct

paginate A with infinite-scroll
paginate B with pagination
paginate C with pager  // pager is only available in AngularJS

service A with serviceClass
service C with serviceImpl

The keywords dto, paginate, service and with were added to the grammar to support these changes. If a wrong option is specified, JDL will inform you of that with a nice, red message and will just ignore it so as not to corrupt JHipster’s JSON files.

Service option

No services specified will create a resource class which will call the repository interface directly. This is the default and simplest option, see A. Service with serviceClass (see B) will make the resource call the service class which will call the repository interface. Service with serviceImpl (see C) will make a service interface which will be used by the resource class. The interface is implemented by an concrete class which will call the repository interface.

Using no service uless sure is the simplest option and good for CRUD. Use service with a Class if you will have a lot of business logic which will use multiple repositories making it ideal for a service class. Jhipsters are not fan of unnecessary Interfaces but if you like them go for service with impl.

entity A
entity B
entity C

// no service for A
service B with serviceClass
service C with serviceImpl

JDL also supports mass-option setting. it is possible to do:

entity A
entity B
...
entity Z

dto * with mapstruct
service all with serviceImpl
paginate C with pagination

Note that * and all are equivalent. Latest version introduces exclusions (which is quite a powerful option when setting options for every entity):

entity A
entity B
...
entity Z

dto * with mapstruct except A
service all with serviceImpl except A, B, C
paginate C with pagination

With JHipster, you can also tell whether you don’t want any client code, or server code. Even if you want to add a suffix to Angular-related files, you can do that in JHipster. Filtering options can be activated on a per entity basis: filter <entity name> or for all entities: filter *. In your JDL file, simply add these lines to do the same:

entity A
entity B
entity C

skipClient A
skipServer B
angularSuffix * with mySuperEntities
filter C

Finally, table names can also be specified (the entity’s name will be used by default):

entity A // A is the table's name here
entity B (the_best_entity) // the_best_entity is the table's name

As of JHipster v3, microservices can be created. You can specify some options to generate your entities in the JDL: the microservice’s name and the search engine.

Here is how you can specify your microservice’s name (the JHipster app’s name):

entity A
entity B
entity C

microservice * with mysuperjhipsterapp except C
microservice C with myotherjhipsterapp
search * with elasticsearch except C

The first option is used to tell JHipster that you want your microservice to deal with your entities, whereas the second specifies how and if you want your entities searched.

Annotations

Annotations are available since JHipster v5. Similarly to what’s possible in Java, annotations work the same way so that you annotate your entities with annotations options.

Take this JDL code for instance:

entity A
entity B
entity C

dto C with mapstruct
paginate * with pager except C
search A with elasticsearch

Here’s its equivalent with annotations:

@paginate(pager)
@search(elasticsearch)
entity A

@paginate(pager)
entity B

@dto(mapstruct)
entity C

While this adds more code than it actually removes, it’s actually useful when using multiple JDL files (with microservices for instance).

Deployment declaration

As of v3.6.0, deployment declaration is possible (compatible with JHipster v5.7 or above).

To import one or several deployments, you need not be in a JHipster application folder.

The most basic declaration is done as follows:

deployment {
  deploymentType docker-compose
  appsFolders [foo, bar]
  dockerRepositoryName "yourDockerLoginName"
}

A JHipster deployment has a config with default values for all other properties and using the previous syntax will ensure your deployment will use the default values (as if you didn’t make any specific choice). The resulting deployment will have:

  • deploymentType: docker-compose
  • appsFolders: foo, bar
  • dockerRepositoryName: yourDockerLoginName
  • serviceDiscoveryType: eureka
  • gatewayType: zuul
  • directoryPath: ../
  • etc.

Now, if you want some custom options:

deployment {
  deploymentType kubernetes
  appsFolders [store, invoice, notification, product]
  dockerRepositoryName "yourDockerLoginName"
  serviceDiscoveryType no
  istio autoInjection
  istioRoute true
  kubernetesServiceType Ingress
  kubernetesNamespace jhipster
  ingressDomain "jhipster.192.168.99.100.nip.io"
}

Those options are only a sample of what’s available in the JDL. The complete list of options is available in the annexes, here.

If you want more than one deployment, here’s how you do it:

// will be created under 'docker-compose' folder
deployment {
  deploymentType docker-compose
  appsFolders [foo, bar]
  dockerRepositoryName "yourDockerLoginName"
}

// will be created under 'kubernetes' folder
deployment {
  deploymentType kubernetes
  appsFolders [foo, bar]
  dockerRepositoryName "yourDockerLoginName"
}

You can have one deployment per deploymentType. The applications defined in appsFolders should be in the same folder where you are creating deployments or in the folder defined in directoryPath. For example for above you need to have a folder structure like this

.
├── yourJdlFile.jdl
├── foo
├── bar
├── kubernetes // will created by the JDL
└── docker-compose // will created by the JDL

Commenting & Javadoc

It is possible to add Javadoc & comments to JDL files.
Just like in Java, this example demonstrates how to add Javadoc comments:

/**
 * Class comments.
 * @author The JHipster team.
 */
entity MyEntity { // another form of comment
  /** A required attribute */
  myField String required
  mySecondField String // another form of comment
}

/**
 * Second entity.
 */
entity MySecondEntity {}

relationship OneToMany {
  /** This is possible too! */
  MyEntity{mySecondEntity}
  to
  /**
   * And this too!
   */
  MySecondEntity{myEntity}
}

These comments will later be added as Javadoc comments by JHipster.

JDL possesses its own kind of comment:

// an ignored comment
/** not an ignored comment */

Therefore, anything that starts with // is considered an internal comment for JDL, and will not be counted as Javadoc.

Please note that the JDL Studio directives that start with # will be ignored during parsing.

Another form of comments are the following comments:

entity A {
  name String /** My super field */
  count Integer /** My other super field */
}

Here A’s name will be commented with My super field, B with My other super field. Yes, commas are not mandatory but it’s wiser to have them so as not to make mistakes in the code. If you want to mix commas and following comments, beware!

entity A {
  name String, /** My comment */
  count Integer
}

A’s name won’t have the comment, because the count will.

All the relationships

Explanation on how to create relationships with JDL.

One-to-One

A bidirectional relationship where the Car has a Driver, and the Driver has a Car.

entity Driver
entity Car
relationship OneToOne {
  Car{driver} to Driver{car}
}

A Unidirectional example where a Citizen has a Passport, but the Passport has no access to sole its owner.

entity Citizen
entity Passport
relationship OneToOne {
  Citizen to Passport
}

// using @MapsId
relationship OneToOne {
      Citizen to Passport with jpaDerivedIdentifier
}

One-to-Many

A bidirectional relationship where the Owner has none, one or more Car objects, and the Car knows its owner.

entity Owner
entity Car
relationship OneToMany {
  Owner{car} to Car{owner}
}

Unidirectional versions for this relationship are not supported by JHipster, but it would look like this:

entity Owner
entity Car
relationship OneToMany {
  Owner to Car
}

Many-to-One

The reciprocal version of One-to-Many relationships is the same as previously. The unidirectional version where the Car knows its owners:

entity Owner
entity Car
relationship ManyToOne {
  Car to Owner
}

Many-to-Many

Finally, in this example we have the Car that knows of its drivers, and the Driver object can access its cars.

entity Driver
entity Car
relationship ManyToMany {
  Car{driver} to Driver{car}
}

Please note that the owning side of the relationship has to be on the left side

Constants

As of JHipster Core v1.2.7, the JDL supports numerical constants. Here is an example:

DEFAULT_MIN_LENGTH = 1
DEFAULT_MAX_LENGTH = 42
DEFAULT_MIN_BYTES = 20
DEFAULT_MAX_BYTES = 40
DEFAULT_MIN = 0
DEFAULT_MAX = 41

entity A {
  name String minlength(DEFAULT_MIN_LENGTH) maxlength(DEFAULT_MAX_LENGTH)
  content TextBlob required
  count Integer min(DEFAULT_MIN) max(DEFAULT_MAX)
}

Workflows

Monolith workflow

There’s no special workflow here:

  • Create your application
  • Create your JDL file
  • Import it

Microservice workflow

Dealing with microservices is a bit trickier, but the JDL gives you some options to handle your entities as you see fit.

With the microservice <ENTITIES> with <MICROSERVICE_APP_NAME> you can specify which entity gets generated in which microservice. Take this setup for instance:

entity A
entity B
entity C

microservice A with firstMS
microservice B with secondMS

Given two JHipster applications (‘firstMS’ and ‘secondMS’), here’s what you’re going to get if you import the JDL file in the two applications:

  • In ‘firstMS’, entities A and C will be generated.
  • In ‘secondMS’, entities B and C will be generated.

C gets generated in both because if there’s no microservice option specifying where this entity gets generated, it will be generated everywhere. If you decide to import this JDL in a monolith app, every entity will be generated (monoliths don’t have restriction options in the JDL).

Note: if you want to make the same entity be generated in two different microservices, you can write two JDL files instead of updating the JDL file. everytime.

The previous example couldn’t have been written like this:

entity A
entity B
entity C

microservice * except B with firstMS
microservice * except A with secondMS

Here’s the result:

  • In ‘firstMS’, only the entity C will be generated
  • In ‘secondMS’, entities B and C will be generated. It’s because, at parsing-time, if an option overlaps with another, the latter takes precedence.

You can also create entire microservice stack using JDL, see this blog post for example


Annexes

Available application options

Here are the application options supported in the JDL:

JDL option name Default value Possible values Comment
applicationType monolith monolith, microservice, gateway, uaa
baseName jhipster
packageName com.mycompany.myapp Sets the packageFolder option
authenticationType jwt or uaa jwt, session, uaa, oauth2 uaa for UAA apps, jwt otherwise
uaaBaseName Mandatory for gateway and microservices if auth type is uaa, must be between double-quotes
buildTool maven maven, gradle
databaseType sql sql, mongodb, cassandra, couchbase, no
devDatabaseType h2Disk h2Disk, h2Memory, * * + the prod database type
prodDatabaseType mysql mysql, mariadb, mssql, postgresql, oracle, no
cacheProvider ehcache or hazelcast ehcache, hazelcast, infinispan, no ehcache for monoliths and gateways, hazelcast otherwise
enableHibernateCache true
clientFramework angularX angularX, react
useSass false
clientPackageManager npm npm, yarn
entitySuffix Suffix for entities. false for empty string.
dtoSuffix DTO Suffix for DTOs. false for empty string.
jhiPrefix jhi
enableTranslation true
nativeLanguage en Any language supported by JHipster
languages [en, fr] Languages available in JHipster Braces are mandatory
enableSwaggerCodegen false
serviceDiscoveryType false eureka, consul, no
messageBroker false kafka, false
searchEngine false elasticsearch, false
serverPort 8080, 8081 or 9999 Depends on the app type
websocket false spring-websocket, false
testFrameworks [] protractor, cucumber, gatling Braces mandatory
skipClient false
skipServer false
skipUserManagement true

Available deployment options

Here are the application options supported in the JDL:

JDL option name Default value Possible values Comment
deploymentType docker-compose docker-compose, kubernetes, openshift
directoryPath "../" Relative path. Must be in double quotes
appsFolders [] Directory names for the applications separated by comma. Must be a list, example [foo, bar]
clusteredDbApps [] Directory names for the applications with clustered DB separated by comma. Must be a list, example [foo, bar]
gatewayType zuul zuul, traefik Value is ignored when serviceDiscoveryType is `no`
monitoring no no, elk, prometheus
consoleOptions [] [curator, zipkin] Must be a list
serviceDiscoveryType eureka eureka, consul, no
dockerRepositoryName The name or URL of the docker repository. Must be in double quotes
dockerPushCommand "docker push" The docker push command to use. Must be in double quotes
kubernetesNamespace default Applicable only when deploymentType is kubernetes
kubernetesServiceType LoadBalancer LoadBalancer, NodePort, Ingress Applicable only when deploymentType is kubernetes
ingressDomain The domain for Ingress when kubernetesServiceType is `Ingress`. Must be in double quotes. Applicable only when deploymentType is kubernetes
istio no no, manualInjection, autoInjection Applicable only when deploymentType is kubernetes
istioRoute false Applicable only when deploymentType is kubernetes
openshiftNamespace default Applicable only when deploymentType is openshift
storageType ephemeral ephemeral, persistent Applicable only when deploymentType is openshift

Available field types and constraints

Here are the types supported in the JDL:

Common databases:

  • PostgreSQL
  • MySQL
  • MariaDB
  • Oracle
  • MsSQL
  • MongoDB
  • Couchbase
Common databases Cassandra Validations
String String required, minlength, maxlength, pattern, unique
Integer Integer required, min, max, unique
Long Long required, min, max, unique
BigDecimal BigDecimal required, min, max, unique
Float Float required, min, max, unique
Double Double required, min, max, unique
Enum required, unique
Boolean Boolean required, unique
LocalDate required, unique
Date required, unique
ZonedDateTime required, unique
UUID required, unique
Blob required, minbytes, maxbytes, unique
AnyBlob required, minbytes, maxbytes, unique
ImageBlob required, minbytes, maxbytes, unique
TextBlob required, unique
Instant Instant required, unique

Available options

Unary options

These options don’t have any value:

  • skipClient
  • skipServer
  • noFluentMethod
  • filter

They can be used like this: <OPTION> <ENTITIES | * | all> except? <ENTITIES>

Binary options

These options take values:

  • dto (mapstruct)
  • service (serviceClass, serviceImpl)
  • paginate (pager, pagination, infinite-scroll)
  • search (elasticsearch)
  • microservice (custom value)
  • angularSuffix (custom value)
  • clientRootFolder (custom value)

Issues and bugs

JDL is available on GitHub, and follows the same contributing guidelines as JHipster.

Please use our project for submitting issues and Pull Requests concerning the library itself.

When submitting anything, you must be as precise as possible:

  • One posted issue must only have one problem (or one demand/question);
  • Pull requests are welcome, but the commits must be ‘atomic’ to really be understandable.