Tehtävät
Ensimmäisen osan oppimistavoitteet

Tuntee käsitteet digitalisaatio, tietokannanhallintajärjestelmä ja tietokanta. Ymmärtää tietokantojen roolin digitalisaatiossa. Tuntee ongelmia, joita tietokannanhallintajärjestelmät pyrkivät ratkaisemaan. Ymmärtää että tietoa voidaan käsitellä ja esittää erilaisilla abstraktiotasoilla ja tuntee termeinä käsitteellisen abstraktiotason, rakenteellisen abstraktiotason sekä fyysisen abstraktiotason. Osaa luoda luokkakaavion annetun ohjelman lähdekoodista ja osaa luoda annetussa luokkakaaviossa kuvatut luokat. Osaa käsitellä tietoa ohjelmallisesti. Tuntee käsiteanalyysin askeleet. Osaa luoda ongelma-alueen kuvauksesta luokkakaavion.

Kurssin alkukysely

Kurssiin kuuluu alkukysely. Käy vastaamassa kyselyyn alla olevassa osoitteessa.

Johdanto

Olemme tietoisesti ja tiedostamattomasti kytköksissä lukemattomiin järjestelmiin. Kodin sähkön- ja vedenkulutusta seurataan elektronisesti, lehti- ja palvelutilaukset tehdään digitaalisiin järjestelmiin, säätiedot tulevat automaattisesti mobiililaitteeseen, suurin osa perinteisestä kirjeillä tehdystä kommunikaatiosta tapahtuu sähköpostitse tai pikaviestinten kautta, kaupat seuraavat varastosaldojen kehitystä automaattisesti ostosten perusteella, sairaaloilla on sähköiset potilasrekisterit, yritykset tarjoavat räätälöityjä palveluita digitaalisen käyttäytymisen perusteella, ja niin edelleen. Toisaalta, vain muutamia vuosikymmeniä sitten, internet oli vain harvojen hupi, digitaaliset palvelut olivat harvinaisia, ja lähes kenelläkään ei esimerkiksi ollut henkilökohtaista kännykkää. Pankkikortit, joita kännyköillä tai muilla laitteilla tapahtuvat maksut ovat hiljalleen korvaamassa, ovat nekin olleet käytössä vain muutamia vuosikymmeniä.

Lähes jokainen edellämainituista palveluista perustuu tavalla tai toisella tiedon keräämiseen. Sähkön- ja vedenkulutuksesta jää historia, jota käytetään laskutuksessa sekä kulutuksen ennustamisessa. Lehti- ja palvelitilaukset tallennetaan järjestelmiin, joiden kautta voidaan suositella vastaavia sopivia tuotteita. Sähköpostit ja pikaviestinviestit säilyvät tyypillisesti ainakin lähettäjällä ja vastaanottajalla, jonka lisäksi esimerkiksi pikaviestinpalveluita tarjoava operaattori voi tallentaa viestit omalle palvelimelleen mahdollista tulevaa käyttöä varten. Kauppojen varastosaldojen kehityksen perusteella voidaan optimoida sisäänostoa ja tätä kautta pienentää hävikkiä sekä toisaalta vähentää tavaroiden varastointiin menevää tilaa. Potilasrekisterit sisältävät mm. hoito- ja rokotehistorian, jolloin lääkärin on helpompi toimia yllättävissä tilanteissa.

Uusia palveluita kehitetään myös jatkuvasti. Alla olevalla videolla on kuvattuna yhdysvaltalaisen ruokajätin Tescon toimintaa Etelä-Koreassa. Tesco muutti ruokatoimijoiden pelikenttää luomalla digitaalisia ostosmahdollisuuksia mm. kaupunkien metroasemille. Tuotteiden tilaaminen ja maksaminen tapahtuu kännykällä, ja tuotteet toimitetaan tilaajan kotiin.

 

 

Tässä palveluiden siirtymisessä ja kehittymisessä sähköiseen muotoon on kyse digitalisaatiosta. Digitalisaatio on tietoteknisten menetelmien ja sähköisessä muodossa olevan tiedon hyödyntämistä ja kehittämistä yksilöiden, yhteisöjen, yritysten ja yhteiskunnan toiminnan edesauttamiseksi. Tämä sisältää mm. tiedon perusteella tapahtuvaa liiketoimintamallien ja asiakaspalvelukokemusten kehittämistä, työn automatisointia ja virtaviivaistamista sekä uusien innovaatioiden luomista ja yrityksen toiminnan parantamista. Digitalisaation ytimessä on kyky hallinnoida ja käsitellä suuria tietomääriä.

Vaikka moni palvelu kerää tietoa palveluiden käyttäjistä ja siirtää palveluitaan sähköiseen muotoon, ei käyttäjien tietojen kerääminen ole itseisarvo. Arvoa voi tuottaa käyttäjille sekä yritykselle myös muilla tavoilla. Alla olevalla videolla on esimerkki eräästä IKEAn tuotteesta: IKEA on digitoinut (eli siirtänyt sähköiseen muotoon) tuotteiden tietoja sekä tehnyt tuotteistaan kolmiulotteisia malleja, jolloin niiden tuonti osaksi sovelluksia on suoraviivaista. Alla kuvattu tuote tarjoaa käyttäjille mahdollisuuden tarkastella miltä IKEAn tuote näyttäisi kotona.

 

 

Tällä kurssilla keskitytään suurien tietomäärien tallentamiseen keskittyneisiin palveluihin, joilla on paljon yhtäaikaisia käyttäjiä. Keskiössä ovat erityisesti relaatiotietokannat. Puhekielessä termillä tietokanta tarkoitetaan yleisesti ottaen tiedon tallentamiseen tarkoitettua paikkaa, josta tietoa voi myös hakea. Esimerkiksi kirkonkirjat voidaan nähdä eräänlaisena sukujen historiaa dokumentoivana tietokantana, jonka kautta sukututkija pääsee käsiksi sukunsa historiaan. Vaikka tietokannat ovat digitalisaation myötä siirtymässä paperisesta muodosta sähköiseen muotoon, on niiden tavoite pysynyt pitkälti samana: haluamme säilöä tietoa ja haluamme päästä tähän tietoon käsiksi.

Tietokannat ovat kaikkialla oleva (ubiikki) ilmiö. Tämä kurssimateriaali sijaitsee tietokannassa, kurssitehtäviin liittyvät pisteet kirjataan tietokantaan ja kurssin suoritusmerkintä kirjataan tietokantaan. Kännykässäsi on todennäköisesti kymmeniä erilaisia tietokantoja; yhteystiedot, kalenteri, herätyskello, aikavyöhykkeet, karttapalvelut, suosikkiverkkosivut, ym, joiden lisäksi moni kännykkäsovellus hyödyntää yhtä tai useampaa tietokantaa. Tietokannat voivat olla paikallisia, eli ne voivat sijaita samalla koneella tietokantaa käyttävän ohjelmiston kanssa, esimerkiksi kännykässä, tai ne voivat sijaita erillisellä palvelimella, johon otetaan tarvittaessa yhteyttä. Loppukäyttäjän näkökulmasta tietokannan konkreettisella sijainnilla ei ole juurikaan merkitystä, sillä haetun tiedon näkee tyypillisesti käytössä olevan sovelluksen käyttöliittymän kautta.

