diff --git a/src/persistence/SubjectChangedColumnsComputer.ts b/src/persistence/SubjectChangedColumnsComputer.ts index 0680aa6f2f..f09f7237fb 100644 --- a/src/persistence/SubjectChangedColumnsComputer.ts +++ b/src/persistence/SubjectChangedColumnsComputer.ts @@ -61,9 +61,11 @@ export class SubjectChangedColumnsComputer { // if there is no database entity then all columns are treated as new, e.g. changed if (subject.databaseEntity) { + // skip transform database value for json / jsonb for comparison later on + const shouldTransformDatabaseEntity = column.type !== "json" && column.type !== "jsonb"; // get database value of the column - let databaseValue = column.getEntityValue(subject.databaseEntity, true); + let databaseValue = column.getEntityValue(subject.databaseEntity, shouldTransformDatabaseEntity); // filter out "relational columns" only in the case if there is a relation object in entity if (column.relationMetadata) { diff --git a/test/other-issues/transformed-json-column-update-compute/entity/json-entity.ts b/test/other-issues/transformed-json-column-update-compute/entity/json-entity.ts new file mode 100644 index 0000000000..7e473621b6 --- /dev/null +++ b/test/other-issues/transformed-json-column-update-compute/entity/json-entity.ts @@ -0,0 +1,17 @@ +import { Entity } from "../../../../src/decorator/entity/Entity"; +import { Column } from "../../../../src/decorator/columns/Column"; +import { PrimaryGeneratedColumn } from "../../../../src/decorator/columns/PrimaryGeneratedColumn"; +import { VersionColumn } from "../../../../src"; +import { testTransformer } from "../test-transformer"; + +@Entity() +export class DummyJSONEntity { + @PrimaryGeneratedColumn() + id: number; + + @VersionColumn() + version: number; + + @Column({ type: "json", transformer: testTransformer }) + value: Record; +} diff --git a/test/other-issues/transformed-json-column-update-compute/entity/jsonb-entity.ts b/test/other-issues/transformed-json-column-update-compute/entity/jsonb-entity.ts new file mode 100644 index 0000000000..3bf1b3507d --- /dev/null +++ b/test/other-issues/transformed-json-column-update-compute/entity/jsonb-entity.ts @@ -0,0 +1,17 @@ +import { Entity } from "../../../../src/decorator/entity/Entity"; +import { Column } from "../../../../src/decorator/columns/Column"; +import { PrimaryGeneratedColumn } from "../../../../src/decorator/columns/PrimaryGeneratedColumn"; +import { VersionColumn } from "../../../../src"; +import { testTransformer } from "../test-transformer"; + +@Entity() +export class DummyJSONBEntity { + @PrimaryGeneratedColumn() + id: number; + + @VersionColumn() + version: number; + + @Column({ type: "jsonb", transformer: testTransformer }) + value: Record; +} diff --git a/test/other-issues/transformed-json-column-update-compute/test-transformer.ts b/test/other-issues/transformed-json-column-update-compute/test-transformer.ts new file mode 100644 index 0000000000..4492ec35f0 --- /dev/null +++ b/test/other-issues/transformed-json-column-update-compute/test-transformer.ts @@ -0,0 +1,14 @@ +export const testTransformer = { + to(data: any) { + if ("secretProperty" in data) { + data.secretProperty = `secret-${data.secretProperty}`; + } + return data; + }, + from(data: any) { + if ("secretProperty" in data) { + data.secretProperty = data.secretProperty.split("-")[1]; + } + return data; + } +}; diff --git a/test/other-issues/transformed-json-column-update-compute/transformed-json-column-update-compute.ts b/test/other-issues/transformed-json-column-update-compute/transformed-json-column-update-compute.ts new file mode 100644 index 0000000000..c5ef4d1aaf --- /dev/null +++ b/test/other-issues/transformed-json-column-update-compute/transformed-json-column-update-compute.ts @@ -0,0 +1,53 @@ +import "reflect-metadata"; +import { createTestingConnections, closeTestingConnections, reloadTestingDatabases } from "../../utils/test-utils"; +import { Connection } from "../../../src/connection/Connection"; +import { expect } from "chai"; +import { DummyJSONEntity } from "./entity/json-entity"; +import { DummyJSONBEntity } from "./entity/jsonb-entity"; + +describe("other issues > correctly compute change for transformed json / jsonb columns", () => { + + let connections: Connection[]; + before(async () => connections = await createTestingConnections({ + entities: [__dirname + "/entity/*{.js,.ts}"], + schemaCreate: true, + dropSchema: true, + enabledDrivers: ["postgres"] + })); + beforeEach(() => reloadTestingDatabases(connections)); + after(() => closeTestingConnections(connections)); + + it("should not update entity if transformed JSON column did not change", () => Promise.all(connections.map(async connection => { + const repository = connection.getRepository(DummyJSONEntity); + + const dummy = repository.create({ + value: { + secretProperty: "hello" + }, + }); + + await repository.save(dummy); + + await repository.save(dummy); + + const dummyEntity = await repository.findOneOrFail(dummy.id); + expect(dummyEntity.version).to.equal(1); + }))); + + it("should not update entity if transformed JSONB column did not change", () => Promise.all(connections.map(async connection => { + const repository = connection.getRepository(DummyJSONBEntity); + + const dummy = repository.create({ + value: { + secretProperty: "hello" + }, + }); + + await repository.save(dummy); + + await repository.save(dummy); + + const dummyEntity = await repository.findOneOrFail(dummy.id); + expect(dummyEntity.version).to.equal(1); + }))); +});