Pas un autre langage de programmation. Partie 3: Physique



La troisième et dernière partie d'une série d'articles sur lsFusion (liens vers les première et deuxième parties)

Il se concentrera sur le modèle physique: tout ce qui n'est pas lié aux fonctionnalités du système, mais est associé à son développement et à l'optimisation des performances, lorsqu'il y a trop de données.

Cet article, comme les précédents, n'est pas très approprié pour une lecture divertissante, mais, contrairement aux autres, il y aura plus de détails techniques et de sujets «chauds» (tels que la dactylographie ou la métaprogrammation), plus cet article donnera une partie des réponses à la question, comment est-ce que tout cela fonctionne à l'intérieur.

Dans cet article, nous nous passerons d'une image (il n'y a pas de pile comme celle-ci), mais nous ferons une table des matières, comme demandé dans les articles précédents:


Identification de l'article


Si le projet se compose de plusieurs petits fichiers, les problèmes de dénomination des éléments ne se posent généralement pas. Tous les noms sont en vue, et assez faciles pour s'assurer qu'ils ne se chevauchent pas. Si le projet, au contraire, se compose de nombreux modules développés par un grand nombre de personnes différentes, et des abstractions dans ces modules d'un domaine, les conflits de noms deviennent beaucoup plus probables. LsFusion dispose de deux mécanismes pour résoudre ces problèmes:

  • Espaces de noms - séparation d'un nom en entier et court, et la possibilité d'utiliser uniquement un nom court lors de l'accès à un élément
  • Typage explicite (pour être plus précis, surcharge de fonctions) - la possibilité de nommer les propriétés (et les actions) de la même manière, puis, lors de leur accès, selon les classes d'arguments, détermine automatiquement la propriété à laquelle l'appel est destiné

Espaces de noms


Tout projet complexe se compose généralement d'un grand nombre d'éléments qui doivent être nommés. Et, si les domaines de domaine se croisent, il est très souvent nécessaire d'utiliser le même nom dans différents contextes. Par exemple, nous avons le nom d'une classe ou d'un formulaire Facture (facture), et nous voulons utiliser ce nom dans différents blocs fonctionnels, par exemple: Achat (Achat), Vente (Vente), Retour d'achat (PurchaseReturn), Retour de vente (SaleReturn). Il est clair que les classes / formulaires peuvent être appelés PurchaseInvoice, SaleInvoice, etc. Mais, premièrement, ces noms en eux-mêmes seront trop volumineux. Et deuxièmement, dans un bloc fonctionnel, les appels vont, en règle générale, aux éléments du même bloc fonctionnel, ce qui signifie que lors du développement, par exemple, du bloc fonction Achat à partir d'une répétition constante du mot Achat, il se répercutera simplement dans vos yeux. Pour éviter que cela ne se produise, la plate-forme a un tel concept comme un espace de noms. Cela fonctionne comme suit:

  • chaque élément de la plateforme est créé dans un espace de noms
  • si dans le processus de création d'un élément, d'autres éléments sont référencés, les éléments créés dans le même espace de noms ont priorité
MODULE PurchaseInvoice;
NAMESPACE Purchase;
CLASS Invoice ' ()' ;
MODULE SaleInvoice;
NAMESPACE Sale;
CLASS Invoice ' ()' ;
MODULE PurchaseShipment;
REQUIRE PurchaseInvoice, SaleInvoice;
NAMESPACE Purchase;
// Invoice Purchase.Invoice, Sale.invoice
// namespace Purchase namespace Purchase
shipment(Invoice invoice) = AGGR ShipmentInvoce WHERE createShipment(invoice);
Les espaces de noms dans la version linguistique actuelle sont définis pour le module entier immédiatement dans l'en-tête du module. Par défaut, si aucun espace de noms n'est spécifié, il est créé implicitement avec un nom égal au nom du module. Si vous devez accéder à un élément à partir d'un espace de noms non prioritaire, vous pouvez le faire en spécifiant le nom complet de l'élément (par exemple, Sale.Invoice).

Saisie explicite


