jhipster / generator-jhipster

JHipster is a development platform to quickly generate, develop, & deploy modern web applications & microservice architectures.
https://www.jhipster.tech
Apache License 2.0
21.53k stars 4.02k forks source link

jHipster 6.0.1: Wrong update component code for OneToMany / ManyToOne relationships #9780

Closed ksilz closed 5 years ago

ksilz commented 5 years ago
Overview of the issue

jHipster 6.0.1 with Angular, Java 11, and H2/PostgreSQL produces two kind of errors for "OneToMany / ManyToOne relationships":

Motivation for or Use Case

My data model, expressed in the JDL, works. The generated code doesn't.

Reproduce the error
Related issues
Suggest a Fix

I fixed this on the update-forms-fixed branch of the sample GitHub repository. See the link for the changes in these files:

JHipster Version(s)

6.0.1

JHipster configuration
JHipster Version(s)
j-hipster-6-java-11-test@0.0.0 /Users/karsten/workspaces/me/services/better-projects-faster/bug-reports/jhipster-6.0.1-java-11-test
└── generator-jhipster@6.0.1 
JHipster configuration, a .yo-rc.json file generated in the root folder
.yo-rc.json file
{
  "generator-jhipster": {
    "databaseType": "sql",
    "devDatabaseType": "h2Disk",
    "enableHibernateCache": true,
    "enableSwaggerCodegen": false,
    "enableTranslation": true,
    "jhiPrefix": "bpf",
    "languages": ["en", "de"],
    "messageBroker": false,
    "nativeLanguage": "en",
    "packageName": "com.betterprojectsfaster.jhipster.test.jhipster6java11",
    "packageFolder": "com/betterprojectsfaster/jhipster/test/jhipster6java11",
    "prodDatabaseType": "postgresql",
    "searchEngine": false,
    "serviceDiscoveryType": false,
    "skipClient": false,
    "skipServer": false,
    "testFrameworks": ["protractor", "cucumber"],
    "websocket": "spring-websocket",
    "baseName": "jHipster6Java11Test",
    "applicationType": "monolith",
    "authenticationType": "jwt",
    "buildTool": "gradle",
    "cacheProvider": "ehcache",
    "clientFramework": "angularX",
    "useSass": true,
    "clientPackageManager": "npm",
    "jhipsterVersion": "6.0.1",
    "skipUserManagement": false,
    "serverPort": "8080",
    "jwtSecretKey": "bXktc2VjcmV0LXRva2VuLXRvLWNoYW5nZS1pbi1wcm9kdWN0aW9uLWFuZC10by1rZWVwLWluLWEtc2VjdXJlLXBsYWNl",
    "clientTheme": "none",
    "entitySuffix": "",
    "dtoSuffix": "DTO",
    "otherModules": []
  },
  "entities": ["Product", "Address", "ShoppingOrder", "ProductOrder", "Shipment"]
}

JDL for the Entity configuration(s) entityName.json files generated in the .jhipster directory
JDL entity definitions
entity Product {
  name String required unique minlength(2) maxlength(90),
  price Float required min(0),
  description TextBlob required,
  picture ImageBlob required,
  specification AnyBlob,
  category ProductCategory,
  inventory Integer required min(0)
}
entity Address {
  addressLine1 String required minlength(2) maxlength(80),
  addressLine2 String minlength(2) maxlength(80),
  city String minlength(2) maxlength(80),
  postalCode String minlength(5) maxlength(5)
}
entity ShoppingOrder {
  name String required unique minlength(2) maxlength(90),
  totalAmount Float min(0),
  ordered ZonedDateTime
}
entity ProductOrder {
  amount Integer required min(0) max(5)
}
entity Shipment {
  shippedAt ZonedDateTime required
}

enum ProductCategory {
  Laptop,
  Desktop,
  Phone,
  Tablet,
  Accessory
}

relationship OneToOne {
  Shipment{order(name) required} to ShoppingOrder{shipment required}
}
relationship OneToMany {
  Product{order required} to ProductOrder{product(name) required},
  ShoppingOrder{orders required} to ProductOrder{overallOrder(name)}
}
relationship ManyToOne {
  Address{user(login) required} to User,
  ShoppingOrder{buyer(login) required} to User,
  ProductOrder{buyer(login) required} to User,
  Shipment{shippedBy(login) required} to User
}

dto Product, Address, ShoppingOrder, ProductOrder, Shipment with mapstruct
service Product, Address, ShoppingOrder, ProductOrder, Shipment with serviceClass

Environment and Tools

openjdk version "11.0.3" 2019-04-16 OpenJDK Runtime Environment AdoptOpenJDK (build 11.0.3+7) OpenJDK 64-Bit Server VM AdoptOpenJDK (build 11.0.3+7, mixed mode)

git version 2.21.0

node: v10.15.3

npm: 6.9.0

Docker version 18.09.2, build 6247962