Tutustu tietokantaan!

Tässä kohtaa on hyvä hetki käydä tutustumassa muutamaan tietokantapalveluun. Osoitteessa http://hiski.genealogia.fi/hiski/ on Suomen Sukututkimusseuran ylläpitämä Historiakirjojen hakupalvelu. Käy sivulle, valitse kieli, etsi "Kaikista", ja valitse "Kastetut".

Minkälaisia tuloksia löydät omalla etunimelläsi? Entä, minkälaisia tuloksia löydät nimillä Matti ja Maija? Palvelu pyrkii muunmuassa sisällyttämään läheiset nimien muunnokset hakutuloksiin, sillä nimet muuttuvat ajan myötä.

Tiedon tallentamiseen ja hakemiseen liittyviä haasteita

Tiedon tallentamisessa sekä tiedon hakemisessa on muutamia ydinkysymyksiä ja ongelmakohtia. Nämä ovat seuraavia.

  • Tietoturva. Keillä on pääsy tietoon? Onko käyttäjillä erilaisia oikeuksia ja onko tiedon kirjoittaminen rajattu vain tietyille käyttäjäryhmille? Minne tieto tallennetaan fyysisesti -- onko sijainti Suomessa vai jossain muualla? Miten yhteys tietokantaan suojataan? ...
  • Suorituskyky. Miten tiedon hakeminen toteutetaan tehokkaasti? Entä jos käyttäjiä on samanaikaisesti satoja tai tuhansia?
  • Eheys. Miten säilyttää tiedon eheys, eli miten varmistaa, että tallennettu tietoa noudattaa (joitakin) annettuja sääntöjä? Miten varmistetaan, että tietyllä arvoalueella olevat arvot (esim. syntymävuosi) tallennetaan ja luetaan numerona? Miten kytköksissä olevaa tietoa tulee käsitellä -- jos henkilön äiti poistetaan tietokannasta, tuleeko myös henkilö poistaa? Miten varmistaa, että käyttäjä ei saa koskaan "vaillinaista" tietoa (esim. tiedon hakeminen kesken tiedon poistamisen)?
  • Pysyvyys. Miten tiedon tallentaminen toteutetaan siten, että järjestelmän toimintavirheet (esim. sähkökatkos) eivät johda tiedon katoamiseen?

Nykyaikaiset tietokannanhallintajärjestelmät tarjoavat ratkaisuja edellisiin ongelmiin.

Tietokannanhallintajärjestelmä

Tietokannahallintajärjestelmä on sovellus, jonka kautta käyttäjä voi luoda ja ylläpitää tietokantoja. Tietokannanhallintajärjestelmän vastuulla on tietokantaan kohdistuvien haku-, muokkaus- ja lisäystoimintojen lisäksi käyttöoikeuksien valvominen. Tietokannanhallintajärjestelmän vastuulla on myös tiedon eheyteen liittyvien sääntöjen noudattamisen valvonta. Tietokannassa voi olla esimerkiksi sääntö "Opiskelijan syntymävuoden tulee sisältää neljä numeroa", jolloin uusien opiskelijoiden lisääminen ilman oikein määriteltyä syntymävuotta ei onnistu. Vastaavia sääntöjä voidaan lisätä muunmuassa varausjärjestelmiin, esimerkiksi lentokoneiden paikkavarausjärjestelmissä halutaan varmistaa, että jokaisella istuimella on korkeintaan yksi varaus. Tietokannanhallintajärjestelmän vastuulla on myös varmistaa, ettei tietoa tuhoudu, vaikka tietokantaa käyttävä järjestelmä hajoaisi -- erilaiset varmuuskopiotoiminnallisuudet ovat tyypillisiä.

Edellisten lisäksi tietokannanhallintajärjestelmät tarjoavat välineitä tiedon hakemiseen liittyvien toimintojen tehokkuuden tarkastelemiseen. Vaikka esimerkiksi opintojen seurantaan liittyvä järjestelmä sisältäisi tiedot kaikista Helsingin yliopiston opiskelijoista (n. 35000) sekä kaikista kurssisuorituksista (rutkasti), tulisi tietokantaan tehtävien kyselyjen toimia nopeasti. Edellä mainittu tietomäärä on esimerkiksi Amazon-verkkokaupan mittakaavassa hyvin pieni.

Yksittäinen sovellus voi myös käyttää useampaa tietokantaa, jotka sijaitsevat eri tietokannanhallintajärjestelmissä. Tyypillinen esimerkki tällaisesta sovelluksesta on analytiikkapalvelu, joka yhdistää eri palveluiden tallentamaa tietoa yhteenvetoraporttien luomiseksi. Yksittäisessä tietokannanhallintajärjestelmässä voi toisaalta sijaita useampia erilaisiin sovelluksiin ja käyttötarkoituksiin liittyviä tietokantoja, joita jokaista käyttää eri käyttäjät tai eri yritys.

Tietokanta

Tietokanta on kokoelma tiettyyn aihepiiriin liittyviä säilytettäviä tietoja. Tietokannan luominen liittyy jonkinlaisen organisaation, yrityksen tai muun yhteisön tarpeeseen säilöä ja hakea tietoa. Esimerkiksi, yliopisto haluaa pitää kirjaa opiskelijoistaan ja heidän opintomenestystään, hotelli haluaa pitää kirjaa hotellin huoneiden varauksista ja kauppaketju haluaa pitää kirjaa asiakkaistaan ja asiakkaiden ostoksista.

Tallennettava tieto liittyy tyypillisesti johonkin tavoitteeseen. Yliopisto haluaa seurata opintojen etenemistä muunmuassa valtionhallinnolle raportointia varten, huoneiden varaustilannetta seuraava hotelli haluaa tietää milloin huoneita on paljon tarjolla ja milloin huoneet ovat lopussa. Kauppaketjun ensisijaisena tavoitteena lienee asiakkaiden ostosten seuranta myynnin optimoimiseksi.

Tietokantojen rakennetta ja jäsentelyä suunniteltaessa ongelmaa lähestytään tunnistamalla ongelma-alueen oleelliset käsitteet. Käsitteitä tarkastelemalla tunnistetaan mikä osa tiedosta on epäoleellista ja mikä osa tulee säilöä. Käsitteiden tunnistamisen yhteydessä selvitetään käsitteiden ominaisuuksia sekä niiden yhteyksiä. Esimerkiksi opiskelijan opintomenestyksen seurannassa oleellisia ovat ainakin käsitteet Opiskelija ja Kurssisuoritus, joilla on yhteys: opiskelijalla on kurssisuorituksia.

Tiedon käsittelyn abstraktiot

Tietokannanhallintajärjestelmistä ja tallennettavasta tiedosta puhuttaessa tietoa käsitellään eri abstraktiotasoilla. Tällä kurssilla esiintyvät käsitteellinen abstraktiotaso, rakenteellinen abstraktiotaso ja fyysinen abstraktiotaso.

Käsitteellinen abstraktiotaso

Käsitteellinen abstraktiotaso (conceptual level) on kuvaus ongelma-alueesta ihmisten ymmärtämässä muodossa. Kuvaus voi esiintyä esimerkiksi tekstidokumentissa, puheessa, tai vaikkapa multimediaesityksessä. Käsitteellisen abstraktiotason kuvauksen luomisen ja välittämisen esiehtona on oletus siitä, että kuvauksen käsitteisiin liittyy jonkinlainen ihmisen mielessä oleva ymmärrys (mentaalimalli).

