几个小BUG,以及对现在属性为undefined会报RowUnexsited错误的处理

This commit is contained in:
Xu Chang 2022-11-01 19:50:50 +08:00
parent f412afd2a9
commit 90c7db02b7
12 changed files with 254 additions and 118 deletions

View File

@ -1,14 +1,18 @@
import { EntityDict, OperateOption, OperationResult, OpRecord, SelectOption } from 'oak-domain/lib/types/Entity';
import { DeduceCreateSingleOperation, DeduceRemoveOperation, DeduceUpdateOperation, EntityDict, OperationResult, OpRecord, SelectOption } from 'oak-domain/lib/types/Entity';
import { StorageSchema } from "oak-domain/lib/types/Storage";
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
import { Checker, CheckerType, Context } from 'oak-domain/lib/types';
import { TreeStore } from 'oak-memory-tree-store';
import { TreeStore, TreeStoreOperateOption } from 'oak-memory-tree-store';
interface CachStoreOperation extends TreeStoreOperateOption {
inSync?: boolean;
}
export declare class CacheStore<ED extends EntityDict & BaseEntityDict, Cxt extends Context<ED>> extends TreeStore<ED, Cxt> {
private executor;
private getFullDataFn?;
private resetInitialDataFn?;
constructor(storageSchema: StorageSchema<ED>, contextBuilder: () => (store: CacheStore<ED, Cxt>) => Cxt, getFullDataFn?: () => any, resetInitialDataFn?: () => void);
operate<T extends keyof ED, OP extends OperateOption>(entity: T, operation: ED[T]['Operation'], context: Cxt, option: OP): Promise<OperationResult<ED>>;
protected updateAbjointRow<T extends keyof ED>(entity: T, operation: DeduceCreateSingleOperation<ED[T]['Schema']> | DeduceUpdateOperation<ED[T]['Schema']> | DeduceRemoveOperation<ED[T]['Schema']>, context: Cxt, option?: CachStoreOperation): Promise<number>;
operate<T extends keyof ED, OP extends TreeStoreOperateOption>(entity: T, operation: ED[T]['Operation'], context: Cxt, option: OP): Promise<OperationResult<ED>>;
sync(opRecords: Array<OpRecord<ED>>, context: Cxt): Promise<void>;
check<T extends keyof ED>(entity: T, operation: ED[T]['Operation'], context: Cxt, checkerTypes?: CheckerType[]): Promise<void>;
select<T extends keyof ED, S extends ED[T]['Selection'], OP extends SelectOption>(entity: T, selection: S, context: Cxt, option: OP): Promise<import("oak-domain/lib/types").SelectionResult<ED[T]["Schema"], S["data"]>>;
@ -24,3 +28,4 @@ export declare class CacheStore<ED extends EntityDict & BaseEntityDict, Cxt exte
*/
resetInitialData(): void;
}
export {};

View File