Les espaces de noms sont importants, mais ce n'est pas le seul moyen de rendre le code plus court et plus lisible. En plus d'eux, lors de la recherche de propriétés (et d'actions), il est également possible de prendre en compte les classes d'arguments qui leur sont passées en entrée. Ainsi, par exemple:
sum = DATA NUMERIC [ 10 , 2 ] (OrderDetail);
sum = GROUP SUM sum(OrderDetail od) BY order(od);
// , Order
// OrderDetail
CONSTRAINT sum(Order o) < 0 MESSAGE ' ' ;
Ici, bien sûr, la question peut se poser: que se passera-t-il si l'espace de noms de la propriété souhaitée n'est pas une priorité, mais il est mieux adapté aux classes? En fait, l'algorithme de recherche général est assez compliqué (sa description complète est ici ) et il y a beaucoup de cas "ambigus", donc en cas d'incertitude, il est recommandé de spécifier explicitement les espaces de noms / classes de la propriété souhaitée, ou de vérifier à nouveau l'IDE (en utilisant Aller à la déclaration - CTRL + B) que la propriété trouvée est exactement ce que l'on voulait dire.

En outre, il convient de noter que la saisie explicite dans lsFusion n'est généralement pas nécessaire. Les classes de paramètres peuvent être omises et si la plateforme dispose de suffisamment d'informations pour trouver la propriété souhaitée, elle le fera. D'un autre côté, dans les projets vraiment complexes, il est toujours recommandé de définir explicitement les classes de paramètres, non seulement du point de vue de la brièveté du code, mais aussi du point de vue de diverses fonctionnalités supplémentaires, telles que: diagnostic précoce des erreurs, auto-complétion intelligente de l'IDE, etc. Nous avions une vaste expérience de travail à la fois avec la frappe implicite (les 5 premières années) et avec la saisie explicite (temps restant), et je dois dire que les temps de la frappe implicite sont maintenant mémorisés avec un frisson (bien que cela puisse simplement «nous ne savions pas comment le cuisiner»).

Modularité


La modularité est l'une des propriétés les plus importantes du système, permettant d'assurer son extensibilité, la réutilisation du code, ainsi qu'une interaction efficace de l'équipe de développement.

LsFusion offre une modularité avec les deux mécanismes suivants:

  • Extensions - la possibilité d'étendre (changer) les éléments du système après leur création.
  • Modules - la possibilité de regrouper certaines fonctionnalités pour une réutilisation ultérieure.

Extensions


lsFusion prend en charge la possibilité d'étendre les classes et les formes, ainsi que les propriétés et les actions, via le mécanisme de polymorphisme décrit dans le premier article.
sum = DATA NUMERIC [ 10 , 2 ] (OrderDetail);
sum = GROUP SUM sum(OrderDetail od) BY order(od);
// , Order
// OrderDetail
CONSTRAINT sum(Order o) < 0 MESSAGE ' ' ;
CLASS ABSTRACT Shape;
CLASS Box : Shape;

CLASS Quadrilateral;
EXTEND CLASS Box : Quadrilateral; //

CLASS ShapeType {
point '' ,
segment ''
}

EXTEND CLASS ShapeType { //
circle ''
}

CLASS ItemGroup;
name = DATA ISTRING [ 100 ] (ItemGroup);

itemGroup = DATA ItemGroup (Item);

EXTEND FORM items
PROPERTIES (i) NEWSESSION DELETE //

OBJECTS g = ItemGroup BEFORE i //
PROPERTIES (g) READONLY name
FILTERS itemGroup(i) == g // , ,
;
De plus, nous notons que presque toutes les autres conceptions de plate-forme (par exemple, navigateur, conception de formulaire) sont extensibles par définition, il n'y a donc pas de logique d'extension distincte pour elles.

Modules


Un module est une partie fonctionnellement complète d'un projet. Dans la version actuelle de lsFusion, un module est un fichier séparé composé de l'en-tête et du corps d'un module. Le titre du module, quant à lui, se compose de: le nom du module, ainsi que, si nécessaire, une liste des modules utilisés et le nom de l'espace de noms de ce module. Le corps du module est constitué de déclarations et / ou d'extensions d'éléments système: propriétés, actions, restrictions, formulaires, métacodes, etc.

En règle générale, les modules utilisent des éléments d'autres modules pour déclarer leurs propres éléments / développer les éléments existants. Par conséquent, si le module B utilise des éléments du module A, il est nécessaire d'indiquer dans le module B qu'il dépend de A.

En fonction de leurs dépendances, tous les modules du projet sont organisés dans un certain ordre dans lequel ils sont initialisés (cet ordre joue un rôle important lors de l'utilisation du mécanisme d'extension susmentionné). Il est garanti que si le module B dépend du module A, l'initialisation du module A se produira avant l'initialisation du module B. Les dépendances cycliques entre les modules du projet ne sont pas autorisées.

Les dépendances entre les modules sont transitoires. Autrement dit, si le module C dépend du module B et que le module B dépend du module A, alors on considère que le module C dépend également du module A.

Tout module dépend toujours automatiquement du module système Système, qu'il soit indiqué explicitement ou non.
MODULE EmployeeExample; //

REQUIRE Authentication, Utils; // , Employee
NAMESPACE Employee; //

CLASS Employee '' ; //
CLASS Position '' ; //

employeePosition(employee) = DATA Position (Employee); //

Métaprogrammation


La métaprogrammation est un type de programmation associé à l'écriture de code de programme, qui génère en conséquence un autre code de programme. LsFusion utilise ce que l'on appelle des métacodes pour la métaprogrammation.

Le métacode comprend:

  • nom du métacode
  • paramètres de métacode
  • corps d'un métacode - un bloc de code composé de déclarations et / ou d'extensions d'éléments système (propriétés, actions, événements, autres métacodes, etc.)

En conséquence, avant de commencer le traitement principal du code, la plate-forme le prépare - remplace toutes les utilisations des métacodes par les corps de ces métacodes. Dans ce cas, tous les paramètres de métacode utilisés dans les identificateurs / littéraux de chaîne sont remplacés par les arguments passés à ce métacode:

Annonce:
META addActions(formName)
EXTEND FORM formName
PROPERTIES () showMessage, closeForm
;
END
Utilisation:
@addActions (documentForm);
@addActions (orderForm);
Code résultant:
EXTEND FORM documentForm
PROPERTIES () showMessage, closeForm
;
EXTEND FORM orderForm
PROPERTIES () showMessage, closeForm
;
En plus de simplement remplacer les paramètres de métacode, la plateforme vous permet également de combiner ces paramètres avec des identifiants / littéraux de chaîne existants (ou entre eux), par exemple:

Annonce:
META objectProperties(object, caption)
object ## Name ' ' ## caption = DATA BPSTRING [ 100 ](object);
object ## Type ' ' ## caption = DATA Type (object);
object ## Value ' ' ## caption = DATA INTEGER (object);
END
Utilisation:
@objectProperties (document, '' );
Code résultant:
DocumentName ' ' = DATA BPSTRING [ 100 ](Document);
DocumentType ' ' = DATA Type (Document);
DocumentValue ' ' = DATA INTEGER (Document);
Les métacodes sont très similaires aux macros en C, mais, contrairement à ce dernier, ils ne fonctionnent pas au niveau du texte (ils ne peuvent pas, par exemple, passer des mots-clés dans le paramètre), mais uniquement au niveau des identifiants / littéraux de chaîne (cette restriction, en particulier, permet analyse du corps du métacode dans l'EDI).

Dans lsFusion, les métacodes résolvent des problèmes similaires aux génériques en Java (passant des classes comme paramètres) et lambda dans les FP (passant des fonctions comme paramètres), cependant, ils ne le font pas très bien. Mais, d'autre part, ils le font dans un cas beaucoup plus général (c'est-à-dire, par exemple, avec la possibilité de combiner des identifiants, à utiliser dans toutes les constructions syntaxiques - formes, conceptions, navigateur, etc.)

Notez que le "déploiement" des métacodes est pris en charge non seulement dans la plate-forme elle-même, mais aussi dans l'IDE. Ainsi, dans l'IDE, il existe un mode spécial Activer la méta, qui génère le code résultant directement dans les sources et permet ainsi à ce code généré de participer à la recherche d'utilisations, à l'achèvement automatique, etc. Dans ce cas, si le corps du métacode change, l'EDI met automatiquement à jour toutes les utilisations de ce métacode.



En outre, les métacodes peuvent être utilisés non seulement pour la génération automatique, mais également pour la génération manuelle de code (en tant que modèles). Pour ce faire, il suffit d'écrire @@ au lieu d'un @ et immédiatement après la saisie complète de la chaîne d'utilisation du métacode (jusqu'au point-virgule), l'EDI remplacera cette utilisation du métacode par le code généré par ce métacode:



Intégration


L'intégration comprend tout ce qui concerne l'interaction du système lsFusion avec d'autres systèmes. Du point de vue de la direction de cette interaction, l'intégration peut être divisée en:

  • Accès au système lsFusion à partir d'un autre système.
  • Accès du système lsFusion à un autre système.

Du point de vue du modèle physique, l'intégration peut être divisée en:

  • Interaction avec des systèmes fonctionnant dans le «même environnement» que le système lsFusion (c'est-à-dire dans la machine virtuelle Java (JVM) du serveur lsFusion et / ou utilisant le même serveur SQL que le système lsFusion).
  • Interaction avec des systèmes distants via des protocoles réseau.

En conséquence, les premiers systèmes seront appelés internes, les seconds - externes.

Ainsi, il existe quatre différents types d'intégration dans la plateforme:

  • Appel à un système externe
  • Appel d'un système externe
  • Appel au système interne
  • Appel du système interne

Appel à un système externe


Dans la plupart des cas, l'accès aux systèmes externes dans lsFusion est implémenté à l'aide de l'opérateur EXTERNAL spécial. Cet opérateur exécute le code donné dans le langage / dans le paradigme du système externe donné. De plus, cet opérateur vous permet de transférer des objets de types primitifs en tant que paramètres d'un tel appel, ainsi que d'écrire les résultats de l'appel dans les propriétés spécifiées (sans paramètres).

Actuellement, la plateforme prend en charge les types d'interactions / systèmes externes suivants:

HTTP - Exécute une requête http à partir d'un serveur Web.

Pour ce type d'interaction, vous devez spécifier une chaîne de requête (URL), qui détermine simultanément à la fois l'adresse du serveur et la demande à exécuter. Les paramètres peuvent être transférés aussi bien dans la ligne de requête (pour accéder au paramètre, le caractère spécial $ et le numéro de ce paramètre, à partir de 1) sont utilisés, ainsi que dans son corps (BODY). Il est supposé que tous les paramètres non utilisés dans la chaîne de requête sont transmis à BODY. S'il existe plusieurs paramètres dans BODY, le type de contenu BODY pendant la transmission est défini sur multipart / mixed et les paramètres sont transférés en tant que composants de ce BODY.

Lors du traitement des paramètres des classes de fichiers (FILE, PDFFILE, etc.) dans BODY, le type de contenu du paramètre est déterminé en fonction de l'extension du fichier (conformément au tableau suivant). Si l'extension de fichier ne figure pas dans ce tableau, le type de contenu est défini sur application / <extension de fichier>.

Si nécessaire, en utilisant l'option spéciale (HEADERS), vous pouvez définir les en-têtes de la demande exécutée. Pour ce faire, vous devez spécifier une propriété avec exactement un paramètre de la classe de chaîne dans laquelle le titre sera stocké et la valeur de la classe de chaîne dans laquelle la valeur de cet en-tête sera stockée.

Le résultat de la requête http est traité de la même manière que ses paramètres, uniquement dans le sens opposé: par exemple, si le type de contenu du résultat est soit présent dans le tableau suivant soit égal à application / *, alors on considère que le résultat obtenu est un fichier et doit être écrit dans une propriété avec la valeur FILE . Les en-têtes du résultat de la requête http sont traités par analogie avec les en-têtes de cette requête elle-même (à la seule différence que l'option est appelée HEADERSTO, pas HEADERS).
EXTERNAL HTTP GET 'https://www.cs.cmu.edu/~chuck/lennapg/len_std.jpg' TO exportFile;
open(exportFile());

LOCAL headers = STRING ( STRING );
headers( 'Authentication : Bearer' ) <- 'd43ks43ds343dd233' ';
EXTERNAL HTTP 'http://tryonline.lsfusion.org/exec?action=getExamples'
HEADERS headers
HEADERSTO headers
PARAMS JSONFILE ( '\{"mode"=1\}' )
TO exportFile;

IMPORT FROM exportFile() FIELDS () STRING caption, STRING code DO
MESSAGE 'Example : ' + caption + ', code : ' + code;
FOR v = headers( STRING s) DO
MESSAGE 'Result Header is : Key - ' + s + ', Value - ' + v;
SQL - exécution d'une commande de serveur SQL.

Pour ce type d'interaction, la chaîne de connexion et la ou les commandes SQL à exécuter sont spécifiées. Les paramètres peuvent être passés à la fois dans la chaîne de connexion et dans la commande SQL. Pour accéder au paramètre, le caractère spécial $ et le numéro de ce paramètre sont utilisés (à partir de 1).

Les paramètres des classes de fichiers (FILE, PDFFILE, etc.) ne peuvent être utilisés que dans la commande SQL. De plus, si l'un des paramètres pendant l'exécution est un fichier TABLE (TABLEFILE ou FILE avec l'extension de table), ce paramètre est également considéré comme une table dans ce cas:

  • avant d'exécuter la commande SQL, la valeur de chacun de ces paramètres est chargée sur le serveur dans une table temporaire
  • lors de la substitution de paramètres, ce n'est pas la valeur de paramètre elle-même qui est substituée, mais le nom de la table temporaire créée

Les résultats d'exécution sont: pour les requêtes DML - nombres égaux au nombre d'enregistrements traités, pour les requêtes SELECT - fichiers au format TABLE (FILE avec l'extension de table) contenant les résultats de ces requêtes. L'ordre de ces résultats coïncide avec l'ordre d'exécution des requêtes correspondantes dans la commande SQL.
externalSQL () {
EXPORT TABLE FROM bc=barcode(Article a) WHERE name(a) LIKE '%%' ; // -
EXTERNAL SQL 'jdbc:mysql://$1/test?user=root&password='
EXEC 'select price AS pc, articles.barcode AS brc from $2 x JOIN articles ON x.bc=articles.barcode'
PARAMS 'localhost' ,exportFile()
TO exportFile; // -

// -
LOCAL price = INTEGER ( INTEGER );
LOCAL barcode = STRING [ 30 ] ( INTEGER );
IMPORT FROM exportFile() TO price=pc,barcode=brc;
FOR barcode(Article a) = barcode( INTEGER i) DO
price(a) <- price(i);
}
LSF - un appel à l'action d'un autre serveur lsFusion.

Pour ce type d'interaction, la chaîne de connexion au serveur lsFusion (ou à son serveur Web, le cas échéant) est spécifiée, l'action à effectuer, ainsi qu'une liste de propriétés (sans paramètres), dans les valeurs desquelles les résultats de l'appel seront écrits. Les paramètres à transférer doivent coïncider en nombre et en classe avec les paramètres de l'action en cours.

La méthode de définition de l'action dans ce type d'interaction est parfaitement cohérente avec la méthode de définition de l'action lors de l'accès à partir d'un système externe (à propos de ce type d'accès dans la section suivante).
externalLSF() {
EXTERNAL LSF 'http://localhost:7651' EXEC 'System.testAction[]' ;
}
Par défaut, ce type d'interaction est implémenté à l'aide du protocole HTTP en utilisant les interfaces appropriées pour accéder à / depuis un système externe.

Dans le cas où vous devez accéder au système en utilisant un protocole différent de celui ci-dessus, vous pouvez toujours le faire en créant une action en Java et en implémentant cet appel là-bas (mais plus à ce sujet plus tard dans la section "Accès aux systèmes internes")

Appel d'un système externe


La plateforme permet aux systèmes externes d'accéder au système développé sur lsFusion en utilisant le protocole réseau HTTP. L'interface de cette interaction consiste à appeler une action avec les paramètres donnés et, si nécessaire, à renvoyer les valeurs de certaines propriétés (sans paramètres) en tant que résultats. On suppose que tous les objets de paramètres et de résultats sont des objets de types primitifs.

L'action appelée peut être définie de trois manières:

  • / exec? action = <nom de l'action> - définit le nom de l'action appelée.
  • / eval? script = <code> - définit le code dans lsFusion. On suppose que dans ce code il y a une déclaration d'une action avec le nom run, c'est cette action qui sera appelée. Si le paramètre de script n'est pas spécifié, il est supposé que le code est transmis en tant que premier paramètre BODY.
  • / eval / action? script = <code d'action> - définit le code d'action dans lsFusion. Pour accéder aux paramètres, vous pouvez utiliser le caractère spécial $ et le numéro de paramètre (à partir de 1).

Dans les deuxième et troisième cas, si le paramètre de script n'est pas spécifié, il est supposé que le code est transmis par le premier paramètre BODY.

Le traitement des paramètres et des résultats est symétrique à l'accès aux systèmes externes en utilisant le protocole HTTP (à la seule différence que les paramètres sont traités comme des résultats et, au contraire, les résultats sont traités comme des paramètres), donc nous ne nous répéterons pas beaucoup.

Par exemple, si nous avons une action:
importOrder( INTEGER no, DATE date, FILE detail) {
NEW o = FOrder {
no(o) <- no;
date(o) <- date;
LOCAL detailId = INTEGER ( INTEGER );
LOCAL detailQuantity = INTEGER ( INTEGER );
IMPORT FROM detail TO detailId, detailQuantity;
FOR imported( INTEGER i) DO {
NEW od = FOrderDetail {
id(od) <- detailId(i);
quantity(od) <- detailQuantity(i);
price(od) <- 5 ;
order(od) <- o;
}
}
APPLY ;
EXPORT JSON FROM price = price(FOrderDetail od), id = id(od) WHERE order(od) = o;
EXPORT FROM orderPrice(o), exportFile();
}
}
Ensuite, vous pouvez y accéder à l'aide d'une demande POST qui:
  • URL - http: // adresse_serveur / exec? Action = importOrder & p = 123 & p = 2019-01-01
  • BODY - fichier json avec chaînes de requête

Exemple d'appel Python
 import json import requests from requests_toolbelt.multipart import decoder lsfCode = ("run(INTEGER no, DATE date, FILE detail) {\n" " NEW o = FOrder {\n" " no(o) <- no;\n" " date(o) <- date;\n" " LOCAL detailId = INTEGER (INTEGER);\n" " LOCAL detailQuantity = INTEGER (INTEGER);\n" " IMPORT JSON FROM detail TO detailId, detailQuantity;\n" " FOR imported(INTEGER i) DO {\n" " NEW od = FOrderDetail {\n" " id(od) <- detailId(i);\n" " quantity(od) <- detailQuantity(i);\n" " price(od) <- 5;\n" " order(od) <- o;\n" " }\n" " }\n" " APPLY;\n" " EXPORT JSON FROM price = price(FOrderDetail od), id = id(od) WHERE order(od) == o;\n" " EXPORT FROM orderPrice(o), exportFile();\n" " }\n" "}") order_no = 354 order_date = '10.10.2017' order_details = [dict(id=1, quantity=10), dict(id=2, quantity=15), dict(id=5, quantity=4), dict(id=10, quantity=18), dict(id=11, quantity=1), dict(id=12, quantity=3)] order_json = json.dumps(order_details) url = 'http://localhost:7651/eval' payload = {'script': lsfCode, 'no': str(order_no), 'date': order_date, 'detail': ('order.json', order_json, 'text/json')} response = requests.post(url, files=payload) multipart_data = decoder.MultipartDecoder.from_response(response) sum_part, json_part = multipart_data.parts sum = int(sum_part.text) data = json.loads(json_part.text) ############################################################## print(sum) for item in data: print('{0:3}: price {1}'.format(int(item['id']), int(item['price']))) ############################################################## # 205 # 4: price 5 # 18: price 5 # 3: price 5 # 1: price 5 # 10: price 5 # 15: price 5 

Appel au système interne


Il existe deux types d'interaction interne:

Interopérabilité Java

Ce type d'interaction vous permet d'appeler du code Java à l'intérieur du serveur JVM lsFusion. Pour ce faire, vous devez:

  • assurez-vous que la classe Java compilée est accessible dans le chemin de classe du serveur d'applications. Il est également nécessaire que cette classe hérite de lsfusion.server.physics.dev.integration.internal.to.InternalAction.
    Exemple de classe Java
     import lsfusion.server.data.sql.exception.SQLHandledException; import lsfusion.server.language.ScriptingErrorLog; import lsfusion.server.language.ScriptingLogicsModule; import lsfusion.server.logics.action.controller.context.ExecutionContext; import lsfusion.server.logics.classes.ValueClass; import lsfusion.server.logics.property.classes.ClassPropertyInterface; import lsfusion.server.physics.dev.integration.internal.to.InternalAction; import java.math.BigInteger; import java.sql.SQLException; public class CalculateGCD extends InternalAction { public CalculateGCD(ScriptingLogicsModule LM, ValueClass... classes) { super(LM, classes); } @Override protected void executeInternal(ExecutionContext<ClassPropertyInterface> context) throws SQLException, SQLHandledException { BigInteger b1 = BigInteger.valueOf((Integer)getParam(0, context)); BigInteger b2 = BigInteger.valueOf((Integer)getParam(1, context)); BigInteger gcd = b1.gcd(b2); try { findProperty("gcd[]").change(gcd.intValue(), context); } catch (ScriptingErrorLog.SemanticErrorException ignored) { } } } 
  • enregistrer une action à l'aide de l'opérateur d'appel interne spécial (INTERNE)
    calculateGCD ' ' INTERNAL 'CalculateGCD' ( INTEGER , INTEGER );
  • une action enregistrée, comme toute autre, peut être appelée à l'aide de l'opérateur d'appel. Dans ce cas, la méthode executeInternal (lsfusion.server.logics.action.controller.context.ExecutionContext context) de la classe Java spécifiée sera exécutée.
    //
    FORM gcd ''
    OBJECTS (a = INTEGER , b = INTEGER ) PANEL
    PROPERTIES 'A' = VALUE (a), 'B' = VALUE (b)

    PROPERTIES gcd(), calculateGCD(a, b)
    ;

    //
    run() {
    calculateGCD( 100 , 200 );
    }
Interaction SQL

Ce type d'interaction vous permet d'accéder aux constructions objets / syntaxe du serveur SQL utilisées par le système lsFusion développé. Pour implémenter ce type d'interaction dans la plateforme, un opérateur spécial est utilisé - FORMULE. Cet opérateur vous permet de créer une propriété qui évalue une formule dans le langage SQL. La formule est définie sous la forme d'une chaîne à l'intérieur de laquelle le caractère spécial $ et le numéro de ce paramètre sont utilisés pour accéder au paramètre (à partir de 1). En conséquence, le nombre de paramètres de la propriété obtenue sera égal au maximum des nombres des paramètres utilisés.
round(number, digits) = FORMULA 'round(CAST(($1) as numeric),$2)' ; // :
jumpWorkdays = FORMULA NULL DATE PG 'jumpWorkdays($1, $2, $3)' , MS 'dbo.jumpWorkdays($1, $2, $3)' ; // SQL
Il est recommandé d'utiliser cet opérateur uniquement dans les cas où la tâche ne peut pas être résolue avec l'aide d'autres opérateurs, ainsi que s'il est garanti quels serveurs SQL spécifiques peuvent être utilisés ou si les constructions de syntaxe utilisées sont conformes à l'une des dernières normes SQL.

Appel du système interne


Tout est symétrique à l'appel au système interne. Il existe deux types d'interaction:

Interopérabilité Java

Dans le cadre de ce type d'interaction, le système interne peut accéder directement aux éléments Java du système lsFusion (comme les objets Java ordinaires). Ainsi, vous pouvez effectuer toutes les mêmes opérations que l'utilisation des protocoles réseau, mais en même temps éviter une surcharge importante d'une telle interaction (par exemple, sérialisation des paramètres / désérialisation du résultat, etc.). De plus, cette méthode de communication est beaucoup plus pratique et efficace si l'interaction est très étroite (c'est-à-dire que pendant l'exécution d'une opération, un contact constant est requis dans les deux sens - du système lsFusion à un autre système et vice versa) et / ou nécessite l'accès à des nœuds de plateforme spécifiques.

, Java- lsFusion- , , Java . :

  • lsFusion ( ), « » , « » ( lsfusion.server.physics.dev.integration.internal.to.InternalAction, , , ).
  • , lsFusion , Spring bean', - , dependency injection ( bean businessLogics).

Java-
 import lsfusion.server.data.sql.exception.SQLHandledException; import lsfusion.server.data.value.DataObject; import lsfusion.server.language.ScriptingErrorLog; import lsfusion.server.language.ScriptingLogicsModule; import lsfusion.server.logics.action.controller.context.ExecutionContext; import lsfusion.server.logics.classes.ValueClass; import lsfusion.server.logics.property.classes.ClassPropertyInterface; import lsfusion.server.physics.dev.integration.internal.to.InternalAction; import java.math.BigInteger; import java.sql.SQLException; public class CalculateGCDObject extends InternalAction { public CalculateGCDObject(ScriptingLogicsModule LM, ValueClass... classes) { super(LM, classes); } @Override protected void executeInternal(ExecutionContext<ClassPropertyInterface> context) throws SQLException, SQLHandledException { try { DataObject calculation = (DataObject)getParamValue(0, context); BigInteger a = BigInteger.valueOf((Integer)findProperty("a").read(context, calculation)); BigInteger b = BigInteger.valueOf((Integer)findProperty("b").read(context, calculation)); BigInteger gcd = a.gcd(b); findProperty("gcd[Calculation]").change(gcd.intValue(), context, calculation); } catch (ScriptingErrorLog.SemanticErrorException ignored) { } } } 

SQL-

SQL- lsFusion- ( , , SQL-), , lsFusion-, SQL-. , , ( / ), (, , — , ..), . lsFusion- , , .

, ( ) OLAP-, .

La migration


. , , , - . , «» , - . , migration.script, classpath , , . :

, , . , , , . , . , . :

 V< > { 1 ... N } 

, , :
DATA PROPERTY oldNS.oldName[class1,...,classN] -> newNS.newName[class1,...,classN]
CLASS oldNS.oldName -> newNS.newName
OBJECT oldNS.oldClassName.oldName -> newNS.newClassName.newName

TABLE oldNS.oldName -> newNS.newName
PROPERTY oldNS.oldName[class1,...,classN] -> newNS.newName[class1,...,classN]
FORM PROPERTY oldNS.oldFormName.oldName(object1,...,objectN) -> newNS.newFormName.newName(object1,...,objectN)
NAVIGATOR oldNS.oldName -> newNS.newName
( , , ). :

  • ( , .)
  • ( ).

, , .

V0. 3.1 {
DATA PROPERTY Item.gender[Item.Article] -> Item.dataGender[Item.Article] // DATA
PROPERTY System.SIDProperty[Reflection.Property] -> Reflection.dbNameProperty[Reflection.Property] //
FORM PROPERTY Item.itemForm.name(i) -> Item.itemForm.itemName(i)
}

V0. 4 {
FORM PROPERTY Document.documentForm.name(i) -> Document.itemForm.itemName(i)
FORM PROPERTY Item.itemForm.itemName(i) -> Item.itemForm.iname // : iname = itemName(i)
CLASS Date.DateInterval -> Date.Interval
OBJECT Geo.Direction.North -> Geo.Direction.north
TABLE User.oldTable -> User.newTable
}


, IDE. , , Change migration file ( ), IDE .


, . , , : , , , .. lsFusion ( , 'abc'), , :
script '{scheduler.script.scheduled.task.detail}' = DATA TEXT (ScheduledTaskDetail);
CONSTRAINT script(ScheduledTaskDetail d) AND action(d) MESSAGE '{scheduler.constraint.script.and.action}' ;
FORM scheduledTask '{scheduler.form.scheduled.task}' ;
ServerResourceBundle.properties:
 scheduler.script.scheduled.task.detail=Script scheduler.constraint.script.and.action=In the scheduler task property and script cannot be selected at the same time scheduler.form.scheduled.task=Tasks 

ServerResourceBundle_ru.properties
 scheduler.script.scheduled.task.detail= scheduler.constraint.script.and.action=           scheduler.form.scheduled.task= 


, , - . , , , .

: . , — . ( - ), , , :

  • . ( , ), , , .
  • . , , .
  • . / , . «» .


. , . , , ( ). , .

, , NULL

, , , , .


. , . , , , , , .

( ).

( , , , ). , . , .
INDEX customer(Order o);

date = DATA DATE (Order);
INDEX date(Order o), o;

INDEX name(Sku s), price(s, DATE d), d;


lsFusion . , , , . key0, key1, ..., keyN, (, — ). , .

, .
TABLE book (Book);

in = DATA BOOLEAN (Sku, Stock);
TABLE skuStock (Sku, Stock); // in

price = DATA NUMERIC [ 10 , 2 ] (Sku, DATE );
TABLE skuDate (Sku, DATE ); // Sku

TABLE sku (Sku);
, . , . , , , «» ( , , ).

, , . .
La politique
( )___1
_2..._N
__
, , . , , - .

, , , (, ), , , , .

Conclusion


: « , ». , , , / lsFusion , , . — .

, , . , , , , , . , , , , , .

: « ...?», , , , tutorial'.

Source: https://habr.com/ru/post/fr460887/


All Articles