Käsite on ihmisen mielessä oleva idea jostakin abstraktista tai konkreettisesta asiasta. Käsitteelliseen kuvaukseen liittyy tieto siitä, miten asiat toimivat ja miten asiat liittyvät toisiinsa, mutta tämä tieto ei aina esiinny konkreettisesti "nähtävänä".

Tarkastellaan konkreettisena esimerkkinä käsitteellisen abstraktiotason kuvauksesta seuraavaa pankin tallennustoiminnallisuuteen liittyvää lausetta: "Tileillä on omistajia, joista jokainen yksilöidään henkilöturvatunnuksen avulla".

Kuvaus sisältää käsitteet tili, omistaja ja henkilöturvatunnus. Tämän lisäksi käsitteisiin liittyy suhteita, jotka osittain ilmenevät kuvaksesta ja osittain vaativat aihealueen ymmärrystä. Esimerkiksi tili ja omistaja ovat erillisiä käsitteitä ja niiden välinen yhteys kuvaa omistajuutta. Omistajan ja henkilöturvatunnuksen välinen yhteys on taas riippuvainen olemassaolosta -- Omistajan henkilöturvatunnusta ei ole olemassa ilman omistajaa. Omistajalla on henkilöturvatunnus.

Tiedon käsittelyyn liittyy oleellisesti myös synonyymien tunnistaminen. Esimerkiksi käsite "omistaja" voidaan ajatella myös käsitteenä "henkilö", mikä voi tehdä kuvauksesta ymmärrettävämmän.

Käsitteellinen abstraktiotaso ei ota kantaa siihen, miten tietoa tulee kuvata tai miten tieto konkreettisesti tallennetaan.

Rakenteellinen abstraktiotaso

Rakenteellinen abstraktiotaso (logical level, structural level) on jotain sovittua loogista määritelmää noudattava kuvaus säilöttävän tiedon rakenteesta. Rakenteellisen abstraktiotason kuvaus on kaikkien käytettyä kuvaustyyppiä ymmärtävien henkilöiden ymmärrettävissä, mikä mahdollistaa muunmuassa kuvauksesta keskustelun.

Rakenteellisen abstraktiotason kuvaus voi olla esimerkiksi taulukkolaskentaohjelmaan tehty kuvaus käsitteistä ja niiden ominaisuuksista, luokkakaavio, tietokantakaavio, tai SQL-kielellä tehdyt tietokantataulujen luomislauseet.

Edeltävän esimerkin perusteella voisi taulukkolaskennassa puhua kahdesta välilehdestä: välilehti tili ja välilehti henkilö. Henkilön kuvaamiseen käytetty välilehti sisältäisi ainakin sarakkeen henkilötunnus, ja jokainen rivi vastaisi yhtä henkilöä. Vastaavasti välilehti tili sisältäisi tilin tietoja kuvaavat sarakkeet sekä tilikohtaisen henkilötunnuksen, mikä kuvaisi tilin omistajaa. Jokaista tiliä kohden taulukkolaskennassa olisi yksi rivi.

Rakenteellinen abstraktiotaso ei ota kantaa siihen, miten tieto tulee konkreettisesti tallentaa. Rakenteellisen abstraktiotason kuvauksesta on myös mahdollista tehdä useita käsitteellisen abstraktiotason kuvauksia.

Fyysinen abstraktiotaso

Fyysinen abstraktiotaso (physical level, internal level) kuvaa konkreettista tiedon tallentamistapaa esimerkiksi kiintolevylle. Tämä sisältää tiedon tallennettavan tiedon formaatista tai muodosta, tietokantaa kuvaavan tai kuvaavien tiedostojen sijainnista, käytettävistä tietorakenteista, tiedon varmuuskopioinnista, hajauttamisesta, ja niin edelleen. Fyysinen abstraktiotaso on tyypillisesti järjestelmäkohtainen ja riippuu myös ainakin osittain tallennettavan tiedon muodosta.

Abstraktio

Käsite abstraktio esiintyi edellä useaan kertaan. Käsitteellä abstraktio tarkoitetaan tässä yksityiskohtien piilottamista mikä mahdollistaa kokonaisuuksien hallinnan. Jos tietokantoja käsiteltäisiin vain fyysisellä abstraktiotasolla, olisi tallennettavasta tiedosta keskustelu hyvin vaikeaa tai jopa mahdotonta tietokoneiden sisäisiä rakenteita heikommin ymmärtävien kanssa.

Tämän kurssin puitteissa tietokantoja suunniteltaessa lähdetään käsitteellisen abstraktiotason kuvauksesta ja pyritään rakenteellisen abstraktiotason kuvaukseen.

Tiedon kuvaamisesta UML-kielellä

Käytämme tällä kurssilla ensisijaisesti UML-kieltä rakenteellisen abstraktiotason kuvauskielenä. Käytämme luokkakaavioita käsitteiden ominaisuuksien ja yhteyksien mallintamiseen ja sekvenssikaavioita järjestelmien välisen kommunikoinnin mallintamiseen. Koko UML-spesifikaatio ei kuitenkaan ole tässä oleellinen -- luokkakaavioistakin esimerkiksi kooste- ja kompositiomerkintä ei ole tällä kurssilla tarpeen.

Luokkakaavio, jossa on luokat Opiskelija ja Kurssisuoritus. Opiskelijalla on monta (nollasta äärettömään kurssisuoritusta). Jokaiseen kurssisuoritukseen liittyy yksi opiskelija.

Luokkien kuvaaminen luokkakaaviossa

Luokkakaavion luominen lähtee luokan (käsitteen) määrittelystä. Luokkakaaviossa luokka piirretään laatikkona, jonka sisällä on luokan nimi. Alla on kuvattu luokka Opiskelija.

[Opiskelija]
Luokka Opiskelija luokkakaaviossa. Yllä oleva luokkakaavio ei sisällä muita luokkia, eikä opiskelijalla ole attribuutteja.

Luokan määrittelyn jälkeen luokalle voidaan lisätä attribuutteja. Attribuutit lisätään luokkakaavioon luokan nimen alle. Alla olevassa esimerkissä jokaiseen Opiskelijaan liittyy opiskelijanumero, nimi ja syntymävuosi.

[Opiskelija|opiskelijanumero;nimi;syntymavuosi]
Luokka Opiskelija luokkakaaviossa. Opiskelijalle on määritelty attribuutit opiskelijanumero, nimi ja syntymävuosi.

Luokkakaavioon voidaan merkitä myös muuttujien tyypit. Alla olevassa esimerkissä yllä olevaa luokkaa on muokattu siten, että opiskelijan attribuutteihin on lisätty myös niiden tyypit. Opiskelijanumero ja nimi ovat merkkijonoja, syntymävuosi on kokonaisluku. Attribuutin tyyppi merkitään attribuutin nimen jälkeen kaksoispisteellä erotettuna, esimerkiksi nimi:String.

[Opiskelija|opiskelijanumero:String;nimi:String;syntymavuosi:int]
Luokka Opiskelija luokkakaaviossa. Opiskelijalle on määritelty attribuutit opiskelijanumero, nimi ja syntymävuosi. Attribuuteille on määritelty myös niiden tyypit.

