MENU

Documentatie  /   Aan de slag met MetaFactory

Deze handleiding helpt je om snel aan de slag te gaan met de installatie van MetaFactory en een demo project te maken in 15 minuten.

  1. Installeren van MetaFactory
  • Vraag om een evaluatie versie van MetaFactory door het versturen van een email naar info@metafactory.nl
  • De installatie bestaat uit het uitpakken van de gestuurde zip (of dmg). Windows gebruikers starten MetaFactory op door run64.cmd uit te voeren. De OSX gebruiker kan de MetaFactory.app starten (of run64.sh in de .app folder). Lees meer over de installatie voor Windows of OSX/macOS.
  • Maak een nieuw project (File –> New)
    • Er wordt gevraagd waar het xml bestand moet worden opgeslagen, kies de gewenste locatie.
    • Er wordt een standaard map structuur gemaakt inclusief een simpele versie van model.xml en pattern.xml. MetaFactory vereist deze beide bestanden met een minimale inhoud, dus vandaar dat ze gemaakt worden door MetaFactory.

Leer meer over de functionaliteit van metafactory.xml, model.xml en pattern.xml:

Je hebt nu MetaFactory geinstalleerd en een minimal project dat nog weinig doet. Maak je gebruik van een IDE, dan raden we je nu aan om deze te configureren voor MetaFactory, zodat de IDE de xml bestanden kent auto-complete mogelijk maakt. Lees meer over de configuratie van je IDELaten we nu ons eerste voorbeeld maken.

Een applicatie schrijven met veel entiteiten wordt al snel een saai werkje. De entiteit classes bevatten velden, nul of meer (overgeërfde) constructors en een verzameling getters en setters. Voor al deze entiteiten heb je aparte controllers, repositories enz. nodig. Om het proces van het schrijven van al deze classes te automatiseren beginnen we met het maken van een java interface en een bijbehorende implementatie van elke intiteit (pojo interface en pojo entiteit).

Het volgende voorbeeld laat zien hoe je een pattern schrijft die pojo classes en interfaces maakt. Voor een nog simpeler voorbeeld wordt verwezen naar het MetaFactory Hello World voorbeeld.

Eerst definieren we ons model (in model.xml):

<?xml version="1.0" encoding="UTF-8"?>
<model xmlns="http://www.firstbase.nl/xsd/personaliom/model" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.firstbase.nl/xsd/personaliom/model http://www.firstbase.nl/xsd/personaliom/model.xsd">
  <package name="domain_model">
    <object name="Person">
      <attribute name="firstName" type="String" length="50" notnull="true"></attribute>
      <attribute name="lastName" type="String" length="50" notnull="true"></attribute>
      <reference name="homeAddress" type="Address" notnull="true" multiplicity="1..1"></reference>
      <reference name="workAddress" type="Address" notnull="false" multiplicity="0..1"></reference>
      <reference name="phone" type="Phone" notnull="false" multiplicity="0..n"></reference>
    </object>
    <object name="Address">
      <attribute name="streetName" type="String" length="100" notnull="true"></attribute>
      <attribute name="zipCode" type="String" length="10" notnull="false"></attribute>
      <attribute name="city" type="String" length="50" notnull="true"></attribute>
      <attribute name="country" type="String" length="50" notnull="false"></attribute>
    </object>
    <object name="Phone">
      <attribute name="number" type="String" length="30" notnull="true"></attribute>
      <attribute name="description" type="String" length="100" notnull="true"></attribute>
      <reference name="person" type="Person" notnull="true" multiplicity="1..1"></reference>
    </object>
  </package>
</model>

Verschillende attributen zoals type,length en notnull zitten in bovenstaand code voorbeeld. Deze attributen doen nog niets zolang er niet expliciet gebruik van wordt gemaakt in het pattern. MetaFactory is zodanig gemaakt dat er geen gebruik wordt gemaakt van impliciete, verborgen functies of standaard gedrag, waardoor het weliswaar heel “verbose”, maar wel volledig transparant is wat er gebeurt. Dit geeft de ontwikkelaar ook volledige controle.

Het <reference> element bevat een attribuut met de naam ‘multiplicity’. Dit attribuut geeft aan hoeveel referenties van het gegeven type het object kan opslaan. De waarde ‘0..n’ geeft bijvoorbeeld aan het dat het object nul of meer entiteiten van het referentie type kan bevatten. Mogelijke waardes zijn 0..1, 1..1, 0..n en 1..n. Het pattern kan het multiplicity attribuut uit het model gebruiken in een ‘condition’ om te beslissen wanneer code gemaakt moet worden.

Laten we nu eens kijken naar het pattern waarmee de interfaces en classes gemaakt worden voor dit model.