docker-compose version 1.23.2, build 1110ad01

Entity configuration(s) entityName.json files generated in the .jhipster directory

Address.json

{
    "name": "Address",
    "fields": [
        {
            "fieldName": "addressLine1",
            "fieldType": "String",
            "fieldValidateRules": [
                "required",
                "minlength",
                "maxlength"
            ],
            "fieldValidateRulesMinlength": 2,
            "fieldValidateRulesMaxlength": 80
        },
        {
            "fieldName": "addressLine2",
            "fieldType": "String",
            "fieldValidateRules": [
                "minlength",
                "maxlength"
            ],
            "fieldValidateRulesMinlength": 2,
            "fieldValidateRulesMaxlength": 80
        },
        {
            "fieldName": "city",
            "fieldType": "String",
            "fieldValidateRules": [
                "minlength",
                "maxlength"
            ],
            "fieldValidateRulesMinlength": 2,
            "fieldValidateRulesMaxlength": 80
        },
        {
            "fieldName": "postalCode",
            "fieldType": "String",
            "fieldValidateRules": [
                "minlength",
                "maxlength"
            ],
            "fieldValidateRulesMinlength": 5,
            "fieldValidateRulesMaxlength": 5
        }
    ],
    "relationships": [
        {
            "relationshipType": "many-to-one",
            "otherEntityName": "user",
            "otherEntityRelationshipName": "address",
            "relationshipValidateRules": "required",
            "relationshipName": "user",
            "otherEntityField": "login"
        }
    ],
    "changelogDate": "20190522100402",
    "entityTableName": "address",
    "dto": "mapstruct",
    "pagination": "no",
    "service": "serviceClass",
    "jpaMetamodelFiltering": false,
    "fluentMethods": true,
    "clientRootFolder": "",
    "applications": [
        "jHipster6Java11Test"
    ]
}

Product.json

{
    "name": "Product",
    "fields": [
        {
            "fieldName": "name",
            "fieldType": "String",
            "fieldValidateRules": [
                "required",
                "unique",
                "minlength",
                "maxlength"
            ],
            "fieldValidateRulesMinlength": 2,
            "fieldValidateRulesMaxlength": 90
        },
        {
            "fieldName": "price",
            "fieldType": "Float",
            "fieldValidateRules": [
                "required",
                "min"
            ],
            "fieldValidateRulesMin": 0
        },
        {
            "fieldName": "description",
            "fieldType": "byte[]",
            "fieldTypeBlobContent": "text",
            "fieldValidateRules": [
                "required"
            ]
        },
        {
            "fieldName": "picture",
            "fieldType": "byte[]",
            "fieldTypeBlobContent": "image",
            "fieldValidateRules": [
                "required"
            ]
        },
        {
            "fieldName": "specification",
            "fieldType": "byte[]",
            "fieldTypeBlobContent": "any"
        },
        {
            "fieldName": "category",
            "fieldType": "ProductCategory",
            "fieldValues": "Laptop,Desktop,Phone,Tablet,Accessory"
        },
        {
            "fieldName": "inventory",
            "fieldType": "Integer",
            "fieldValidateRules": [
                "required",
                "min"
            ],
            "fieldValidateRulesMin": 0
        }
    ],
    "relationships": [
        {
            "relationshipType": "one-to-many",
            "otherEntityName": "productOrder",
            "otherEntityRelationshipName": "product",
            "relationshipValidateRules": "required",
            "relationshipName": "order"
        }
    ],
    "changelogDate": "20190522100401",
    "entityTableName": "product",
    "dto": "mapstruct",
    "pagination": "no",
    "service": "serviceClass",
    "jpaMetamodelFiltering": false,
    "fluentMethods": true,
    "clientRootFolder": "",
    "applications": [
        "jHipster6Java11Test"
    ]
}

ProductOrder.json

{
    "name": "ProductOrder",
    "fields": [
        {
            "fieldName": "amount",
            "fieldType": "Integer",
            "fieldValidateRules": [
                "required",
                "min",
                "max"
            ],
            "fieldValidateRulesMin": 0,
            "fieldValidateRulesMax": 5
        }
    ],
    "relationships": [
        {
            "relationshipType": "many-to-one",
            "otherEntityName": "user",
            "otherEntityRelationshipName": "productOrder",
            "relationshipValidateRules": "required",
            "relationshipName": "buyer",
            "otherEntityField": "login"
        },
        {
            "relationshipType": "many-to-one",
            "otherEntityName": "product",
            "otherEntityRelationshipName": "order",
            "relationshipValidateRules": "required",
            "relationshipName": "product",
            "otherEntityField": "name"
        },
        {
            "relationshipType": "many-to-one",
            "otherEntityName": "shoppingOrder",
            "otherEntityRelationshipName": "orders",
            "relationshipName": "overallOrder",
            "otherEntityField": "name"
        }
    ],
    "changelogDate": "20190522100404",
    "entityTableName": "product_order",
    "dto": "mapstruct",
    "pagination": "no",
    "service": "serviceClass",
    "jpaMetamodelFiltering": false,
    "fluentMethods": true,
    "clientRootFolder": "",
    "applications": [
        "jHipster6Java11Test"
    ]
}