Lisätään luokkakaavioon toinen luokka. Lisätään luokkakaavioon käsite Harrastus.

[Opiskelija|opiskelijanumero:String;nimi:String;syntymavuosi:int]
						  [Harrastus]
Luokkakaavio, joka sisältää luokat Opiskelija ja Harrastus. Opiskelijalle on määritelty attribuutit.

Lisätään harrastukselle seuraavaksi nimi-attribuutti. Sovitaan, että nimi on merkkijono.

[Opiskelija|opiskelijanumero:String;nimi:String;syntymavuosi:int]
						       [Harrastus|nimi:String]
Luokkakaavio, joka sisältää luokat Opiskelija ja Harrastus. Opiskelijalle ja harrastukselle on määritelty attribuutit.

Luokkakaaviossa on nyt sekä opiskelija että harrastus. Näillä ei ole tällä hetkellä kuitenkaan minkäänlaista yhteyttä. Tutustutaan seuraavaksi yhteyksien merkintään luokkakaavioon.

Yhteydet luokkakaaviossa

Tutustutaan seuraavaksi yhteyksien luomiseen luokkakaaviossa. Yhteydet voidaan karkeasti ottaen jakaa kolmeen kategoriaan: monen suhde moneen, yhden suhde moneen ja yhden suhde yhteen.

Monen suhde moneen

Kahden käsitteen välillä on monen suhde moneen (N-N) -yhteys, jos ensimmäisen käsitteen ilmentymään voi liittyä monta toisen käsitteen ilmentymää, ja toisen käsitteen ilmentymään voi liittyä monta ensimmäisen käsitteen ilmentymää.

Tällainen suhde on esimerkiksi annoksen ja raaka-aineen välillä. Käytännössä yksittäinen annos -- esimerkiksi Poronkäristys -- voi sisältää montaa eri raaka-ainetta kuten perunaa, puolukkaa ja poroa. Toisaalta, yksittäinen raaka-aine kuten peruna voi sisältyä moneen eri annokseen.

[Annos|nimi:String;koko:String;hinta:double]
						      [RaakaAine|nimi:String]
						      [Annos]*-*[RaakaAine]
Monen suhde moneen. Yllä annokseen voi liittyä montaa eri raaka-ainetta, ja yksi raaka-aine voi esiintyä useammassa eri annoksessa. Monen suhde moneen merkitään luokkakaavioon piirrettyyn viivaan kahdella tähdellä, missä viivan kummassakin päässä on tähti.

Yhden suhde moneen

Kahden käsitteen välillä on yhden suhde moneen (1-N) -yhteys, jos ensimmäisen käsitteen ilmentymään voi liittyä monta toisen käsitteen ilmentymää, mutta yhteen toisen käsitteen ilmentymään voi liittyä vain yksi ensimmäisen käsitteen ilmentymä.

Tällainen suhde on esimerkiksi asiakkaan ja tilauksen välillä. Käytännössä yksittäinen asiakas -- esimerkiksi Anna Asiakas -- voi tehdä monta tilausta, mutta jokainen tehty tilaus liittyy täsmälleen yhteen asiakkaaseen. Sama tilaus ei voi liittyä samaan aikaan Anna Asiakkaaseen sekä Essi Esimerkkiin.

[Asiakas|nimi:String;puhelinnumero:String;katuosoite:String;postinumero:Integer;postitoimipaikka:String]
						    [Tilaus|aika:Date;kuljetustapa:String;vastaanotettu:Boolean;toimitettu:Boolean]
						    [Asiakas]1-*[Tilaus]
Yhden suhde moneen. Yllä yhteen asiakkaaseen voi liittyä monta tilausta, mutta yksi tilaus liittyy aina täsmälleen yhteen asiakkaaseen. Yhden suhde moneen merkitään luokkakaavioon piirrettyyn viivaan tähdellä ja numerolla 1. Tähti tulee yhteyden siihen päähän, joita voi olla monta, ja ykkönen siihen päähän, joita voi olla vain yksi.

Yhdestä moneen yhteystyyppi edellyttää sen käsitteen ilmentymän olemassaoloa, johon liittyy monta toisen käsitteen ilmentymää. Käytännössä tilauksen luominen edellyttää tilaukseen liittyvän asiakkaan olemassaoloa. Toisin sanoen, tilausta ei voi luoda ilman, että sille olisi asiakas.

Yhden suhde yhteen

Kahden käsitteen välillä on yhden suhde yhteen (1-1) -yhteys, jos ensimmäisen käsitteen ilmentymään voi liittyä vain yksi toisen käsitteen ilmentymä, ja toisen käsitteen ilmentymään voi liittyä vain yksi ensimmäisen käsitteen ilmentymä.

Eräs esimerkki tällaisesta yhteystyypistä voisi olla kuljettajien ja kuljetusvälineiden välinen -- ehkäpä hieman teennäinen -- yhteys. Yhdellä kuljettajalla voi olla käytössään vain yksi kuljetusväline, ja yksi kuljetusväline voi olla vain yhden kuljettajan käytössä. Vastaavan esimerkin voisi rakentaa myös lainaesineiden kautta -- esimerkiksi yhdellä opiskelijalla voi olla vain yksi laite lainassa ylläpidolta, ja ylläpito voi lainata tietyn laitteen vain yhdelle opiskelijalle.

Käsiteanalyysi

Käsiteanalyysia (conceptual modeling, domain modeling) käytetään ongelma-alueen käsitteellistämiseen ja kielentämiseen, mikä edesauttaa ongelma-alueeseen liittyvää keskustelua sekä päätöksentekoa. Käsiteanalyysi tehdään iteratiivisesti esimerkiksi ongelma-alueen tekstuaalista kuvausta läpikäyden.

Käsiteanalyysin tuloksena saadaan aikaan ongelma-aluetta kuvaava tietomalli, joka sisältää ongelma-alueen käsitteet ja niiden yhteydet sekä käsitteisiin liittyvät attribuutit selkeästi ja kuvaavasti nimettyinä. Käsiteanalyysin lopputuloksessa ei ole sellaisia käsitteitä tai attribuutteja, jotka ovat ongelma-aluetta varten rakennettavan järjestelmän tai ratkaisun kannalta epäoleellisia.

Käsiteanalyysin lopputulos voi olla esimerkiksi luokkakaavio tai ER-kaavio. Tällä kurssilla käsiteanalyysin lopputuloksena on luokkia, luokkien attribuutteja, sekä luokkien välisiä yhteyksiä, jotka kuvataan luokkakaaviossa.

Käsite ja käsitteiden väliset yhteydet

Arkielämässä näkee useita saman tyyppisiä esineitä. Esimerkiksi suurin osa älypuhelimista on karkeasti ottaen saman mallisia -- jokainen puhelin on rakennettu tiettyä mallia noudattaen. Olio-ohjelmoinnin termejä noudattaen voimme sanoa, että omistamasi puhelin on Puhelin-luokasta tehty ilmentymä eli olio. Luokka on rakennuspiirrustus, jonka perusteella yksittäiset oliot luodaan.