@ -5,6 +5,7 @@ var tslib_1 = require("tslib");
var TriggerExecutor_1 = require("oak-domain/lib/store/TriggerExecutor");
var oak_memory_tree_store_1 = require("oak-memory-tree-store");
var assert_1 = tslib_1.__importDefault(require("assert"));
;
var CacheStore = /** @class */ (function (_super) {
tslib_1.__extends(CacheStore, _super);
function CacheStore(storageSchema, contextBuilder, getFullDataFn, resetInitialDataFn) {
@ -16,6 +17,28 @@ var CacheStore = /** @class */ (function (_super) {
_this.resetInitialDataFn = resetInitialDataFn;
return _this;
}
CacheStore.prototype.updateAbjointRow = function (entity, operation, context, option) {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var action, data, attributes, key;
var _a;
return tslib_1.__generator(this, function (_b) {
if (!(option === null || option === void 0 ? void 0 : option.inSync)) {
action = operation.action, data = operation.data;
if (action === 'create') {
attributes = this.getSchema()[entity].attributes;
for (key in attributes) {
if (data[key] === undefined) {
Object.assign(data, (_a = {},
_a[key] = null,
_a));
}
}
}
}
return [2 /*return*/, _super.prototype.updateAbjointRow.call(this, entity, operation, context, option)];
});
});
};
CacheStore.prototype.operate = function (entity, operation, context, option) {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var autoCommit, result, err_1;
@ -77,7 +100,9 @@ var CacheStore = /** @class */ (function (_super) {
_a.label = 2;
case 2:
_a.trys.push([2, 4, , 7]);
return [4 /*yield*/, _super.prototype.sync.call(this, opRecords, context)];
return [4 /*yield*/, _super.prototype.sync.call(this, opRecords, context, {
inSync: true,
})];
case 3:
result = _a.sent();
return [3 /*break*/, 7];

View File

@ -1,17 +1,18 @@
import { EntityDict, SelectOption, Context, RowStore, DeduceCreateOperation, DeduceRemoveOperation, DeduceUpdateOperation, OperateOption, SelectionResult, SelectRowShape } from "oak-domain/lib/types";
import { TreeStore } from 'oak-memory-tree-store';
import { EntityDict, Context, RowStore, DeduceCreateOperation, DeduceRemoveOperation, DeduceUpdateOperation, SelectionResult, SelectRowShape } from "oak-domain/lib/types";
import { TreeStore, TreeStoreOperateOption, TreeStoreSelectOption } from 'oak-memory-tree-store';
import { StorageSchema, Trigger, Checker } from "oak-domain/lib/types";
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
interface DebugStoreOperateOption extends OperateOption {
interface DebugStoreOperateOption extends TreeStoreOperateOption {
noLock?: true;
}
interface DebugStoreSelectOption extends SelectOption {
interface DebugStoreSelectOption extends TreeStoreSelectOption {
noLock?: true;
}
export declare class DebugStore<ED extends EntityDict & BaseEntityDict, Cxt extends Context<ED>> extends TreeStore<ED, Cxt> {
private executor;
private rwLock;
constructor(storageSchema: StorageSchema<ED>, contextBuilder: (cxtString?: string) => (store: RowStore<ED, Cxt>) => Promise<Cxt>);
protected updateAbjointRow<T extends keyof ED, OP extends DebugStoreOperateOption>(entity: T, operation: ED[T]['CreateSingle'] | ED[T]['Update'] | ED[T]['Remove'], context: Cxt, option?: OP): Promise<number>;
protected cascadeUpdate<T extends keyof ED, OP extends DebugStoreOperateOption>(entity: T, operation: DeduceCreateOperation<ED[T]["Schema"]> | DeduceUpdateOperation<ED[T]["Schema"]> | DeduceRemoveOperation<ED[T]["Schema"]>, context: Cxt, option: OP): Promise<import("oak-domain/lib/types").OperationResult<ED>>;
protected cascadeSelect<T extends keyof ED, S extends ED[T]["Selection"], OP extends DebugStoreSelectOption>(entity: T, selection: S, context: Cxt, option: OP): Promise<SelectRowShape<ED[T]['Schema'], S['data']>[]>;
operate<T extends keyof ED, OP extends DebugStoreOperateOption>(entity: T, operation: ED[T]['Operation'], context: Cxt, option: OP): Promise<import("oak-domain/lib/types").OperationResult<ED>>;

View File

@ -15,6 +15,26 @@ var DebugStore = /** @class */ (function (_super) {
_this.rwLock = new concurrent_1.RWLock();
return _this;
}
DebugStore.prototype.updateAbjointRow = function (entity, operation, context, option) {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var action, data, attributes, key;
var _a;
return tslib_1.__generator(this, function (_b) {
action = operation.action, data = operation.data;
if (action === 'create') {
attributes = this.getSchema()[entity].attributes;
for (key in attributes) {
if (data[key] === undefined) {
Object.assign(data, (_a = {},
_a[key] = null,
_a));
}
}
}
return [2 /*return*/, _super.prototype.updateAbjointRow.call(this, entity, operation, context, option)];
});
});
};
DebugStore.prototype.cascadeUpdate = function (entity, operation, context, option) {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var result;

View File

@ -1,4 +1,4 @@
import { EntityDict, OperateOption, SelectOption, OpRecord, Context, AspectWrapper, SelectionResult, CheckerType } from 'oak-domain/lib/types';
import { EntityDict, OperateOption, SelectOption, OpRecord, Context, AspectWrapper, CheckerType } from 'oak-domain/lib/types';
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
import { CommonAspectDict } from 'oak-common-aspect';
import { Feature } from '../types/Feature';
@ -36,7 +36,8 @@ export declare class Cache<ED extends EntityDict & BaseEntityDict, Cxt extends C
tryRedoOperationsThenSelect<T extends keyof ED, S extends ED[T]['Selection']>(entity: T, selection: S, opers: Array<{
entity: keyof ED;
operation: ED[keyof ED]['Operation'];
}>): Promise<SelectionResult<ED[T]["Schema"], S["data"]>>;
}>): Promise<import("oak-domain/lib/types").SelectRowShape<ED[T]["Schema"], S["data"]>[]>;
private getInner;
get<T extends keyof ED, S extends ED[T]['Selection']>(entity: T, selection: S, params?: SelectOption): Promise<import("oak-domain/lib/types").SelectRowShape<ED[T]["Schema"], S["data"]>[]>;
judgeRelation(entity: keyof ED, attr: string): string | 0 | 1 | 2 | string[];
bindOnSync(callback: (opRecords: OpRecord<ED>[]) => Promise<void>): void;

View File

@ -225,7 +225,7 @@ var Cache = /** @class */ (function (_super) {
*/
Cache.prototype.tryRedoOperationsThenSelect = function (entity, selection, opers) {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var result, context, opers_1, opers_1_1, oper, e_2_1, err_3, missedRows;
var context, opers_1, opers_1_1, oper, e_2_1, result, err_3;
var e_2, _a;
return tslib_1.__generator(this, function (_b) {
switch (_b.label) {
@ -269,50 +269,63 @@ var Cache = /** @class */ (function (_super) {
(0, selection_1.reinforceSelection)(this.cacheStore.getSchema(), entity, selection);
_b.label = 10;
case 10:
if (!true) return [3 /*break*/, 20];
_b.label = 11;
_b.trys.push([10, 13, , 15]);
return [4 /*yield*/, this.getInner(entity, selection, context)];
case 11:
_b.trys.push([11, 14, , 19]);
result = _b.sent();
return [4 /*yield*/, context.rollback()];
case 12:
_b.sent();
return [2 /*return*/, result];
case 13:
err_3 = _b.sent();
return [4 /*yield*/, context.rollback()];
case 14:
_b.sent();
throw err_3;
case 15: return [2 /*return*/];
}
});
});
};
Cache.prototype.getInner = function (entity, selection, context) {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var result, err_4, missedRows;
return tslib_1.__generator(this, function (_a) {
switch (_a.label) {
case 0:
if (!true) return [3 /*break*/, 8];
_a.label = 1;
case 1:
_a.trys.push([1, 3, , 7]);
return [4 /*yield*/, this.cacheStore.select(entity, selection, context, {
dontCollect: true,
})];
case 12:
result = _b.sent();
return [4 /*yield*/, context.rollback()];
case 13:
_b.sent();
case 2:
result = (_a.sent()).result;
return [2 /*return*/, result];
case 14:
err_3 = _b.sent();
if (!(err_3 instanceof Exception_1.OakRowUnexistedException)) return [3 /*break*/, 16];
missedRows = err_3.getRows();
case 3:
err_4 = _a.sent();
if (!(err_4 instanceof Exception_1.OakRowUnexistedException)) return [3 /*break*/, 5];
missedRows = err_4.getRows();
return [4 /*yield*/, this.aspectWrapper.exec('fetchRows', missedRows)];
case 15:
_b.sent();
return [3 /*break*/, 18];
case 16: return [4 /*yield*/, context.rollback()];
case 17:
_b.sent();
throw err_3;
case 18: return [3 /*break*/, 19];
case 19: return [3 /*break*/, 10];
case 20: return [2 /*return*/];
case 4:
_a.sent();
return [3 /*break*/, 6];
case 5: throw err_4;
case 6: return [3 /*break*/, 7];
case 7: return [3 /*break*/, 0];
case 8: return [2 /*return*/];
}
});
});
};
Cache.prototype.get = function (entity, selection, params) {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var context, result;
var context;
return tslib_1.__generator(this, function (_a) {
switch (_a.label) {
case 0:
context = this.contextBuilder();
return [4 /*yield*/, this.cacheStore.select(entity, selection, context, {})];
case 1:
result = (_a.sent()).result;
return [2 /*return*/, result];
}
context = this.contextBuilder();
return [2 /*return*/, this.getInner(entity, selection, context)];
});
});
};

View File

@ -86,7 +86,7 @@ declare class ListNode<ED extends EntityDict & BaseEntityDict, T extends keyof E
entity: T;
})[] | undefined>;
getProjection(): Promise<ED[T]['Selection']['data']>;
constructSelection(withParent?: true): Promise<{
constructSelection(withParent?: true, disableOperation?: boolean): Promise<{
data: ED[T]["Selection"]["data"];
filter: ED[T]["Selection"]["filter"] | undefined;
sorter: DeduceSorterItem<ED[T]["Schema"]>[];
@ -114,7 +114,7 @@ declare class SingleNode<ED extends EntityDict & BaseEntityDict, T extends keyof
};
addChild(path: string, node: SingleNode<ED, keyof ED, Cxt, AD> | ListNode<ED, keyof ED, Cxt, AD>): void;
removeChild(path: string): void;
getFreshValue(): Promise<SelectRowShape<ED[T]['Schema'], ED[T]['Selection']['data']> | undefined>;
getFreshValue(disableOperation?: boolean): Promise<SelectRowShape<ED[T]['Schema'], ED[T]['Selection']['data']> | undefined>;
doBeforeTrigger(): Promise<void>;
doAfterTrigger(): Promise<void>;
addOperation(oper: Omit<ED[T]['Operation'], 'id'>, beforeExecute?: Operation<ED, T>['beforeExecute'], afterExecute?: Operation<ED, T>['afterExecute']): Promise<void>;
@ -124,7 +124,7 @@ declare class SingleNode<ED extends EntityDict & BaseEntityDict, T extends keyof
getProjection(withDecendants?: boolean): Promise<ED[keyof ED]["Selection"]["data"]>;
refresh(): Promise<void>;
clean(): void;
getParentFilter<T2 extends keyof ED>(childNode: Node<ED, keyof ED, Cxt, AD>): Promise<ED[T2]['Selection']['filter'] | undefined>;
getParentFilter<T2 extends keyof ED>(childNode: Node<ED, keyof ED, Cxt, AD>, disableOperation?: boolean): Promise<ED[T2]['Selection']['filter'] | undefined>;
}
declare class VirtualNode {
private dirty;

View File

@ -600,7 +600,7 @@ var ListNode = /** @class */ (function (_super) {
finally { if (e_9) throw e_9.error; }
}
if (!needRefresh) return [3 /*break*/, 3];
return [4 /*yield*/, this.constructSelection(true)];
return [4 /*yield*/, this.constructSelection(true, true)];
case 1:
_a = _c.sent(), filter = _a.filter, sorter = _a.sorter;
return [4 /*yield*/, this.cache.get(this.getEntity(), {
@ -895,7 +895,7 @@ var ListNode = /** @class */ (function (_super) {
}
return [4 /*yield*/, this.cache.tryRedoOperationsThenSelect(this.entity, selection, operations)];
case 4:
result = (_e.sent()).result;
result = _e.sent();
return [2 /*return*/, result];
case 5: return [2 /*return*/, []];
}
@ -1155,7 +1155,7 @@ var ListNode = /** @class */ (function (_super) {
});
});
};
ListNode.prototype.constructSelection = function (withParent) {
ListNode.prototype.constructSelection = function (withParent, disableOperation) {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var _a, filters, sorters, data, validParentFilter, sorterArr, filterArr, filterOfParent, filters2, filter;
var _this = this;
@ -1200,7 +1200,7 @@ var ListNode = /** @class */ (function (_super) {
filterArr = _b.sent();
if (!(withParent && this.parent)) return [3 /*break*/, 5];
if (!(this.parent instanceof SingleNode)) return [3 /*break*/, 5];
return [4 /*yield*/, this.parent.getParentFilter(this)];
return [4 /*yield*/, this.parent.getParentFilter(this, disableOperation)];
case 4:
filterOfParent = _b.sent();
if (filterOfParent) {
@ -1400,7 +1400,7 @@ var SingleNode = /** @class */ (function (_super) {
SingleNode.prototype.removeChild = function (path) {
(0, lodash_1.unset)(this.children, path);
};
SingleNode.prototype.getFreshValue = function () {
SingleNode.prototype.getFreshValue = function (disableOperation) {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var projection, modies, _a, operations, filter, createOper, parent_1, result;
var _this = this;
@ -1419,31 +1419,38 @@ var SingleNode = /** @class */ (function (_super) {
modies = _a;
operations = modies ? (0, modi_1.createOperationsFromModies)(modies) : [];
filter = this.id && { id: this.id };
if (!filter) {
if (!filter && !disableOperation) {
createOper = this.operations.find(function (ele) { return ele.oper.action === 'create'; });
if (createOper) {
(0, assert_1.assert)(createOper.oper.data.id);
filter = {
id: createOper.oper.data.id
};
}
parent_1 = this.parent;
if (parent_1 instanceof ListNode || parent_1 instanceof SingleNode) {
filter = parent_1.getParentFilter(this);
}
}
if (!filter) return [3 /*break*/, 5];
operations.push.apply(operations, tslib_1.__spreadArray([], tslib_1.__read(this.operations.map(function (ele) { return ({
entity: _this.entity,
operation: ele.oper,
}); })), false));
if (!!filter) return [3 /*break*/, 5];
parent_1 = this.parent;
if (!(parent_1 instanceof ListNode || parent_1 instanceof SingleNode)) return [3 /*break*/, 5];
return [4 /*yield*/, parent_1.getParentFilter(this, disableOperation)];
case 4:
filter = _b.sent();
_b.label = 5;
case 5:
if (!filter) return [3 /*break*/, 7];
if (!disableOperation) {
operations.push.apply(operations, tslib_1.__spreadArray([], tslib_1.__read(this.operations.map(function (ele) { return ({
entity: _this.entity,
operation: ele.oper,
}); })), false));
}
return [4 /*yield*/, this.cache.tryRedoOperationsThenSelect(this.entity, {
data: projection,
filter: filter,
}, operations)];
case 4:
result = (_b.sent()).result;
case 6:
result = _b.sent();
return [2 /*return*/, result[0]];
case 5: return [2 /*return*/];
case 7: return [2 /*return*/];
}
});
});
@ -1863,13 +1870,13 @@ var SingleNode = /** @class */ (function (_super) {
this.children[child].clean();
}
};
SingleNode.prototype.getParentFilter = function (childNode) {
SingleNode.prototype.getParentFilter = function (childNode, disableOperation) {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var value, key, sliceIdx, key2, rel;
var _a;
return tslib_1.__generator(this, function (_b) {
switch (_b.label) {
case 0: return [4 /*yield*/, this.getFreshValue()];
case 0: return [4 /*yield*/, this.getFreshValue(disableOperation)];
case 1:
value = (_b.sent());
if (!value) {
@ -1882,28 +1889,34 @@ var SingleNode = /** @class */ (function (_super) {
rel = this.judgeRelation(key2);
if (rel === 2) {
// 基于entity/entityId的多对一
return [2 /*return*/, {
id: value.entityId,
}];
if (value.entityId && value.entity === childNode.getEntity()) {
return [2 /*return*/, {
id: value.entityId,
}];
}
return [2 /*return*/, undefined];
}
else if (typeof rel === 'string') {
return [2 /*return*/, {
id: value["".concat(rel, "Id")],
}];
if (value["".concat(rel, "Id")]) {
return [2 /*return*/, {
id: value["".concat(rel, "Id")],
}];
}
return [2 /*return*/, undefined];
}
else {
(0, assert_1.assert)(rel instanceof Array);
if (rel[1]) {
// 基于普通外键的一对多
return [2 /*return*/, (_a = {},
_a[rel[1]] = this.id,
_a[rel[1]] = value.id,
_a)];
}
else {
// 基于entity/entityId的一对多
return [2 /*return*/, {
entity: this.entity,
entityId: this.id,
entityId: value.id,
}];
}
}

View File

@ -1,11 +1,15 @@
import { EntityDict, OperateOption, OperationResult, OpRecord, SelectOption } from 'oak-domain/lib/types/Entity';
import { DeduceCreateSingleOperation, DeduceRemoveOperation, DeduceUpdateOperation, EntityDict, OperateOption, OperationResult, OpRecord, SelectOption } from 'oak-domain/lib/types/Entity';
import { StorageSchema } from "oak-domain/lib/types/Storage";
import { TriggerExecutor } from 'oak-domain/lib/store/TriggerExecutor';
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
import { Checker, CheckerType, Context, Trigger } from 'oak-domain/lib/types';
import { TreeStore } from 'oak-memory-tree-store';
import { TreeStore, TreeStoreOperateOption } from 'oak-memory-tree-store';
import assert from 'assert';
interface CachStoreOperation extends TreeStoreOperateOption {
inSync?: boolean;
};
export class CacheStore<
ED extends EntityDict & BaseEntityDict,
Cxt extends Context<ED>
@ -28,7 +32,25 @@ export class CacheStore<
this.resetInitialDataFn = resetInitialDataFn;
}
async operate<T extends keyof ED, OP extends OperateOption>(
protected async updateAbjointRow<T extends keyof ED>(entity: T, operation: DeduceCreateSingleOperation<ED[T]['Schema']> | DeduceUpdateOperation<ED[T]['Schema']> | DeduceRemoveOperation<ED[T]['Schema']>, context: Cxt, option?: CachStoreOperation): Promise<number> {
if (!option?.inSync) {
// 如果不是同步需要补齐所有的null属性
const { action, data } = operation;
if (action === 'create') {
const { attributes } = this.getSchema()[entity];
for (const key in attributes) {
if (data[key] === undefined) {
Object.assign(data, {
[key]: null,
});
}
}
}
}
return super.updateAbjointRow(entity, operation, context, option);
}
async operate<T extends keyof ED, OP extends TreeStoreOperateOption>(
entity: T,
operation: ED[T]['Operation'],
context: Cxt,
@ -67,7 +89,9 @@ export class CacheStore<
let result;
try {
result = await super.sync(opRecords, context);
result = await super.sync<CachStoreOperation>(opRecords, context, {
inSync: true,
});
} catch (err) {
if (autoCommit) {
await context.rollback();

View File

@ -1,15 +1,15 @@
import { EntityDict, SelectOption, Context, RowStore, DeduceCreateOperation, DeduceRemoveOperation, DeduceUpdateOperation, OperateOption, SelectionResult, SelectRowShape } from "oak-domain/lib/types";
import { TreeStore } from 'oak-memory-tree-store';
import { TreeStore, TreeStoreOperateOption, TreeStoreSelectOption } from 'oak-memory-tree-store';
import { StorageSchema, Trigger, Checker } from "oak-domain/lib/types";
import { TriggerExecutor } from 'oak-domain/lib/store/TriggerExecutor';
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
import { RWLock } from 'oak-domain/lib/utils/concurrent';
interface DebugStoreOperateOption extends OperateOption {
interface DebugStoreOperateOption extends TreeStoreOperateOption {
noLock?: true;
};
interface DebugStoreSelectOption extends SelectOption {
interface DebugStoreSelectOption extends TreeStoreSelectOption {
noLock?: true;
};
@ -22,6 +22,26 @@ export class DebugStore<ED extends EntityDict & BaseEntityDict, Cxt extends Cont
this.rwLock = new RWLock();
}
protected async updateAbjointRow<T extends keyof ED, OP extends DebugStoreOperateOption>(
entity: T,
operation: ED[T]['CreateSingle'] | ED[T]['Update'] | ED[T]['Remove'],
context: Cxt,
option?: OP) {
// 对于create动作没有值的属性要置NULL
const { action, data } = operation;
if (action === 'create') {
const { attributes } = this.getSchema()[entity];
for (const key in attributes) {
if (data[key] === undefined) {
Object.assign(data, {
[key]: null,
});
}
}
}
return super.updateAbjointRow(entity, operation, context, option);
}
protected async cascadeUpdate<T extends keyof ED, OP extends DebugStoreOperateOption>(entity: T, operation: DeduceCreateOperation<ED[T]["Schema"]> | DeduceUpdateOperation<ED[T]["Schema"]> | DeduceRemoveOperation<ED[T]["Schema"]>, context: Cxt, option: OP) {
if (!option.blockTrigger) {
await this.executor.preOperation(entity, operation, context, option);

View File

@ -171,7 +171,6 @@ export class Cache<
operation: ED[keyof ED]['Operation'];
}>
) {
let result: SelectionResult<ED[T]['Schema'], S['data']>;
const context = this.contextBuilder!();
await context.begin();
for (const oper of opers) {
@ -188,9 +187,21 @@ export class Cache<
);
}
reinforceSelection(this.cacheStore!.getSchema(), entity, selection);
try {
const result = await this.getInner(entity, selection, context);
await context.rollback();
return result;
}
catch (err) {
await context.rollback();
throw err;
}
}
private async getInner<T extends keyof ED, S extends ED[T]['Selection']>(entity: T, selection: S, context: Cxt) {
while (true) {
try {
result = await this.cacheStore!.select(
const { result } = await this.cacheStore!.select(
entity,
selection,
context,
@ -198,15 +209,12 @@ export class Cache<
dontCollect: true,
}
);
await context.rollback();
return result;
} catch (err) {
if (err instanceof OakRowUnexistedException) {
const missedRows = err.getRows();
await this.aspectWrapper.exec('fetchRows', missedRows);
} else {
await context.rollback();
throw err;
}
}
@ -219,13 +227,8 @@ export class Cache<
params?: SelectOption
) {
const context = this.contextBuilder!();
const { result } = await this.cacheStore!.select(
entity,
selection,
context,
{}
);
return result;
return this.getInner(entity, selection, context);
}
judgeRelation(entity: keyof ED, attr: string) {

View File

@ -583,7 +583,8 @@ class ListNode<
userRelation的删除动作就会导致user不会被移出list
*/
if (needRefresh) {
const { filter, sorter } = await this.constructSelection(true);
// 这里因为operation还没被移除掉(execute还没有结束)所以同步的时候不能计算动态的operation产生的id
const { filter, sorter } = await this.constructSelection(true, true);
const result = await this.cache.get(this.getEntity(), {
data: {
id: 1,
@ -815,7 +816,7 @@ class ListNode<
});
}
const { result } = await this.cache.tryRedoOperationsThenSelect(this.entity, selection, operations);
const result = await this.cache.tryRedoOperationsThenSelect(this.entity, selection, operations);
return result;
}
return [];
@ -936,7 +937,7 @@ class ListNode<
return projection;
}
async constructSelection(withParent?: true) {
async constructSelection(withParent?: true, disableOperation?: boolean) {
const { filters, sorters } = this;
const data = await this.getProjection();
let validParentFilter = true;
@ -964,7 +965,7 @@ class ListNode<
if (withParent && this.parent) {
if (this.parent instanceof SingleNode) {
const filterOfParent = await this.parent.getParentFilter<T>(this);
const filterOfParent = await this.parent.getParentFilter<T>(this, disableOperation);
if (filterOfParent) {
filterArr.push(filterOfParent as any);
}
@ -1149,7 +1150,7 @@ class SingleNode<ED extends EntityDict & BaseEntityDict,
unset(this.children, path);
}
async getFreshValue(): Promise<SelectRowShape<ED[T]['Schema'], ED[T]['Selection']['data']> | undefined> {
async getFreshValue(disableOperation?: boolean): Promise<SelectRowShape<ED[T]['Schema'], ED[T]['Selection']['data']> | undefined> {
const projection = await this.getProjection(false);
// 如果本结点是在modi路径上需要将modi更新之后再得到后项
@ -1159,31 +1160,35 @@ class SingleNode<ED extends EntityDict & BaseEntityDict,
operation: ED[keyof ED]['Operation'];
}> : [];
let filter = this.id && { id: this.id } as ED[T]['Selection']['filter'];
if (!filter) {
if (!filter && !disableOperation) {
// 可能是create
const createOper = this.operations.find(
(ele) => ele.oper.action === 'create'
) as { oper: ED[T]['CreateSingle'] };
if (createOper) {
assert(createOper.oper.data.id);
filter = {
id: createOper.oper.data.id
};
}
}
}
if (!filter) {
// 还可能是来自父级的外键
const { parent } = this;
if (parent instanceof ListNode || parent instanceof SingleNode) {
filter = parent.getParentFilter(this);
filter = await parent.getParentFilter(this, disableOperation);
}
}
if (filter) {
operations.push(...this.operations.map(
ele => ({
entity: this.entity,
operation: ele.oper,
})
));
const { result } = await this.cache.tryRedoOperationsThenSelect(this.entity, {
if (!disableOperation) {
operations.push(...this.operations.map(
ele => ({
entity: this.entity,
operation: ele.oper,
})
));
}
const result = await this.cache.tryRedoOperationsThenSelect(this.entity, {
data: projection,
filter,
}, operations);
@ -1439,8 +1444,8 @@ class SingleNode<ED extends EntityDict & BaseEntityDict,
}
}
async getParentFilter<T2 extends keyof ED>(childNode: Node<ED, keyof ED, Cxt, AD>): Promise<ED[T2]['Selection']['filter'] | undefined> {
const value = (await this.getFreshValue())!;
async getParentFilter<T2 extends keyof ED>(childNode: Node<ED, keyof ED, Cxt, AD>, disableOperation?: boolean): Promise<ED[T2]['Selection']['filter'] | undefined> {
const value = (await this.getFreshValue(disableOperation))!;
if (!value) {
return;
}
@ -1452,28 +1457,34 @@ class SingleNode<ED extends EntityDict & BaseEntityDict,
const rel = this.judgeRelation(key2);
if (rel === 2) {
// 基于entity/entityId的多对一
return {
id: value.entityId,
};
if (value.entityId && value.entity === childNode.getEntity()) {
return {
id: value.entityId,
};
}
return undefined;
}
else if (typeof rel === 'string') {
return {
id: value[`${rel}Id`],
};
if (value[`${rel}Id`]) {
return {
id: value[`${rel}Id`],
};
}
return undefined;
}
else {
assert(rel instanceof Array);
if (rel[1]) {
// 基于普通外键的一对多
return {
[rel[1]]: this.id,
[rel[1]]: value.id,
};
}
else {
// 基于entity/entityId的一对多
return {
entity: this.entity,
entityId: this.id,
entityId: value.id,
};
}
}