Shipment.json

{
    "name": "Shipment",
    "fields": [
        {
            "fieldName": "shippedAt",
            "fieldType": "ZonedDateTime",
            "fieldValidateRules": [
                "required"
            ]
        }
    ],
    "relationships": [
        {
            "relationshipType": "one-to-one",
            "otherEntityName": "shoppingOrder",
            "otherEntityRelationshipName": "shipment",
            "relationshipValidateRules": "required",
            "relationshipName": "order",
            "otherEntityField": "name",
            "ownerSide": true
        },
        {
            "relationshipType": "many-to-one",
            "otherEntityName": "user",
            "otherEntityRelationshipName": "shipment",
            "relationshipValidateRules": "required",
            "relationshipName": "shippedBy",
            "otherEntityField": "login"
        }
    ],
    "changelogDate": "20190522100405",
    "entityTableName": "shipment",
    "dto": "mapstruct",
    "pagination": "no",
    "service": "serviceClass",
    "jpaMetamodelFiltering": false,
    "fluentMethods": true,
    "clientRootFolder": "",
    "applications": [
        "jHipster6Java11Test"
    ]
}

ShoppingOrder.json

{
    "name": "ShoppingOrder",
    "fields": [
        {
            "fieldName": "name",
            "fieldType": "String",
            "fieldValidateRules": [
                "required",
                "unique",
                "minlength",
                "maxlength"
            ],
            "fieldValidateRulesMinlength": 2,
            "fieldValidateRulesMaxlength": 90
        },
        {
            "fieldName": "totalAmount",
            "fieldType": "Float",
            "fieldValidateRules": [
                "min"
            ],
            "fieldValidateRulesMin": 0
        },
        {
            "fieldName": "ordered",
            "fieldType": "ZonedDateTime"
        }
    ],
    "relationships": [
        {
            "relationshipType": "one-to-many",
            "otherEntityName": "productOrder",
            "otherEntityRelationshipName": "overallOrder",
            "relationshipValidateRules": "required",
            "relationshipName": "orders"
        },
        {
            "relationshipType": "many-to-one",
            "otherEntityName": "user",
            "otherEntityRelationshipName": "shoppingOrder",
            "relationshipValidateRules": "required",
            "relationshipName": "buyer",
            "otherEntityField": "login"
        },
        {
            "relationshipType": "one-to-one",
            "otherEntityName": "shipment",
            "otherEntityRelationshipName": "order",
            "relationshipValidateRules": "required",
            "relationshipName": "shipment",
            "ownerSide": false
        }
    ],
    "changelogDate": "20190522100403",
    "entityTableName": "shopping_order",
    "dto": "mapstruct",
    "pagination": "no",
    "service": "serviceClass",
    "jpaMetamodelFiltering": false,
    "fluentMethods": true,
    "clientRootFolder": "",
    "applications": [
        "jHipster6Java11Test"
    ]
}
Browsers and Operating System

macOS 10.14.5, all browsers

devWhyqueue commented 5 years ago

Got a similar problem with the dropdown menus in One-To-Many-Relationships. Empty \<option>-Tags are created in the single-\<select> for the related entities but no actual attribute (like id or name) is displayed.

clementdessoude commented 5 years ago

The issue is comming from the entity definition, and particularly the use of required.

For instance, for the one-to-one relationship between Shipment and ShipmentOrder, you make the relationship required from both side. I guess there could be some use cases where that can be useful, but the logic depend on your business, and cannot be automated, IMO. Same for one-to-many relationship with required on both sides.

For the moment, what is generated is (for the one-to-one relationship, but the logic is the same for the others):

What you did is deleting this check : that lead to deleting the required rule on the ShoppingOrder side

ksilz commented 5 years ago

Hello @clement26695! Thank you for your comments!

I agree with you - having a two-sided relationship required on both ends is not very useful, I need to think about why I did this some more.

But from what I could see, your pull request only fixes one bug I reported here — the one with the "left-over check". It seems the other one still stands:

How will that be fixed?

clementdessoude commented 5 years ago

I didn't test yet, but I think #9768 fixed the rest of it. Could you test to see if it solved your issue ? Otherwise, I'll test it tomorrow

clementdessoude commented 5 years ago

I updated the PR. It should be good now

ksilz commented 5 years ago

@clement26695 I'm testing it with the latest version of the jHipster main branch.

ksilz commented 5 years ago

@clement26695 I wasn't able to test with the latest jHipster main branch. I think I got the dev version of the jHipster generator and the Java library project installed correctly. But then the generated entities wouldn't work, so I gave up. :-(