Käsitteet ovat samalla tavalla abstrakteja kuin luokat, eli käsitteestä voi olla useampia ilmentymiä. Käsitteistä luodut ilmentymät voidaan toisaalta myös erottaa toisistaan jollain tavalla, tai niille tulee olla vähintään mahdollista määritellä jonkinlainen yksilöivä tunnus.

Käsitteiden välisillä yhteyksillä tarkoitetaan käsitteiden välisiä suhteita. Esimerkiksi puhelimella voi olla omistaja, ja toisaalta puhelimen omistaja voi opiskella jossain opinahjossa.

Käsitteitä voidaan ajatella myös hetkellisen olemassaolon kautta. Hyvät käsitteet eivät tyypillisesti ole (pysyvästi) olemassaoloriippuvaisia. Esimerkiksi puhelimen olemassaolo ei ole riippuvainen omistajasta, ja puhelimen omistaja ei ole riippuvainen opinahjosta. Käsitteen olemassaolosta riippuvaiset asiat -- kuten esimerkiksi henkilön nimi -- ovat hyviä attribuuttiehdokkaita.

Käsiteanalyysin vaiheet

Käsiteanalyysi koostuu viidestä vaiheesta, jotka ovat seuraavat:

  1. Tunnista käsite-ehdokkaat. Käsite-ehdokkaat tunnistetaan etsimällä ongelma-alueen kuvauksesta oleellisia substantiiveja ja ilmiöitä. Tässä vaiheessa myös rajataan pois käsitteitä, jotka eivät ole oleellisia ongelma-alueen kannalta.
  2. Tunnista käsitteiden väliset yhteydet. Yhteydet tunnistetaan etsimällä ongelma-alueen kuvauksesta verbejä, käsitteiden yhteyksiä sekä käsitteitä kuvaavia lausahduksia.
  3. Tunnista ja määrittele osallistumisrajoitteet. Osallistumisrajoitteet tarkentavat lopputuloksena saatavaa tietomallia. Osallistumisrajoitteita saadaan selville ongelma-alueen kuvauksessa esiintyvien adjektiivien ja määreiden kautta.
  4. Tunnista attribuutit ja lisää ne käsitteille. Tunnista käsitteisiin liittyvät tiedot eli attribuutit, joita halutaan tallentaa tietokantaan. Käsitteisiin liittyvät attribuutit tunnistaa muunmuassa olemassaoloriippuvaisista substantiiveista sekä käsitteiden yleisistä ominaisuuksista. Attribuutti saattaa olla joko yksittäinen arvo tai arvojoukko -- arvojoukot tunnistetaan tyypillisesti lukumäärien kuvauksista.
  5. Yleistä ja eriytä käsitteitä. Tunnista käsitteistä yliluokkia ja aliluokkia. Näiden tunnistaminen tapahtuu esimerkiksi käsitteitä tarkastelemalla ja miettimällä "onko käsite toisen käsitteen erikoistapaus".

Sovelletaan käsiteanalyysin askeleita seuraavaan Uimaseuraesimerkkiin.

Tunnista käsite-ehdokaat

Käsite-ehdokkaita tunnistaessa laaditaan luettelo ongelma-alueen oleellisista tietokohteista. Luettelon laatiminen alkaa substantiivien tunnistamisesta. Ensimmäisessä vaiheessa oleelliset käsite-ehdokkaat alleviivataan.

Substantiiveja tarkastelemalla luotu lista on seuraavanlainen. Alla olevassa listassa käsite-ehdokkaat on muutettu yksikkömuotoon.

  • Uimaseura
  • Paperi
  • Uimari
  • Tulos
  • Valmennuspäällikkö
  • Kirjanpito
  • Tietokone
  • Seura
  • Miesuimari
  • Naisuimari
  • Selkäuinti
  • Laji
  • Kilpailu

Käsite-ehdokkaiden karsinta tapahtuu harkitsemalla jokaista ehdokasta erikseen ja miettimällä onko se oleellinen ongelma-alueen ratkaisun kannalta. Alla kuvattu eräs karsinta.

  • Uimaseura -- seuralle tehdään järjestelmää, voidaan jättää pois ainakin toistaiseksi.
  • Paperi -- tästä haluttiin päästä eroon, tulokset kirjattiin aiemmin paperille.
  • Uimari
  • Tulos
  • Valmennuspäällikkö -- valmennuspäällikkö haluaa uuden järjestelmän, mutta ei oleellinen käsite tietomallin kannalta.
  • Kirjanpito -- järjestelmä tulee sisältämään kirjanpidon, mutta kirjanpito ei käsite järjestelmässä.
  • Tietokone -- kts. edellinen
  • Seura -- kts. uimaseura.
  • Miesuimari -- Uimari on valittuna käsitteeksi, sukupuoli voi esim. olla uimarin attribuuttina.
  • Naisuimari -- kts. edellinen
  • Selkäuinti -- Laji on valittuna käsitteeksi.
  • Laji
  • Kilpailu

Ehdokkaiden karsinnan jälkeen seuraavat käsitteet ovat jäljellä:

  • Kilpailu
  • Laji
  • Uimari
  • Tulos

Tunnista käsitteiden väliset yhteydet

Yhteydet tunnistetaan etsimällä tekstistä verbejä, käsitteiden yhteyksiä sekä käsitteitä kuvaavia lausahduksia. Tämän lisäksi aiempi aihealueen tietämys on tässä hyödyksi.

Edellä tarkastellusta kuvauksesta nousee esille seuraavat tekstit:

  • Meillä on noin sata mies- ja naispuolista uimaria
  • Uimarit kilpailevat yleensä yhdessä lajissa
  • jotkut uimarit kilpailevat useammassakin lajissa
  • Tuloksia kirjataan sekä kuukausittain järjestettävistä seuran sisäisistä "kuukauden vesihiisi"-kisoista, että jokaisesta seuran ulkopuolella järjestettävästä kilpailusta

Teksteistä voidaan päätellä seuraavat yhteydet:

  • Meillä on noin sata mies- ja naispuolista uimaria: seuraan liittyy uimareita. Poistimme aiemmin käsitteen seura, joten tämä yhteys ei ole relevantti.
  • Uimarit kilpailevat yleensä yhdessä lajissa: uimariin liittyy laji, uimariin liittyy kilpailu, kilpailuun liittyy laji.
  • jotkut uimarit kilpailevat useammassakin lajissa: (sama kuin yllä).
  • Tuloksia kirjataan sekä kuukausittain järjestettävistä seuran sisäisistä "kuukauden vesihiisi"-kisoista, että jokaisesta seuran ulkopuolella järjestettävästä kilpailusta: tulos liittyy kilpailuun.

Aiempi tieto aihealueeseen liittyen antaa olettaa, että tulokseen liittyy kilpailun lisäksi myös laji ja uimari.

Esille nousee siis seuraavat yhteydet:

  • Uimariin liittyy laji
  • Uimariin liittyy kilpailu
  • Kilpailuun liittyy laji
  • Tulokseen liittyy kilpailu
  • Tulokseen liittyy laji
  • Tulokseen liittyy uimari
[Uimari]-[Laji]
						    [Laji]-[Kilpailu]
						    [Kilpailu]-[Uimari]
						    [Tulos]-[Uimari]
						    [Tulos]-[Laji]
						    [Tulos]-[Kilpailu]

 

Kun käsitteet on tunnistettu, hahmotellaan niiden välisiä yhteyksiä. Yllä on kuvattuna eräs mahdollisuus ongelma-alueen käsitteiden yhteyksiksi.

