Skip to content

Commit

Permalink
feat: composite id backend support
Browse files Browse the repository at this point in the history
  • Loading branch information
yelhouti committed Oct 10, 2022
1 parent f785acd commit ffda950
Show file tree
Hide file tree
Showing 44 changed files with 713 additions and 346 deletions.
53 changes: 10 additions & 43 deletions generators/bootstrap-application/generator.spec.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,7 @@ const filterEntity = entity => ({
ownFields: '[ownFields] getter',
fields: '[fields] getter',
derivedFields: '[derivedFields] getter',
ids: entity.primaryKey.ids.map(id => ({
...id,
field: `[field] ${id.field.fieldName}`,
})),
ids: '[ids] getter',
},
});

Expand Down Expand Up @@ -207,6 +204,8 @@ Object {
"fieldName": "id",
"fieldNameAsDatabaseColumn": "id",
"fieldNameCapitalized": "Id",
"fieldNameDotted": "id",
"fieldNameDottedAsserted": "id!",
"fieldNameHumanized": "ID",
"fieldNameUnderscored": "id",
"fieldTranslationKey": "global.field.id",
Expand Down Expand Up @@ -517,19 +516,7 @@ Object {
"fields": "[fields] getter",
"hasLong": true,
"hasUUID": false,
"ids": Array [
Object {
"autoGenerate": true,
"field": "[field] id",
"getter": "getId",
"name": "id",
"nameCapitalized": "Id",
"nameDotted": "id",
"nameDottedAsserted": "id!",
"relationshipsPath": Array [],
"setter": "setId",
},
],
"ids": "[ids] getter",
"name": "id",
"nameCapitalized": "Id",
"ownFields": "[ownFields] getter",
Expand Down Expand Up @@ -631,6 +618,8 @@ Object {
"fieldName": "id",
"fieldNameAsDatabaseColumn": "id",
"fieldNameCapitalized": "Id",
"fieldNameDotted": "id",
"fieldNameDottedAsserted": "id!",
"fieldNameHumanized": "Id",
"fieldNameUnderscored": "id",
"fieldTranslationKey": "jhipsterApp.entityA.id",
Expand Down Expand Up @@ -739,19 +728,7 @@ Object {
"fields": "[fields] getter",
"hasLong": false,
"hasUUID": true,
"ids": Array [
Object {
"autoGenerate": true,
"field": "[field] id",
"getter": "getId",
"name": "id",
"nameCapitalized": "Id",
"nameDotted": "id",
"nameDottedAsserted": "id!",
"relationshipsPath": Array [],
"setter": "setId",
},
],
"ids": "[ids] getter",
"name": "id",
"nameCapitalized": "Id",
"ownFields": "[ownFields] getter",
Expand Down Expand Up @@ -909,6 +886,8 @@ Object {
"fieldName": "id",
"fieldNameAsDatabaseColumn": "id",
"fieldNameCapitalized": "Id",
"fieldNameDotted": "id",
"fieldNameDottedAsserted": "id!",
"fieldNameHumanized": "Id",
"fieldNameUnderscored": "id",
"fieldTranslationKey": "jhipsterApp.entityA.id",
Expand Down Expand Up @@ -1017,19 +996,7 @@ Object {
"fields": "[fields] getter",
"hasLong": false,
"hasUUID": true,
"ids": Array [
Object {
"autoGenerate": true,
"field": "[field] id",
"getter": "getId",
"name": "id",
"nameCapitalized": "Id",
"nameDotted": "id",
"nameDottedAsserted": "id!",
"relationshipsPath": Array [],
"setter": "setId",
},
],
"ids": "[ids] getter",
"name": "id",
"nameCapitalized": "Id",
"ownFields": "[ownFields] getter",
Expand Down
15 changes: 15 additions & 0 deletions generators/database-changelog-liquibase/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,10 @@ module.exports = class extends BaseBlueprintGenerator {
// fakeDataCount must be limited to the size of required unique relationships.
Object.defineProperty(this.entity, 'fakeDataCount', {
get: () => {
const relatedRequiredEntities = this._computeRelatedRequiredEntities(this.entity);
if (relatedRequiredEntities.some(e => e.fakeDataCount === 0)) {
return 0;
}
const uniqueRelationships = this.entity.relationships.filter(rel => rel.unique && (rel.relationshipRequired || rel.id));
return _.min([this.entity.liquibaseFakeData.length, ...uniqueRelationships.map(rel => rel.otherEntity.fakeDataCount)]);
},
Expand Down Expand Up @@ -411,4 +415,15 @@ module.exports = class extends BaseBlueprintGenerator {
relationship.columnDataType = relationship.otherEntity.columnType;
return relationship;
}

_computeRelatedRequiredEntities(entity, firstEntity = entity, computedEntities = []) {
const newEntities = entity.relationships
.filter(r => (r.relationshipRequired || r.id) && r.otherEntity !== firstEntity && !computedEntities.includes(r.otherEntity))
.map(r => r.otherEntity);
if (newEntities.length) {
computedEntities.push(...newEntities);
newEntities.forEach(e => this._computeRelatedRequiredEntities(e), firstEntity, computedEntities);
}
return computedEntities;
}
};
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,12 @@
<%_ } _%>
<%_ for (relationship of relationships) {
if ((relationship.relationshipType === 'many-to-one' || (relationship.relationshipType === 'one-to-one' && relationship.ownerSide === true))
&& !relationship.id) {
&& (!relationship.id || entity.primaryKey.composite)) {
relationship.otherEntity.primaryKey.fields.forEach(idField => {
const uniqueConstraintName = relationship.relationshipType === 'one-to-one' ? getUXConstraintName(entity.entityTableName, relationship.columnName + '_' + idField.columnName, entity.prodDatabaseType) : null;
const uniqueConstraintName = relationship.relationshipType === 'one-to-one' ? getUXConstraintName(entity.entityTableName, relationship.columnNamePrefix + idField.columnName, entity.prodDatabaseType) : null;
_%>
<column name="<%= relationship.columnName %>_<%= idField.columnName %>" type="<%= idField.columnType %>">
<constraints nullable="<%= relationship.nullable %>"<% if (uniqueConstraintName) { %> unique="true" uniqueConstraintName="<%= uniqueConstraintName %>"<% } %> />
<column name="<%= relationship.columnNamePrefix %><%= idField.columnName %>" type="<%= idField.columnType %>">
<constraints <% if (relationship.id) { %>primaryKey="true" <% } %>nullable="<%= relationship.nullable %>"<% if (uniqueConstraintName) { %> unique="true" uniqueConstraintName="<%= uniqueConstraintName %>"<% } %> />
</column>
<%_ });
}
Expand All @@ -74,7 +74,7 @@ _%>
<createTable tableName="<%= relationship.joinTable.name %>">
<%_ for (field of relationship.otherEntity.primaryKey.fields) { _%>
<column name="<%= relationship.columnName %>_<%= field.columnName %>" type="<%= field.columnType %>">
<column name="<%= relationship.columnNamePrefix %><%= field.columnName %>" type="<%= field.columnType %>">
<constraints nullable="false"/>
</column>
<%_ } _%>
Expand All @@ -85,7 +85,7 @@ _%>
<%_ } _%>
</createTable>
<addPrimaryKey columnNames="<%= entity.primaryKey.fields.map(field => `${entity.entityTableName}_${field.columnName}`).join(', ') %>, <%= relationship.otherEntity.primaryKey.fields.map(field => `${relationship.columnName}_${field.columnName}`).join(', ') %>" tableName="<%= relationship.joinTable.name %>"/>
<addPrimaryKey columnNames="<%= entity.primaryKey.fields.map(field => `${entity.entityTableName}_${field.columnName}`).join(', ') %>, <%= relationship.otherEntity.primaryKey.fields.map(field => `${relationship.columnNamePrefix}${field.columnName}`).join(', ') %>" tableName="<%= relationship.joinTable.name %>"/>
<%_ } _%>
</changeSet>
<%_ } _%>
Expand Down Expand Up @@ -113,17 +113,12 @@ _%>
<column name="<%= field.columnName %>_content_type" type="string"/>
<%_ } _%>
<%_ } _%>
<%_ for (relationship of relationships) {
if (relationship.relationshipValidate === true && relationship.relationshipRequired
&& (relationship.relationshipType === "many-to-one"
|| (relationship.relationshipType === "one-to-one" && relationship.ownerSide === true
&& (relationship.id == null || relationship.id === false))
)) {
_%>
<%_ for (field of relationship.otherEntity.primaryKey.fields) { _%>
<column name="<%= relationship.columnName %>_<%= field.columnName %>" type="<%= field.loadColumnType %>"/>
<%_ } _%>
<%_ } _%>
<%_ const relationshipsToGenerate = [...relationships.filter(r => r.id && entity.primaryKey.composite), ...relationships.filter(r => !r.id && r.relationshipRequired && (r.relationshipManyToOne || (r.relationshipOneToOne && r.ownerSide)))];
for (relationship of relationshipsToGenerate) {
_%>
<%_ for (field of relationship.otherEntity.primaryKey.fields) { _%>
<column name="<%= relationship.columnNamePrefix %><%= field.columnName %>" type="<%= field.loadColumnType %>"/>
<%_ } _%>
<%_ } _%>
<!-- jhipster-needle-liquibase-add-loadcolumn - JHipster (and/or extensions) can add load columns here -->
</loadData>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,9 @@ for (field of fieldsToGenerate) {
header.push(field.columnName + '_content_type');
}
}
for (relationship of relationships) {
const { relationshipType, joinColumnNames } = relationship;
if (
(relationshipType === "many-to-one"
|| (relationshipType === "one-to-one" && relationship.ownerSide === true
&& (relationship.id == null || relationship.id === false))
) && (relationship.relationshipValidate === true && relationship.relationshipRequired)
) {
header.push(joinColumnNames[0]);
}
const relationshipsToGenerate = [...relationships.filter(r => r.id && entity.primaryKey.composite), ...relationships.filter(r => !r.id && r.relationshipRequired && (r.relationshipManyToOne || (r.relationshipOneToOne && r.ownerSide)))];
for (relationship of relationshipsToGenerate) {
header.push(...relationship.joinColumnNames);
}
table.push(header);
Expand All @@ -59,27 +52,21 @@ for (lineNb = 0; lineNb < entity.fakeDataCount; lineNb++) {
}
line.push(data);
if (field.columnType === 'blob' || field.columnType === 'longblob') {
if (field.fieldType === 'byte[]' && field.fieldTypeBlobContent !== 'text') {
line.push('image/png');
}
}
for (relationship of relationships) {
const relationshipType = relationship.relationshipType;
if (
(relationshipType === "many-to-one"
|| (relationshipType === "one-to-one" && relationship.ownerSide === true
&& (relationship.id == null || relationship.id === false))
) && (relationship.relationshipValidate === true && relationship.relationshipRequired)
) {
const otherLiquibaseFakeData = relationship.otherEntity.liquibaseFakeData;
let relationshipRow = lineNb;
if (relationshipRow >= relationship.otherEntity.fakeDataCount && !relationship.unique) {
relationshipRow = entity.faker.datatype.number({min: 1, max: relationship.otherEntity.fakeDataCount}) - 1;
}
if (relationshipRow < relationship.otherEntity.fakeDataCount) {
line.push(otherLiquibaseFakeData[relationshipRow][relationship.otherEntity.primaryKey.name]);
}
for (relationship of relationshipsToGenerate) {
let relationshipRow = lineNb;
if (relationshipRow >= relationship.otherEntity.fakeDataCount && !relationship.unique) {
relationshipRow = entity.faker.datatype.number({min: 1, max: relationship.otherEntity.fakeDataCount}) - 1;
}
if (relationshipRow < relationship.otherEntity.fakeDataCount) {
relationship.otherEntity.primaryKey.fields.forEach(field => {
const otherLiquibaseFakeData = field.entity.liquibaseFakeData;
line.push(otherLiquibaseFakeData[relationshipRow][field.propertyName]);
})
}
}
if (line.length === header.length) {
Expand Down
2 changes: 1 addition & 1 deletion generators/entity-client/files-angular.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ async function writeAngularFiles() {
sections: angularFiles,
rootTemplatesPath: 'angular',
transform: !application.enableTranslation ? [replaceAngularTranslations] : undefined,
context: { ...application, ...entity },
context: { ...application, ...entity, entity },
});

if (!entity.embedded) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,12 @@
<%_
const enumImports = this.generateEntityClientEnumImports(fields, clientFramework);
_%>
<%_ if (fieldsContainDate) { _%>
import dayjs from 'dayjs/esm';
<%_ } _%>

<%_ enumImports.forEach( (importedPath, importedType) => { _%>
import { <%- importedType %> } from '<%- importedPath %>';
<%_ }); _%>

import { I<%= entityAngularName %><% if (primaryKey) { %>, New<%= entityAngularName %><% } %> } from './<%= entityFileName %>.model';
import { I<%= entityAngularName %> } from './<%= entityFileName %>.model';

export const sampleWithRequiredData: I<%= entityAngularName %> = <%- this.generateTypescriptTestEntity(fields.filter(field => field.id || field.fieldValidationRequired).map(field => field.reference)) %>;

Expand Down
13 changes: 12 additions & 1 deletion generators/entity-server/files.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,16 @@ const modelFiles = {
},
],
},
{
condition: generator => generator.primaryKey && generator.primaryKey.composite && !generator.primaryKey.derived,
path: SERVER_MAIN_SRC_DIR,
templates: [
{
file: 'package/domain/EntityId.java',
renameTo: generator => `${generator.entityAbsoluteFolder}/domain/${generator.persistClass}Id.java`,
},
],
},
],
modelTestFiles: [
{
Expand Down Expand Up @@ -447,7 +457,8 @@ function writeFiles() {
return this.writeFiles({
sections: serverFiles,
rootTemplatesPath: application.reactive ? ['reactive', ''] : undefined,
context: { ...application, ...entity },
// entity needed for recursive DTO
context: { ...application, ...entity, entity },
});
},

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ int databaseSizeBeforeUpdate = <%= entityInstance %>Repository.findAll()<%= call
// Update the <%= entityInstance %> using partial update
<%= persistClass %> partialUpdated<%= persistClass %> = new <%= persistClass %>();
partialUpdated<%= persistClass %>.set<%= primaryKey.nameCapitalized %>(<%= persistInstance %>.get<%= primaryKey.nameCapitalized %>());
<%_ primaryKey.composite && fields.filter(f => f.id).forEach(f => { _%>
partialUpdated<%= persistClass %>.set<%= f.fieldNameCapitalized %>(<%= persistInstance %>.get<%= f.fieldNameCapitalized %>());
<%_ }) _%>
<%_ relationships.filter(r => r.id).forEach(r => { _%>
partialUpdated<%= persistClass %>.set<%= r.relationshipNameCapitalized %>(<%= persistInstance %>.get<%= r.relationshipNameCapitalized %>());
<%_ }) _%>
<%_ fieldsToUpdate = fields.filter(field => field.includeField) %>
<%_ if (fluentMethods && fieldsToUpdate.length > 0) { _%>
partialUpdated<%= persistClass %><% for (field of fieldsToUpdate) { %>
Expand All @@ -47,7 +53,7 @@ webTestClient
.expectStatus()
.isOk();
<%_ } else { _%>
rest<%= entityClass %>MockMvc.perform(patch(ENTITY_API_URL_ID, partialUpdated<%= persistClass %>.get<%= primaryKey.nameCapitalized %>())<% if (testsNeedCsrf) { %>.with(csrf())<% }%>
rest<%= entityClass %>MockMvc.perform(patch(ENTITY_API_URL_ID, <%- persistInstanceUrlId.replaceAll(persistInstance, 'partialUpdated' + persistClass) %>)<% if (testsNeedCsrf) { %>.with(csrf())<% }%>
.contentType("application/merge-patch+json")
.content(TestUtil.convertObjectToJsonBytes(<%= 'partialUpdated' + persistClass %>)))
.andExpect(status().isOk());
Expand Down
4 changes: 2 additions & 2 deletions generators/entity-server/templates/partials/save_template.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -43,15 +43,15 @@ if (isUsingMapsId) {
<%_ resultEntity = persistInstance; _%>
<%= persistClass %> <%= persistInstance %> = <%= dtoToEntity %>(<%= instanceName %>);
<%_ if (isUsingMapsId) { _%>
<%= mapsIdAssoc.otherEntity.primaryKey.type %> <%= otherEntityName %>Id = <%= instanceName %>.get<%= mapsIdAssoc.relationshipNameCapitalized %>().get<%= primaryKey.nameCapitalized %>();
<%= mapsIdAssoc.otherEntity.primaryKey.type %> <%= otherEntityName %>Id = <% if (primaryKey.composite) { %><%= mapper %>.toEntity(<%= instanceName %>)<% } else { %><%= instanceName %><% } %><% if (primaryKey.fields.length === 1) { %>.get<%= mapsIdAssoc.relationshipNameCapitalized %>()<% } %>.get<%= primaryKey.nameCapitalized %>();
<%= mapsIdRepoInstance %>.findById(<%= otherEntityName %>Id).ifPresent(<%= persistInstance %>::<%_ if (!fluentMethods) { _%>set<%= mapsIdAssoc.relationshipNameCapitalized %> <%_ } else { _%><%= mapsIdAssoc.relationshipName %><%_ } _%>);
<%_ } _%>
<%= persistInstance %> = <%= entityInstance %>Repository.save(<%= persistInstance %>);
<%= returnPrefix %> <%= entityToDto %>(<%= persistInstance %>);
<%_ } else {
resultEntity = 'result'; _%>
<%_ if (isUsingMapsId) { _%>
<%= mapsIdAssoc.otherEntity.primaryKey.type %> <%= otherEntityName %>Id = <%= instanceName %>.get<%= mapsIdAssoc.relationshipNameCapitalized %>().get<%= primaryKey.nameCapitalized %>();
<%= mapsIdAssoc.otherEntity.primaryKey.type %> <%= otherEntityName %>Id = <%= instanceName %><% if (primaryKey.fields.length === 1) { %>.get<%= mapsIdAssoc.relationshipNameCapitalized %>()<% } %>.get<%= primaryKey.nameCapitalized %>();
<%= mapsIdRepoInstance %>.findById(<%= otherEntityName %>Id).ifPresent(<%= instanceName %>::<%_ if (!fluentMethods) { _%>set<%= mapsIdAssoc.relationshipNameCapitalized %> <%_ } else { _%><%= otherEntityName %><%_ } _%>);
<%_ } _%>
<%= returnPrefix %> <%= entityInstance %>Repository.save(<%= persistInstance %>);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ if (isUsingMapsId) {
<%= persistInstance %>.setIsPersisted();
<%_ } _%>
<%_ if (isUsingMapsId) { _%>
<%= mapsIdAssoc.otherEntity.primaryKey.type %> <%= otherEntityName %>Id = <%= instanceName %>.get<%= mapsIdAssoc.relationshipNameCapitalized %>().get<%= primaryKey.nameCapitalized %>();
<%= mapsIdAssoc.otherEntity.primaryKey.type %> <%= otherEntityName %>Id = <% if (primaryKey.composite) { %><%= mapper %>.toEntity(<%= instanceName %>)<% } else { %><%= instanceName %><% } %><% if (primaryKey.fields.length === 1) { %>.get<%= mapsIdAssoc.relationshipNameCapitalized %>()<% } %>.get<%= primaryKey.nameCapitalized %>();
<%= mapsIdRepoInstance %>.findById(<%= otherEntityName %>Id).ifPresent(<%= persistInstance %>::<%_ if (!fluentMethods) { _%>set<%= mapsIdAssoc.relationshipNameCapitalized %> <%_ } else { _%><%= mapsIdAssoc.relationshipName %><%_ } _%>);
<%_ } _%>
<%_ if (updatableEntity) { _%>
Expand All @@ -53,7 +53,7 @@ if (isUsingMapsId) {
<%_ } else {
resultEntity = 'result'; _%>
<%_ if (isUsingMapsId) { _%>
<%= mapsIdAssoc.otherEntity.primaryKey.type %> <%= otherEntityName %>Id = <%= instanceName %>.get<%= mapsIdAssoc.relationshipNameCapitalized %>().get<%= primaryKey.nameCapitalized %>();
<%= mapsIdAssoc.otherEntity.primaryKey.type %> <%= otherEntityName %>Id = <%= instanceName %><% if (primaryKey.fields.length === 1) { %>.get<%= mapsIdAssoc.relationshipNameCapitalized %>()<% } %>.get<%= primaryKey.nameCapitalized %>();
<%= mapsIdRepoInstance %>.findById(<%= otherEntityName %>Id).ifPresent(<%= instanceName %>::<%_ if (!fluentMethods) { _%>set<%= mapsIdAssoc.relationshipNameCapitalized %> <%_ } else { _%><%= otherEntityName %><%_ } _%>);
<%_ } _%>
<%_ if (isPersisted) { _%>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ if (field.fieldValidate) {
const MAX_VALUE = 2147483647;
const isBlob = field.fieldTypeBytes;
if (field.fieldValidationRequired && !isBlob) {
if (field.fieldValidationRequired && !field.autoGenerate && !isBlob) {
// reactive tests need a default validation message because lookup is blocking
validators.push('@NotNull' + (reactive ? '(message = "must not be null")' : ''));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@
if (viaService) {
beans.push({class: `${entityClass}Service`, instance: `${entityInstance}Service`});
beans.push({class: `${entityClass}Repository`, instance: `${entityInstance}Repository`});
if (dtoMapstruct && primaryKey.composite) {
beans.push({class: `${entityClass}Mapper`, instance: `${entityInstance}Mapper`});
}
if (queryService) {
beans.push({class: `${entityClass}QueryService`, instance: `${entityInstance}QueryService`});
}
Expand All @@ -32,7 +35,7 @@
}
} else {
beans.push({class: `${entityClass}Repository`, instance: `${entityInstance}Repository`});
if (dtoMapstruct) {
if (dtoMapstruct || primaryKey.composite) {
beans.push({class: `${entityClass}Mapper`, instance: `${entityInstance}Mapper`});
}
if (searchEngineElasticsearch) {
Expand Down
Loading

0 comments on commit ffda950

Please sign in to comment.