Gids voor het Kotlin Exposed Framework

1. Inleiding

In deze zelfstudie gaan we bekijken hoe u een relationele database kunt opvragen met Exposed.

Exposed is een open source-bibliotheek (Apache-licentie) ontwikkeld door JetBrains, die een idiomatische Kotlin-API biedt voor sommige relationele database-implementaties, terwijl de verschillen tussen databaseleveranciers worden gladgestreken.

Exposed kan zowel worden gebruikt als een DSL op hoog niveau over SQL en als een lichtgewicht ORM (Object-Relational Mapping). Daarom behandelen we beide toepassingen in de loop van deze zelfstudie.

2. Exposed Framework-instelling

Exposed staat nog niet op Maven Central, dus we moeten een speciale repository gebruiken:

  blootgesteld blootgesteld //dl.bintray.com/kotlin/exposed 

Vervolgens kunnen we de bibliotheek opnemen:

 org.jetbrains. belicht blootgesteld 0.10.4 

In de volgende secties laten we ook voorbeelden zien waarbij de H2-database in het geheugen wordt gebruikt:

 com.h2database h2 1.4.197 

We kunnen de nieuwste versie van Exposed vinden op Bintray en de nieuwste versie van H2 op Maven Central.

3. Verbinding maken met de database

We definiëren databaseverbindingen met de Database klasse:

Database.connect ("jdbc: h2: mem: test", driver = "org.h2.Driver")

We kunnen ook een gebruiker en een wachtwoord als genoemde parameters:

Database.connect ("jdbc: h2: mem: test", driver = "org.h2.Driver", user = "mezelf", wachtwoord = "geheim")

Merk op dat het aanroepen aansluiten brengt niet meteen een verbinding tot stand met de database. Het slaat gewoon de verbindingsparameters op voor later.

3.1. Aanvullende parameters

Als we andere verbindingsparameters moeten opgeven, gebruiken we een andere overbelasting van het aansluiten methode die ons volledige controle geeft over het verwerven van een databaseverbinding:

Database.connect ({DriverManager.getConnection ("jdbc: h2: mem: test; MODE = MySQL")})

Deze versie van aansluiten vereist een sluitingsparameter. Exposed roept de sluiting aan wanneer er een nieuwe verbinding met de database nodig is.

3.2. Gebruik maken van een Databron

Als we in plaats daarvan verbinding maken met de database met behulp van een Databron, zoals gewoonlijk het geval is in bedrijfstoepassingen (bijvoorbeeld om te profiteren van pooling van verbindingen), kunnen we het juiste aansluiten overbelasten:

Database.connect (gegevensbron)

4. Een transactie openen

Elke databasebewerking in Exposed heeft een actieve transactie nodig.

De transactie methode sluit af en roept deze op met een actieve transactie:

transactie {// Doe coole dingen}

De transactie geeft terug wat de sluiting terugkeert. Vervolgens sluit Exposed automatisch de transactie wanneer de uitvoering van het blok wordt beëindigd.

4.1. Vastleggen en terugdraaien

Wanneer de transactie block keert succesvol terug, Exposed pleegt de transactie. Wanneer in plaats daarvan de sluiting wordt afgesloten door een uitzondering te werpen, draait het raamwerk de transactie terug.

We kunnen een transactie ook handmatig vastleggen of terugdraaien. De sluiting die we bieden transactie is eigenlijk een instantie van de Transactie klasse dankzij Kotlin magie.

We hebben dus een plegen en een terugrollen beschikbare methode:

transactie {// Doe wat dingen commit () // Doe andere dingen}

4.2. Verklaringen loggen

Bij het leren van het framework of het debuggen, kunnen we het nuttig vinden om de SQL-statements en queries te inspecteren die Exposed naar de database stuurt.

Zo'n logger kunnen we eenvoudig aan de actieve transactie toevoegen:

transactie {addLogger (StdOutSqlLogger) // Dingen doen}

5. Tabellen definiëren

Meestal werken we in Exposed niet met onbewerkte SQL-strings en namen. In plaats daarvan definiëren we tabellen, kolommen, sleutels, relaties, enz. Met behulp van een DSL op hoog niveau.

We vertegenwoordigen elke tabel met een instantie van de Tafel klasse:

object StarWarsFilms: Table ()

Exposed berekent automatisch de naam van de tabel op basis van de klassenaam, maar we kunnen ook een expliciete naam opgeven:

object StarWarsFilms: Table ("STAR_WARS_FILMS")

5.1. Kolommen

Een tabel heeft geen betekenis zonder kolommen. We definiëren kolommen als eigenschappen van onze tabelklasse:

object StarWarsFilms: Table () {val id = integer ("id"). autoIncrement (). primaryKey () val sequelId = integer ("sequel_id"). uniqueIndex () val name = varchar ("name", 50) val director = varchar ("director", 50)}

We hebben de typen weggelaten voor beknoptheid, omdat Kotlin ze voor ons kan afleiden. Hoe dan ook, elke kolom is van het type Kolom en het heeft een naam, een type en mogelijk typeparameters.

5.2. Primaire sleutels

Zoals we kunnen zien aan de hand van het voorbeeld in de vorige sectie, we kunnen eenvoudig indexen en primaire sleutels definiëren met een vloeiende API.

Voor het algemene geval van een tabel met een primaire sleutel met een geheel getal, biedt Exposed echter klassen IntIdTable en LongIdTable die voor ons de sleutel bepalen:

object StarWarsFilms: IntIdTable () {val sequelId = integer ("sequel_id"). uniqueIndex () val name = varchar ("name", 50) val director = varchar ("director", 50)}

Er is ook een UUIDTable; Bovendien kunnen we onze eigen varianten definiëren door middel van subclassificatie IdTable.

5.3. Vreemde sleutels

Vreemde sleutels zijn gemakkelijk in te voeren. We profiteren ook van statisch typen omdat we altijd verwijzen naar eigenschappen die bekend zijn tijdens het compileren.

Stel dat we de namen willen volgen van de acteurs die in elke film spelen:

object Players: Table () {val sequelId = integer ("sequel_id") .uniqueIndex () .references (StarWarsFilms.sequelId) val name = varchar ("name", 50)}

Om te voorkomen dat u het type kolom moet spellen (in dit geval geheel getal) als het kan worden afgeleid uit de kolom waarnaar wordt verwezen, kunnen we de referentie methode als een afkorting:

val sequelId = reference ("sequel_id", StarWarsFilms.sequelId) .uniqueIndex ()

Als de verwijzing naar de primaire sleutel is, kunnen we de naam van de kolom weglaten:

val filmId = reference ("film_id", StarWarsFilms)

5.4. Tabellen maken

We kunnen de tabellen zoals hierboven gedefinieerd programmatisch maken:

transactie {SchemaUtils.create (StarWarsFilms, Players) // Dingen doen}

De tabellen worden alleen gemaakt als ze nog niet bestaan. Er is echter geen ondersteuning voor databasemigraties.

6. Vragen

Nadat we enkele tabelklassen hebben gedefinieerd, zoals we in de vorige secties hebben laten zien, kunnen we query's naar de database sturen door de uitbreidingsfuncties te gebruiken die door het framework worden geboden.

6.1. Selecteer alles

Om gegevens uit de database te halen, gebruiken we Vraag objecten opgebouwd uit tabelklassen. De eenvoudigste query is er een die alle rijen van een bepaalde tabel retourneert:

val query = StarWarsFilms.selectAll ()

Een vraag is een Herhaalbaar, dus het ondersteunt voor elk:

query.forEach {assertTrue {it [StarWarsFilms.sequelId]> = 7}}

De sluitingsparameter, impliciet aangeroepen het in het bovenstaande voorbeeld is een instantie van de ResultRow klasse. We kunnen het zien als een kaart die per kolom is ingetoetst.

6.2. Een subset kolommen selecteren

We kunnen ook een subset van de kolommen van de tabel selecteren, d.w.z. een projectie uitvoeren met behulp van de plak methode:

StarWarsFilms.slice (StarWarsFilms.name, StarWarsFilms.director) .selectAll () .forEach {assertTrue {it [StarWarsFilms.name] .startsWith ("The")}}

We gebruiken plak om ook een functie op een kolom toe te passen:

StarWarsFilms.slice (StarWarsFilms.name.countDistinct ())

Vaak bij het gebruik van geaggregeerde functies zoals tellen en gemiddeld, we hebben een group by-clausule nodig in de query. We zullen in sectie 6.5 over de groep praten.

6.3. Filteren met Where-uitdrukkingen

Exposed bevat een speciale DSL voor waar uitdrukkingen, die worden gebruikt om query's en andere soorten instructies te filteren. Dit is een minitaal op basis van de kolomeigenschappen die we eerder zijn tegengekomen en een reeks booleaanse operatoren.

Dit is een waar uitdrukking:

{(StarWarsFilms.director zoals "J.J.%") en (StarWarsFilms.sequelId eq 7)}

Het type is complex; het is een subklasse van SqlExpressionBuilder, die operators zoals zoals, eq, en. Zoals we kunnen zien, is het een opeenvolging van vergelijkingen gecombineerd met en en of operators.

We kunnen zo'n uitdrukking doorgeven aan de selecteer methode, die opnieuw een query retourneert:

val select = StarWarsFilms.select {...} assertEquals (1, select.count ())

Dankzij type-inferentie hoeven we het complexe type van de where-expressie niet te spellen wanneer deze direct wordt doorgegeven aan de selecteer methode zoals in het bovenstaande voorbeeld.

Aangezien expressies Kotlin-objecten zijn, zijn er geen speciale voorzieningen voor queryparameters. We gebruiken simpelweg variabelen:

val sequelNo = 7 StarWarsFilms.select {StarWarsFilms.sequelId> = sequelNo}

6.4. Geavanceerde filtering

De Vraag objecten geretourneerd door selecteer en zijn varianten hebben een aantal methoden die we kunnen gebruiken om de query te verfijnen.

We willen bijvoorbeeld dubbele rijen uitsluiten:

query.withDistinct (true) .forEach {...}

Of we willen misschien alleen een subset van de rijen retourneren, bijvoorbeeld bij het pagineren van de resultaten voor de gebruikersinterface:

query.limit (20, offset = 40) .forEach {...}

Deze methoden retourneren een nieuw Vraag, zodat we ze gemakkelijk kunnen ketenen.

6.5. BestellenDoor en GroepDoor

De Query.orderBy methode accepteert een lijst met kolommen die zijn toegewezen aan een Sorteervolgorde waarde die aangeeft of het sorteren oplopend of aflopend moet zijn:

query.orderBy (StarWarsFilms.name naar SortOrder.ASC)

Terwijl de groepering op basis van een of meer kolommen, vooral handig bij het gebruik van geaggregeerde functies (zie paragraaf 6.2.), Wordt bereikt met de groupBy methode:

StarWarsFilms .slice (StarWarsFilms.sequelId.count (), StarWarsFilms.director) .selectAll () .groupBy (StarWarsFilms.director)

6.6. Doet mee

Joins zijn misschien wel een van de verkoopargumenten van relationele databases. In de meest eenvoudige gevallen, als we een externe sleutel hebben en geen voorwaarden voor join, kunnen we een van de ingebouwde join-operators gebruiken:

(StarWarsFilms innerJoin Players) .selectAll ()

Hier hebben we laten zien innerJoin, maar we hebben ook links, rechts en cross-join beschikbaar met hetzelfde principe.

Vervolgens kunnen we join-voorwaarden toevoegen met een where-uitdrukking; Als er bijvoorbeeld geen externe sleutel is en we de join expliciet moeten uitvoeren:

(StarWarsFilms innerJoin Players). Selecteer {StarWarsFilms.sequelId eq Players.sequelId}

In het algemeen is de volledige vorm van een join als volgt:

val complexJoin = Doe mee (StarWarsFilms, Players, onColumn = StarWarsFilms.sequelId, otherColumn = Players.sequelId, joinType = JoinType.INNER, additionalConstraint = {StarWarsFilms.sequelId eq 8}) complexJoin.selectAll ()

6.7. Aliasing

Dankzij de toewijzing van kolomnamen aan eigenschappen, hebben we geen aliasing nodig in een typische join, zelfs niet als de kolommen dezelfde naam hebben:

(StarWarsFilms innerJoin Players) .selectAll () .forEach {assertEquals (it [StarWarsFilms.sequelId], it [Players.sequelId])}

In het bovenstaande voorbeeld StarWarsFilms.sequelId en Players.sequelId zijn verschillende kolommen.

Als dezelfde tabel echter meer dan eens in een query voorkomt, willen we deze misschien een alias geven. Daarvoor gebruiken we de alias functie:

val sequel = StarWarsFilms.alias ("sequel")

We kunnen de alias dan een beetje als een tabel gebruiken:

Doe mee met (StarWarsFilms, sequel, additionalConstraint = {vervolg [StarWarsFilms.sequelId] eq StarWarsFilms.sequelId + 1}). SelectAll (). Voor elke {assertEquals (het [vervolg [StarWarsFilms.sequelId]], het [StarWarsFilms] + 1sequelId]. )}

In het bovenstaande voorbeeld kunnen we zien dat de vervolg alias is een tafel die deelneemt aan een join. Als we toegang willen hebben tot een van de kolommen, gebruiken we de kolom van de alias in de tabel als sleutel:

vervolg [StarWarsFilms.sequelId]

7. Verklaringen

Nu we hebben gezien hoe we de database kunnen doorzoeken, gaan we kijken hoe we DML-instructies kunnen uitvoeren.

7.1. Gegevens invoegen

Om gegevens in te voegen, noemen we een van de varianten van de invoegen functie. Alle varianten hebben een sluiting:

StarWarsFilms.insert {it [name] = "The Last Jedi" it [sequelId] = 8 it [director] = "Rian Johnson"}

Er zijn twee opmerkelijke objecten betrokken bij de bovenstaande sluiting:

  • dit (de sluiting zelf) is een voorbeeld van de StarWarsFilms klasse; daarom hebben we toegang tot de kolommen, die eigenschappen zijn, met hun ongekwalificeerde naam
  • het (de sluitingsparameter) is een InsertStatement; ikt is een kaartachtige structuur met een sleuf voor elke kolom om in te voegen

7.2. Kolomwaarden automatisch verhogen

Als we een invoeginstructie hebben met automatisch gegenereerde kolommen (meestal automatisch ophogen of reeksen), willen we misschien de gegenereerde waarden ophalen.

In het typische geval hebben we maar één gegenereerde waarde en bellen we insertAndGetId:

val id = StarWarsFilms.insertAndGetId {it [name] = "The Last Jedi" it [sequelId] = 8 it [director] = "Rian Johnson"} assertEquals (1, id.value)

Als we meer dan één gegenereerde waarde hebben, kunnen we ze bij naam lezen:

val insert = StarWarsFilms.insert {it [name] = "The Force Awakens" it [sequelId] = 7 it [director] = "J.J. Abrams"} assertEquals (2, voeg [StarWarsFilms.id] ?. waarde in)

7.3. Gegevens bijwerken

We kunnen nu gebruiken wat we hebben geleerd over query's en invoegingen om bestaande gegevens in de database bij te werken. Inderdaad, een simpele update ziet eruit als een combinatie van een selectie met een insert:

StarWarsFilms.update ({StarWarsFilms.sequelId eq 8}) {it [name] = "Episode VIII - The Last Jedi"}

We kunnen het gebruik van een where-uitdrukking zien in combinatie met een UpdateStatement sluiting. In feite, UpdateStatement en InsertStatement delen de meeste API en logica via een gemeenschappelijke superklasse, UpdateBuilder, die de mogelijkheid biedt om de waarde van een kolom in te stellen met behulp van idiomatische vierkante haken.

Wanneer we een kolom moeten bijwerken door een nieuwe waarde te berekenen op basis van de oude waarde, gebruiken we de SqlExpressionBuilder:

StarWarsFilms.update ({StarWarsFilms.sequelId eq 8}) {met (SqlExpressionBuilder) {it.update (StarWarsFilms.sequelId, StarWarsFilms.sequelId + 1)}}

Dit is een object dat tussenvoegseloperatoren biedt (zoals plus, minus enzovoort) die we kunnen gebruiken om een ​​update-instructie samen te stellen.

7.4. Gegevens verwijderen

Ten slotte kunnen we gegevens verwijderen met de deleteWhere methode:

StarWarsFilms.deleteWhere ({StarWarsFilms.sequelId eq 8})

8. De DAO API, een lichtgewicht ORM

Tot nu toe hebben we Exposed gebruikt om bewerkingen op Kotlin-objecten rechtstreeks toe te wijzen aan SQL-query's en -instructies. Elke methode-aanroep zoals invoegen, bijwerken, selecteren enzovoort resulteert in een SQL-string die onmiddellijk naar de database wordt gestuurd.

Exposed heeft echter ook een DAO-API op een hoger niveau die een eenvoudige ORM vormt. Laten we daar nu eens op ingaan.

8.1. Entiteiten

In de vorige secties hebben we klassen gebruikt om databasetabellen weer te geven en om bewerkingen erover uit te drukken, met behulp van statische methoden.

Als we nog een stap verder gaan, kunnen we entiteiten definiëren op basis van die tabelklassen, waarbij elke instantie van een entiteit een databaserij vertegenwoordigt:

class StarWarsFilm (id: EntityID): Entity (id) {begeleidend object: EntityClass (StarWarsFilms) var sequelId door StarWarsFilms.sequelId var naam door StarWarsFilms.name var director door StarWarsFilms.director}

Laten we nu de bovenstaande definitie stuk voor stuk analyseren.

Op de eerste regel kunnen we zien dat een entiteit een klasse is die zich uitbreidt Entiteit. Het heeft een ID met een specifiek type, in dit geval Int.

klasse StarWarsFilm (id: EntityID): Entity (id) {

Dan komen we een definitie van een begeleidend object tegen. Het begeleidende object vertegenwoordigt de entiteitsklasse, dat wil zeggen de statische metagegevens die de entiteit definiëren en de bewerkingen die we erop kunnen uitvoeren.

Verder verbinden we in de declaratie van het begeleidende object de entiteit, StarWarsFilm - enkelvoud, aangezien het een enkele rij vertegenwoordigt Naar de tafel, StarWarsFilms - meervoud, omdat het de verzameling van alle rijen vertegenwoordigt.

begeleidend object: EntityClass (StarWarsFilms)

Ten slotte hebben we de eigenschappen, geïmplementeerd als eigenschap delegeert naar de corresponderende tabelkolommen.

var sequelId door StarWarsFilms.sequelId var naam door StarWarsFilms.name var director door StarWarsFilms.director

Merk op dat we eerder de kolommen hebben gedeclareerd met val omdat het onveranderlijke metadata zijn. Nu declareren we in plaats daarvan de entiteitseigenschappen met var, omdat het veranderlijke slots in een databaserij zijn.

8.2. Gegevens invoegen

Om een ​​rij in een tabel in te voegen, maken we eenvoudig een nieuwe instantie van onze entiteitsklasse met behulp van de statische fabrieksmethode nieuw in een transactie:

val theLastJedi = StarWarsFilm.new {name = "The Last Jedi" sequelId = 8 director = "Rian Johnson"}

Merk op dat bewerkingen tegen de database traag worden uitgevoerd; ze worden alleen uitgegeven als de warme cache wordt gespoeld. Ter vergelijking: Hibernate noemt de warme cache een sessie.

Dit gebeurt automatisch wanneer dat nodig is; bijv. de eerste keer dat we de gegenereerde identifier lezen, voert Exposed stilletjes de insert-instructie uit:

assertEquals (1, theLastJedi.id.value) // Het lezen van de ID veroorzaakt een flush

Vergelijk dit gedrag met de invoegen methode uit sectie 7.1., die onmiddellijk een verklaring afgeeft tegen de database. Hier werken we op een hoger abstractieniveau.

8.3. Objecten bijwerken en verwijderen

Om een ​​rij bij te werken, wijzen we eenvoudigweg de eigenschappen ervan toe:

theLastJedi.name = "Aflevering VIII - The Last Jedi"

Terwijl we een object willen verwijderen, noemen we verwijderen ben ermee bezig:

theLastJedi.delete ()

Net als bij nieuw, de update en operaties worden lui uitgevoerd.

Updates en verwijderingen kunnen alleen worden uitgevoerd op een eerder geladen object. Er is geen API voor enorme updates en verwijderingen. In plaats daarvan moeten we de API op een lager niveau gebruiken die we in sectie 7 hebben gezien. Toch kunnen de twee API's samen in dezelfde transactie worden gebruikt.

8,4. Opvragen

Met de DAO API kunnen we drie soorten zoekopdrachten uitvoeren.

Om alle objecten zonder voorwaarden te laden, gebruiken we de statische methode alle:

val movies = StarWarsFilm.all ()

Om een ​​enkel object op ID te laden, bellen we findById:

val theLastJedi = StarWarsFilm.findById (1)

Als er geen object is met die ID, findById geeft terug nul.

Ten slotte gebruiken we in het algemene geval vind met een where-uitdrukking:

val movies = StarWarsFilm.find {StarWarsFilms.sequelId eq 8}

8.5. Veel-op-een-verenigingen

Net zoals joins een belangrijk kenmerk zijn van relationele databases, het in kaart brengen van joins naar referenties is een belangrijk aspect van een ORM. Laten we dus eens kijken wat Exposed te bieden heeft.

Stel dat we de beoordeling van elke film door gebruikers willen volgen. Ten eerste definiëren we twee extra tabellen:

object Gebruikers: IntIdTable () {val name = varchar ("name", 50)} object UserRatings: IntIdTable () {val value = long ("value") val film = reference ("film", StarWarsFilms) val user = reference ("gebruiker", gebruikers)}

Vervolgens zullen we de corresponderende entiteiten schrijven. Laten we de Gebruiker entiteit, die triviaal is, en direct naar de Gebruikersbeoordeling klasse:

class UserRating (id: EntityID): IntEntity (id) {begeleidend object: IntEntityClass (UserRatings) var waarde door UserRatings.value var film door StarWarsFilm gerefereerd Op UserRatings.film var gebruiker door Gebruiker referencedOn UserRatings.user}

Let in het bijzonder op de verwezen op infix-methode roept eigenschappen aan die associaties vertegenwoordigen. Het patroon is als volgt: a var verklaring, door de entiteit waarnaar wordt verwezen, verwezen op de verwijzingskolom.

Eigenschappen die op deze manier zijn gedeclareerd, gedragen zich als normale eigenschappen, maar hun waarde is het bijbehorende object:

val someUser = User.new {name = "Some User"} val rating = UserRating.new {waarde = 9 user = someUser film = theLastJedi} assertEquals (theLastJedi, rating.film)

8,6. Optionele verenigingen

De associaties die we in de vorige sectie hebben gezien, zijn verplicht, dat wil zeggen dat we altijd een waarde moeten specificeren.

Als we een optionele associatie willen, moeten we eerst de kolom als nul in de tabel declareren:

val user = reference ("gebruiker", gebruikers) .nullable ()

Dan gebruiken we optioneelReferencedOn in plaats van verwezen op in de entiteit:

var gebruiker door Gebruiker optionalReferencedOn UserRatings.user

Op die manier kan de gebruiker eigenschap zal nullabel zijn.

8.7. Een-op-veel verenigingen

Misschien willen we ook de andere kant van de vereniging in kaart brengen. Een rating gaat over een film, dat is wat we in de database modelleren met een foreign key; bijgevolg heeft een film een ​​aantal beoordelingen.

Om de beoordelingen van een film in kaart te brengen, voegen we eenvoudig een eigenschap toe aan de 'een'-kant van de associatie, dat wil zeggen de filmentiteit in ons voorbeeld:

class StarWarsFilm (id: EntityID): Entity (id) {// Andere eigenschappen hebben valbeoordelingen door UserRating referrersOn UserRatings.film} weggelaten

Het patroon is vergelijkbaar met dat van veel-op-een-relaties, maar het gebruikt referrersOn. De aldus gedefinieerde eigenschap is een Herhaalbaar, zodat we het kunnen doorkruisen voor elk:

theLastJedi.ratings.forEach {...}

Merk op dat we, in tegenstelling tot reguliere eigenschappen, hebben gedefinieerd waarderingen met val. Inderdaad, het eigendom is onveranderlijk, we kunnen het alleen lezen.

De waarde van de eigenschap heeft ook geen API voor mutatie. Dus om een ​​nieuwe beoordeling toe te voegen, moeten we deze maken met een verwijzing naar de film:

UserRating.new {waarde = 8 gebruiker = someUser film = theLastJedi}

Dan is de film waarderingen lijst zal de nieuw toegevoegde beoordeling bevatten.

8.8. Veel-op-veel verenigingen

In sommige gevallen hebben we misschien een veel-op-veel-associatie nodig. Laten we zeggen dat we een referentie willen toevoegen aan een Acteurs tafel naar de StarWarsFilm klasse:

object Actors: IntIdTable () {val firstname = varchar ("firstname", 50) val lastname = varchar ("lastname", 50)} class Actor (id: EntityID): IntEntity (id) {begeleidend object: IntEntityClass (Actors) var firstname door Actors.firstname var lastname door Actors.lastname}

Nadat we de tabel en de entiteit hebben gedefinieerd, hebben we een andere tabel nodig om de associatie weer te geven:

object StarWarsFilmActors: Table () {val starWarsFilm = reference ("starWarsFilm", StarWarsFilms) .primaryKey (0) val actor = reference ("actor", Acteurs) .primaryKey (1)}

De tabel heeft twee kolommen die beide externe sleutels zijn en die ook een samengestelde primaire sleutel vormen.

Ten slotte kunnen we de associatietafel verbinden met de StarWarsFilm entiteit:

class StarWarsFilm (id: EntityID): IntEntity (id) {begeleidend object: IntEntityClass (StarWarsFilms) // Andere eigenschappen verwijderd var-acteurs door Actor via StarWarsFilmActors}

Op het moment van schrijven is het niet mogelijk om een ​​entiteit met een gegenereerde ID te maken en deze op te nemen in een veel-op-veel-associatie in dezelfde transactie.

In feite moeten we meerdere transacties gebruiken:

// Maak eerst de film val film = transactie {StarWarsFilm.new {name = "The Last Jedi" sequelId = 8 director = "Rian Johnson" r}} // Maak vervolgens de acteur waarde actor = transactie {Actor.new {firstname = "Daisy" lastname = "Ridley"}} // Koppel tot slot de twee transactie {film.actors = SizedCollection (listOf (actor))}

Hier hebben we voor het gemak drie verschillende transacties gebruikt. Twee zouden echter voldoende zijn geweest.

9. Conclusie

In dit artikel hebben we een grondig overzicht gegeven van het Exposed-framework voor Kotlin. Zie de Exposed-wiki voor meer informatie en voorbeelden.

De implementatie van al deze voorbeelden en codefragmenten is te vinden in het GitHub-project.


$config[zx-auto] not found$config[zx-overlay] not found