Tunnista ja määrittele osallistumisrajoitteet

Osallistumisrajoitteilla tarkoitetaan lukumäärällisiä rajoitteita käsitteiden välillä. Osallistumisrajoitteet merkitään luokkakaavioon käsitteitä yhdistävien viivojen päätyihin. Osallistumisrajoitteita saadaan selville ongelma-alueen kuvauksessa esiintyvien adjektiivien ja määreiden kautta, jonka lisäksi aihealueeseen liittyvä tietämyksestä on hyötyä.

Edellisessä askeleessa tunnistetuista yhteyksistä saadaan selville seuraavat tiedot: uimari voi osallistua yhteen tai useampaan lajiin, eli uimariin voi liittyä monta lajia. Toisaalta, yhtä lajia voi harrastaa useampi uimari. Kilpailussa voi olla monta lajia, ja lajia voidaan todennäköisesti uida monessa kilpailussa. Kilpailussa voi olla monta uimaria, ja uimari voi uida useammassa kilpailussa. Yksittäiseen tulokseen taas liittyy yksi uimari, yksi laji, ja yksi kilpailu -- mutta, yhteen uimariin voi liittyä monta tulosta, yhteen lajiin voi liittyä monta tulosta, ja yhteen kilpailuun voi liittyä monta tulosta.

Ehdotus käsitekaavioksi osallistumisrajoitteiden kanssa on seuraavanlainen:

[Uimari]*-*[Laji]
								 [Laji]*-*[Kilpailu]
								 [Kilpailu]*-*[Uimari]
								 [Tulos]*-1[Uimari]
								 [Tulos]*-1[Laji]
								 [Tulos]*-1[Kilpailu]

 

Kun käsitteiden väliset yhteydet on tunnistettu, lisätään yhteyksiin osallistumisrajoitteet. Yllä pohdittu erästä mahdollisuutta osallistumisrajoitteiksi.

Tunnista attribuutit ja lisää ne käsitteille

Käsitteisiin liittyvät attribuutit tunnistaa muunmuassa olemassaoloriippuvaisista substantiiveista sekä käsitteiden yleisistä ominaisuuksista. Attribuutti saattaa olla joko yksittäinen arvo tai arvojoukko -- arvojoukot tunnistetaan tyypillisesti lukumäärien kuvauksista. Samalla kuitenkin halutaan tallentaa vain ne attribuutit (ja käsitteet), jotka ovat ongelma-alueen kannalta oleellisua.

Ongelma-alueen kuvauksesta tiedämme, että kilpailuilla on paikka (esim. "paikalliset kilpailut", "seuran ulkopuoliset kilpailut") ja nimi (esim. "kuukauden vesihiisi"), jonka lisäksi nimestä voi päätellä, että kilpailuun liittyy aika. Vastaavasti uimareihin liittyy todennäköisesti nimi ja syntymäaika, vaikkei kuvauksessa kumpaakaan suoraan pyydetä. Nimen perusteella on helppo tarkastaa kenestä on kyse, ja syntymäaika auttaa seuraamaan tuloskehitystä. Tulokseen tarvitaan jonkinlainen tieto tuloksesta -- uimakisoissa kyseessä on tarkka aika, ja laji kerrotaan tässä nimenä.

[Uimari|nimi:String;syntymäaika:Date]
										   [Laji|nimi:String]
										   [Kilpailu|nimi:String;paikka:String;aika:Date]
										   [Tulos|millisekuntia:Double]
										   [Uimari]*-*[Laji]
										   [Laji]*-*[Kilpailu]
										   [Kilpailu]*-*[Uimari]
										   [Tulos]*-1[Uimari]
										   [Tulos]*-1[Laji]
										   [Tulos]*-1[Kilpailu]

 

Kun käsitteiden väliset yhteydet ja osallistumisrajoitteet on tunnistettu, lisätään käsitteille attribuutit.

Yleistä ja eriytä käsitteitä

Tunnista käsitteistä yliluokkia ja aliluokkia. Näiden tunnistaminen tapahtuu esimerkiksi käsitteitä tarkastelemalla ja miettimällä "onko käsite toisen käsitteen erikoistapaus". Vastaavasti toistuvat attribuutit saattavat antaa ilmi yli- ja aliluokkia.

Yli- ja aliluokkien etsintä kannattaa tehdä matriisina, missä jokaista käsitettä verrataan jokaiseen muuhun käsitteeseen. Käymällä läpi käsitteemme, huomaamme, ettei niissä ole yli- tai aliluokille tarvetta.

- Kilpailu Laji Tulos Uimari
Kilpailu - Kilpailu ei ole lajin erikoistapaus. Kilpailu ei ole tuloksen erikoistapaus. Kilpailu ei ole uimarin erikoistapaus.
Laji Laji ei ole kilpailun erikoistapaus. - Laji ei ole tuloksen erikoistapaus. Laji ei ole uimarin erikoistapaus.
Tulos Tulos ei ole kilpailun erikoistapaus. Tulos ei ole lajin erikoistapaus. - Tulos ei ole uimarin erikoistapaus.
Uimari Uimari ei ole kilpailun erikoistapaus. Uimari ei ole lajin erikoistapaus. Uimari ei ole tuloksen erikoistapaus. -

Luokkakaaviota ei siis tarvitse tässä tapauksessa muuttaa.

Tarkastellaan vielä tilannetta, missä luokkakaaviosta löytyy tapaus, missä toinen käsite on toisen käsitteen erikoistapaus. Oletetaan, että käytössämme ovat käsitteet Henkilö ja Opiskelija. Henkilöllä on nimi, syntymäaika ja sähköpostiosoite. Opiskelijalla on nimi, syntymäaika, sähköpostiosoite ja opiskelijanumero.

[Henkilo|nimi:String;syntymäaika:Date;email:String]
							 [Opiskelija|nimi:String;syntymäaika:Date;email:String;opiskelijanumero:String]

 

Henkilö ja opiskelija luokkakaaviossa.

Huomaamme, että opiskelija on henkilön erikoistapaus. Opiskelijalla on muuten samat ominaisuudet kuin henkilöllä, mutta sillä on lisäksi opiskelijanumero. Voimme luoda tämän perusteella luokkakaavion, missä opiskelija perii henkilön. Tämä merkitään seuraavasti.

[Henkilo|nimi:String;syntymäaika:Date;email:String]
								 [Opiskelija|opiskelijanumero:String]
								 [Henkilo]^-[Opiskelija]

 

Henkilö ja opiskelija luokkakaaviossa. Opiskelija perii henkilön, eli opiskelijalla on kaikki henkilön attribuutit, jonka lisäksi opiskelijalla on myös omat attribuuttinsa.

Johdatus tiedon käsittelyyn Java-ohjelmoijalle

Java-ohjelmoijalle tietokantaan tallennetuilla käsitteillä ja niiden yhteyksillä on suorat vertauskuvat. Käsitteet ovat luokkia -- käsitteisiin liittyy ominaisuuksia eli attribuutteja (tai oliomuuttujia) -- ja käsitteiden yhteydet ovat viitteitä luokkien välillä.

Luokkakaavio Java-luokkina