<?xml version="1.0" encoding="UTF-8"?>
<pattern xmlns="http://www.firstbase.nl/xsd/personaliom/pattern" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.firstbase.nl/xsd/personaliom/pattern http://www.firstbase.nl/xsd/personaliom/pattern.xsd">
  <properties>
    <java.output.directory>java</java.output.directory>
  </properties>
  <package name="com.firstbase.ba.example6a.pojo" package="domain_model">
    <interface name="I${object.name}" foreach="object">
      <attribute name="id" access="rw">
        <datatype>long</datatype>
        <apicomment>primary key</apicomment>
      </attribute>
      <attribute name="${attribute.name}" access="rw" foreach="attribute">
        <datatype>${attribute.type}</datatype>
        <apicomment>${attribute.name} property</apicomment>
      </attribute>
      <attribute name="${reference.name}" foreach="reference" access="rw" condition="${reference.multiplicity}=0..1 OR ${reference.multiplicity}=1..1">
        <datatype>I${reference.type}</datatype>
        <apicomment>Reference to I${reference.type} object</apicomment>
      </attribute>
      <attribute name="${reference.name}Set" foreach="reference" access="rw" condition="${reference.multiplicity}=0..n OR ${reference.multiplicity}=1..n">
        <library>java.util.Set</library>
        <datatype><![CDATA[Set<I${reference.type}>]]>
        </datatype>
        <apicomment>Set of I${reference.type} objects</apicomment>
      </attribute>
      <operation name="add${firstUpper(${reference.name})}" foreach="reference" condition="${reference.multiplicity}=0..n OR ${reference.multiplicity}=1..n">
        <apicomment>Utility method to add a object of type I${reference.type} to ${reference.name}Set</apicomment>
        <parameter name="${firstLower(${reference.name})}">
          <datatype> I${reference.type}</datatype>
          <apicomment> object to add to ${reference.name}Set</apicomment>
        </parameter>
      </operation>
      <operation name="delete${firstUpper(${reference.name})}" foreach="reference" condition="${reference.multiplicity}=0..n OR ${reference.multiplicity}=1..n">
        <parameter name="${firstLower(${reference.name})}">
          <datatype> I${reference.type}</datatype>
        </parameter>
      </operation>
    </interface>
    <class name="${object.name}Impl" foreach="object">
      <implements>I${object.name}</implements>
      <attribute name="id" access="rw">
        <datatype>long</datatype>
        <apicomment>primary key</apicomment>
      </attribute>
      <attribute name="${attribute.name}" foreach="attribute" access="rw">
        <datatype>${attribute.type}</datatype>
      </attribute>
      <attribute name="${reference.name}" foreach="reference" access="rw" condition="${reference.multiplicity}=0..1 OR ${reference.multiplicity}=1..1">
        <datatype> I${reference.type}</datatype>
      </attribute>
      <attribute name="${reference.name}Set" foreach="reference" access="rw" condition="${reference.multiplicity}=0..n OR ${reference.multiplicity}=1..n">
        <library>java.util.Set</library>
        <library>java.util.HashSet</library>
        <datatype><![CDATA[Set<I${reference.type}>]]></datatype>
        <body><![CDATA[
          new HashSet<I${reference.type}>()
        ]]></body>
      </attribute>
      <operation name="add${firstUpper(${reference.name})}" foreach="reference" condition="${reference.multiplicity}=0..n OR ${reference.multiplicity}=1..n">
        <parameter name="${firstLower(${reference.name})}">
          <datatype> I${reference.type}</datatype>
        </parameter>
        <body> ${firstLower(${reference.name})}.${getSetterName(${reference.type},${object.name})}(this);
          ${reference.name}Set.add(${firstLower(${reference.name})});</body>
      </operation>
      <operation name="delete${firstUpper(${reference.name})}" foreach="reference" condition="${reference.multiplicity}=0..n OR ${reference.multiplicity}=1..n">
        <parameter name="${firstLower(${reference.name})}">
          <datatype> I${reference.type}</datatype>
        </parameter>
        <body> ${firstLower(${reference.name})}.${getSetterName(${reference.type},${object.name})}(null);
          ${reference.name}Set.remove(${firstLower(${reference.name})});</body>
      </operation>
    </class>
  </package>
</pattern>

Enige uitleg over dit pattern:

  • Het <package> element wordt gebruikt om het java package op te geven waarin de java classes en interfaces gemaakt worden. Dit element heeft ook een ‘package’ attribuut, waarmee wordt verwezen naar de groep van objecten uit het model, zoals gedefinieerd in model.xml.
  • De model objecten kunnen dynamisch worden aangesproken in het pattern via de MetaFactory expression language.
  • <![CDATA ]> tags zijn (helaas) soms nodig om bijzondere symbolen te kunnen gebruiken zoals  ‘<‘
  • Het <attribute> element in het pattern bevat een xml attribute ‘access=”rw” waarmee MetaFactory wordt verteld dat er zowel getters als setters gemaakt moeten worden.
  • Het <operation> element wordt gebruikt om methodes te maken in de java class of interface.

Als je zowel het pattern als het model bijgewerkt hebt in je project, dan wordt het tijd om met MetaFactory de java code te produceren (menu Transform -> Run). Je kan hier de gemaakte code downloaden

Tip: voeg wat nieuwe entiteiten toe aan je model en kijk welke extra code er bij komt.

Gefeliciteerd, je hebt zojuist je eerste code met MetaFactory gemaakt! We moeten wel opmerken dat het maken van lange expressies in een condition attribuut of het schrijven van veel code in xml ongewenst is. Ook kan de xml code in het pattern weinig intelligentie bevatten. Gelukkig kunnen we daarom gebruik maken van snippets (Freemarker of Velocity templates) om de wat geavanceerdere code te maken. De uitvoer van een snippet (true, false, stukje code enz.) kan heel gemakkelijk gebruikt worden in het pattern. Om meer te leren over het gebruik van snippets bekijk dit voorbeeld.