Tarkastellaan seuraavaa Opiskelijaa ja Kurssisuoritusta sekä näiden välistä yhteyttä kuvaavaa luokkakaaviota.

Luokkakaavio, jossa on luokat Opiskelija ja Kurssisuoritus. Opiskelijalla on monta (nollasta äärettömään kurssisuoritusta). Jokaiseen kurssisuoritukseen liittyy yksi opiskelija.

Määritellään ensin luokat Opiskelija ja Kurssisuoritus siten, että niillä ei ole vielä yhteyksiä merkittynä.

public class Opiskelija {
    String opiskelijanumero;
    String nimi;
    int syntymavuosi;
}
public class Kurssisuoritus {
    String kurssi;
}

Lisätään seuraavaksi luokkien välille yhteys. Jokaiseen kurssisuoritukseen liittyy tasan yksi Opiskelija (kurssisuorituksen ja opiskelijan välisessä viivassa opiskelijan päädyssä on numero yksi). Lisätään siis luokkaan Kurssisuriotus viite Opiskelijaan.

public class Kurssisuoritus {
    Opiskelija opiskelija;
    String kurssi;
}

Yllä kuvattu luokka Kurssisuoritus määrittelee Kurssisuorituksen, jolla on kurssin nimi. Kurssisuoritukseen liittyy yksi opiskelija.

Lisätään seuraavaksi yhteys opiskelijasta kurssisuoritukseen. Jokaiseen opiskelijaan voi liittyä nollasta äärettömään kurssisuoritusta (kurssisuorituksen ja opiskelijan välisessä viivassa kurssisuorituksen päädyssä on tähti). Lisätään siis luokkaan Opiskelija äärettömän määrän kurssisuorituksia mahdollistava viite eli lista.

import java.util.List;

public class Opiskelija {
    List<Kurssisuoritus> kurssisuoritukset;
    String opiskelijanumero;
    String nimi;
    int syntymavuosi;
}

Oleellista luokkakaavioiden ja lähdekoodin välisessä muunnoksessa on se, että luokkien väliset yhteydet eivät näy luokkakaaviossa, mutta luokat sisältävät yhteyksiä kuvaavat oliomuuttujat. Esimerkiksi yllä olevassa lähdekoodissa Kurssisuoritus sisältää tiedon opiskelijasta, mutta luokkakaaviossa opiskelija ei ole kurssisuorituksen oliomuuttuja.

Huom! Päivitä TMC ennen tehtävien tekemisen aloittamista. TMC:n päivitys onnistuu valitsemalla TMC:ssä Help -> Check for Updates.

Alla on kuvattuna erään kirjojen lainausjärjestelmän luokkakaavio. Luo tehtäväpohjaan luokkakaavion esittämät luokat ja lisää luokkiin tarvittavat oliomuuttujat.

[Kirja|nimi:String;kirjoittaja:String;julkaisuvuosi:Integer]
						 [Hylly|sijainti:String]
						 [Nide|tunnus:Integer]
						 [Henkilo|nimi:String
						 [Laina|alku:LocalDate;loppu:LocalDate;palautettu:Boolean]
						 [Nide]*-1[Hylly]
						 [Kirja]1-*[Nide]
						 [Laina]*-1[Henkilo]
						 [Nide]1-*[Laina]

Kun olet valmis, aja testit ja palauta tehtävä TMC:lle.

Tiedon tallentaminen ja lataaminen

Tiedon tallentaminen pysyväismuistiin tarkoittaa käytännössä tiedon tallentamista kiintolevylle tai vastaavalle medialle. Tietokoneen käyttöjärjestelmä tarjoaa kiintolevyn käsittelylle abstraktion tiedosto -- tiedosto on käytännössä yksi tai useampi rajattu alue, johon tietoa on kirjoitettu. Tiedon tallennusmuoto määräytyy tallennettavan tiedon perusteella ja vaikuttaa sekä tallennusoperaatioiden että lukuoperaatioiden toteuttamiseen. Suoraviivainen tapa tallentaa tietoa on säilöä jokaisen käsitteen (luokan) ilmentymät (oliot) käsitekohtaisiin tiedostoihin, missä rivi kuvaa aina yksittäistä käsitettä.

Opiskelija- ja Kurssisuoritusoliot voidaan tallentaa tiedostoihin. Käytetään tiedostoja opiskelija.data ja kurssisuoritus.data -- tässä .data on itse keksimämme pääte.

Opiskelijalistan tallentaminen tiedostoon onnistuu nyt esimerkiksi seuraavasti.

// oletetaan, että käytössä on lista opiskelijoita
// List<Opiskelija> opiskelijat

PrintWriter pw = new PrintWriter("opiskelija.data");
opiskelijat.forEach(o -> pw.println(o.opiskelijanumero + "\t" + o.nimi + "\t" + o.syntymavuosi));
pw.flush();
pw.close();

Vastaavasti opiskelijoiden lataaminen onnistuu seuraavasti.

// oletetaan, että käytössä on tyhjä opiskelijalista
// List<Opiskelija> opiskelijat

Files.lines(Paths.get("opiskelija.data")).forEach(rivi -> {
    String[] palat = rivi.split("\t");

    Opiskelija o = new Opiskelija();
    o.opiskelijanumero = palat[0];
    o.nimi = palat[1];
    o.syntymavuosi = Integer.parseInt(palat[2]);

    opiskelijat.add(o);
});

Opiskelijan kurssisuoritukset eivät tässä tallennu opiskelijaa kuvaavaan tiedostoon. Koska kurssisuoritus on erillinen -- vaikkakin opiskelijaan liittyvä -- käsite, tallennetaan kurssisuoritukset erilliseen tiedostoon. Miten sitten saamme selville jokaiseen opiskelijaan kuuluvat kurssisuoritukset? Kun tallennamme kurssisuorituksia, tallennamme jokaiseen kurssisuoritukseen tiedon opiskelijasta, johon kurssisuoritus kuuluu. Teemme samalla oletuksen, että opiskelijanumero yksilöi opiskelijan.

// oletetaan, että käytössä on lista kurssisuorituksia
// List<Kurssisuoritus> kurssisuoritukset

PrintWriter pw = new PrintWriter("kurssisuoritus.data");
kurssisuoritukset.forEach(k -> pw.println(k.opiskelija.opiskelijanumero + "\t" + k.kurssi));
pw.flush();
pw.close();

Entä sitten kurssisuoritusten lataaminen? Kurssisuoritus-olio ei sisällä opiskelijanumeroa merkkijonona, joten kurssisuoritusten lataamisen yhteydessä tulee aiemmin ladatuista opiskelijoista aina etsiä opiskelijanumeroa vastaava olio.

Tehtäväpohjassa on ohjelmakoodi opiskelijoiden lukemiseen ja tallentamiseen sekä kurssisuoritusten tallentamiseen. Täydennä tehtäväpohjassa olevan luokan Lukija metodia lueKurssisuoritukset siten, että se lukee kurssisuoritukset sekä kurssisuorituksiin liittyvät opiskelijat. Älä muuta metodin parametrien tyyppejä tai palautettavan arvon tyyppiä.

Rajattu tiedon käsittely

Edellinen esimerkki on tyypillinen ohjelmoinnin perusteiden ja jatkokurssin tiedon käsittelyesimerkki.

Esimerkissä on muutamia haasteita, sillä esimerkiksi pienten tietomäärien hakeminen isoista tiedostoista ei ole kovin tehokasta. Todellisuudessa suurten tietomäärien ohjelmallisessa käsittelyssä hyödynnetään käyttöjärjestelmän tarjoamaa mahdollisuutta tiedoston osien lukemiseen. Ohjelmointikielet kuten Java tarjoavat tähän myös abstraktion -- Javalla luokka RandomAccessFile tarjoaa lukumahdollisuuden vain osaan tiedoston sisällöistä.

Edellä mainitun työvälineen avulla tiedostojen käsittely tapahtuu tavumuodossa. Jos tiedämme, että opiskelijan opiskelijanumeron pituus on 10 merkkiä, ja opiskelijanumero on tiedoston alussa, onnistuu lukeminen seuraavasti.

// Avataan tiedosto 'opiskelija.data' lukemista varten
RandomAccessFile tiedosto = new RandomAccessFile("opiskelija.data", "r");

// Varataan 10 tavua tilaa muistista opiskelijanumeroa varten
byte[] opnro = new byte[10];

// luetaan opnro-taulukkoon tiedoston alusta taulukon kokoinen määrä sisältöä
tiedosto.read(opnro);

// luodaan opiskelijanumerosta merkkijono
String opiskelijanumero = new String(opnro);

Vastaavasti, opiskelijanumeron vaihtaminen tiedoston tietyssä kohdassa onnistuu seuraavasti.

// Avataan tiedosto 'opiskelija.data' lukemista ja kirjoittamista varten
RandomAccessFile tiedosto = new RandomAccessFile("opiskelija.data", "rwd");

// Oletetaan, että käytössämme on opiskelijanumero "0123456789"
String opiskelijanumero = "0123456789";

// Etsitään kohta, mihin opiskelijanumero halutaan kirjoittaa -- kirjoitetaan alkuun
tiedosto.seek(0);

// Kirjoitetaan opiskelijanumero tiedostoon tietyn opiskelijan kohdalle.
tiedosto.write(opiskelijanumero.getBytes());

// Nyt opiskelijanumero tiedostossa on muuttunut.

Mitä hyötyä tästä on? Jos sovimme ennalta, että kunkin tiedostossa olevan kentän -- eli esimerkiksi opiskelijanumeron tai nimen -- pituus on ennalta määrätty, tiedämme ennalta opiskelijaolioiden sijainnit tiedostossa. Oletetaan, että opiskelijanumero on aina 10 merkkiä ja nimi 40 merkkiä. Tällöin uuden opiskelijan tiedot alkavat aina 50 merkin välein. Hyötynä tästä on muunmuassa se, että tällöin tietyn opiskelijan tietojen etsimisessä tiedostosta muistissa tulee olla korkeintaan vain 50 merkkiä -- alla esimerkki opiskelijan nimen etsimisestä tietyn opiskelijanumeron perusteella.

// Avataan tiedosto 'opiskelija.data' lukemista ja kirjoittamista varten
RandomAccessFile tiedosto = new RandomAccessFile("opiskelija.data", "rwd");

// Oletetaan, että haemme opiskelijanumeroa "0123456789"
String haettava = "0123456789";

byte[] opiskelijanumero = new byte[10];

for (int indeksi = 0; indeksi < tiedosto.length(); indeksi += 50) {
    // tälle ei käytännössä olisi tarvetta, sillä tiedoston kohta päivittyy
    tiedosto.seek(indeksi);

    // komento tiedosto.read lukee tiedostosta annetun tiedoston mittaisen määrän tietoa
    // tässä luettu määrä määräytyy opiskelijanumero-taulukon pituuden perusteella 
    tiedosto.read(opiskelijanumero);

    // lukemisen jälkeen tiedoston käsittelykohta siirtyy eteenpäin 10 merkillä

    // tehokkuusnäkökulmasta tässäkin todennäköisesti oikeasti verrattaisiin
    // yksittäisiä merkkejä
    if(!haettava.equals(new String(opiskelijanumero))) {
        continue;
    }

    // koska tiedoston käsittelykohta on siirtynyt eteenpäin 10 merkillä
    // on seuraavaksi luettavana nimi
    byte[] nimi = new byte[40];
    tiedosto.read(nimi);
    System.out.println("Haetun opiskelijan nimi on " + new String(nimi).trim());

    break;
}

Toisaalta, jos haluaisimme pitää kirjaa opiskelijoiden sijainneista, ja hyväksyä ajatus siitä, että opiskelijanumerot ovat jatkuvasti muistissa, voi opiskelijoiden sijainnit tallentaa hajautustaulukkoon.

// Avataan tiedosto 'opiskelija.data' lukemista ja kirjoittamista varten
RandomAccessFile tiedosto = new RandomAccessFile("opiskelija.data", "rwd");

// Oletetaan, että käytössä on hajautustaulukko, jossa
// on tieto opiskelijanumerosta tiedoston indeksiin
Map<String, Integer> opiskelijoidenSijainnit = new HashMap<>();

// Oletetaan, että haemme opiskelijanumeroa "0123456789"
String haettava = "0123456789";

// nyt opiskelijan nimen saa helpohkosti -- olettaen, että opiskelija on olemassa ja että
// hajautustaulussa on opiskelijan tiedot aloittava indeksi

// siirrytään tiedostossa sopivaan kohtaan -- opiskelijan nimi tulee opiskelijanumeron jälkeen
tiedosto.seek(opiskelijoidenSijainnit.get(haettava) + 10);

// luetaan nimi
byte[] nimi = new byte[40];
tiedosto.read(nimi);
System.out.println("Haetun opiskelijan nimi on " + new String(nimi).trim());

Alla on kuvattuna erään viestijärjestelmän luokkakaavio. Jokaisella viestillä on sekä vastaanottaja että lähettäjä, jonka lisäksi viestiin kuuluu viestin sisältö. Käyttäjästä tallennetaan järjestelmään käyttäjätunnus, salasana, nimi, osoite ja puhelinnumero.

[Henkilo|kayttajatunnus:String;nimi:String;osoite:String;puhelinnumero:String]
					       [Viesti|sisalto:String]
					       [Henkilo]2-*[Viesti]

Tarkastellaan henkilöiden tallentamista ja lukemista RandomAccessFile-tiedoston avulla.

Oletetaan, että käyttäjätunnus on korkeintaan 8 merkkiä, salasana korkeintaan 8 merkkiä, nimi korkeintaan 30 merkkiä, osoite korkeintaan 30 merkkiä ja puhelinnumero korkeintaan 15 merkkiä. Tehtäväpohjassa on valmiina henkilöiden lukemis- ja kirjoitustoiminnallisuus.

Kirjoita toiminnallisuus, joka mahdollistaa henkilöiden tietojen muokkaamisen. Toteuta luokan Tallentaja metodi korvaaHenkilo siten, että metodi korvaa parametrina annetulla käyttäjätunnuksella tunnistettavan henkilön tiedon parametrina saadulla Henkilo-oliolla. Älä muokkaa metodin parametreja tai metodin palautustyyppiä.

Voit olettaa, että käyttäjä syöttää aina käyttäjätunnuksen, joka löytyy tiedostosta. Älä muokkaa muiden tiedostoon tallennettujen henkilöiden tietoja.

Sisällysluettelo