Skip to content

Latest commit

 

History

History
535 lines (404 loc) · 14.4 KB

File metadata and controls

535 lines (404 loc) · 14.4 KB

Matrikkel API Java Project - Oppsettsveiledning

Innholdsfortegnelse

  1. Prosjektoversikt
  2. Teknologisk Stack
  3. Installasjon
  4. Database Setup
  5. Import-bruk
  6. Arkitektur
  7. Utvikling

Prosjektoversikt

Formål

En robust Java-applikasjon som:

  • Kommuniserer med Matrikkel SOAP API (alle tjenester)
  • Håndterer server-side filtrering for effektiv datanedlasting
  • Lagrer data i PostgreSQL med full relasjonell integritet
  • Støtter to-stegs import-pattern for optimal ytelse
  • Håndterer eierforhold (personer, juridiske personer, matrikkelenheter)

Hvorfor Java?

  • Automatisk SOAP serialisering: JAX-WS håndterer komplekse objekter (MatrikkelBubbleId) automatisk
  • Type-sikkerhet: Kompileringstidssjekk av alle API-kall
  • WSDL-støtte: wsimport genererer perfekte klient-klasser fra WSDL
  • Spring Boot: Moderne stack med god PostgreSQL-integrasjon
  • Flyway: Database-migrasjoner med versjonskontroll

Teknologisk Stack

<properties>
    <java.version>21</java.version>
    <spring-boot.version>3.4.0</spring-boot.version>
    <flyway.version>10.20.1</flyway.version>
    <jaxws.version>4.0.0</jaxws.version>
</properties>

Core Dependencies

  • Spring Boot 3.4.0 - Latest stable release
  • Java 21 - Moderne LTS-versjon med bedre ytelse
  • PostgreSQL 17 - Database med full support for komplekse relasjoner
  • Flyway 10.20.1 - Database migrations (PostgreSQL 17 kompatibel)
  • JAX-WS 4.0.0 - SOAP client implementation
  • Spring Data JPA - Database access layer
  • Lombok - Reduser boilerplate code

Installasjon

Forutsetninger

# Java 21 JDK
java --version  # skal vise 21.x.x

# Maven 3.9+
mvn --version  # skal vise 3.9.x eller nyere

# PostgreSQL 17
psql --version  # skal vise 17.x

Klon Prosjekt

git clone <repository-url>
cd matrikkel_java

Environment Setup

Opprett .env-fil i prosjektets rot:

# Database Configuration
DB_HOST=localhost
DB_PORT=5432
DB_NAME=matrikkel2
DB_USERNAME=your_username
DB_PASSWORD=your_password

# Matrikkel API credentials (test environment)
MATRIKKEL_USERNAME=your_kommune_test
MATRIKKEL_PASSWORD=your_password

# Spring Configuration
SPRING_PROFILES_ACTIVE=dev

Kompiler Prosjekt

# Generer SOAP-klienter fra WSDL og kompiler
mvn clean compile

# Verifiser at genererte klasser finnes
ls -la target/generated-sources/wsimport/no/matrikkel/client/generated/nedlastning/

Database Setup

1. Opprett Database

# Koble til PostgreSQL
psql -U postgres

# Opprett database
CREATE DATABASE matrikkel2;

# Opprett bruker (hvis nødvendig)
CREATE USER your_username WITH PASSWORD 'your_password';
GRANT ALL PRIVILEGES ON DATABASE matrikkel2 TO your_username;

\q

2. Kjør Flyway Migrations

# Last inn environment variables
export $(grep -v '^#' .env | xargs)

# Kjør migrasjoner
mvn flyway:migrate

# Verifiser migrasjons-status
mvn flyway:info

Forventet output:

+-----------+---------+------------------+------+---------------------+---------+
| Category  | Version | Description      | Type | Installed On        | State   |
+-----------+---------+------------------+------+---------------------+---------+
| Versioned | 1       | baseline schema  | SQL  | 2025-10-21 12:00:00 | Success |
+-----------+---------+------------------+------+---------------------+---------+

3. Verifiser Tabeller

# Koble til database
export $(grep -v '^#' .env | xargs)
psql -h $DB_HOST -p $DB_PORT -U $DB_USERNAME -d $DB_NAME

# Vis alle tabeller
\dt

# Forventet resultat (14 tabeller):
# - matrikkel_matrikkelenheter
# - matrikkel_personer
# - matrikkel_fysiske_personer
# - matrikkel_juridiske_personer
# - matrikkel_eierforhold
# - matrikkel_bygninger
# - matrikkel_bruksenheter
# - matrikkel_veger
# - matrikkel_adresser
# - matrikkel_vegadresser
# - matrikkel_matrikkelenhet_owners (deprecated)
# - matrikkel_person_owners (deprecated)
# - matrikkel_juridisk_person_owners (deprecated)
# - flyway_schema_history

Import-bruk

Filtrert Import (Anbefalt)

Import kun data for spesifikke personer/organisasjoner:

# Last ned matrikkelenheter + personer + bygninger + adresser for personnummer
export $(grep -v '^#' .env | xargs) && mvn spring-boot:run \
  -Dspring-boot.run.arguments="--import --kommune=1103 --personnummer=964965226"

# Eller for organisasjon
export $(grep -v '^#' .env | xargs) && mvn spring-boot:run \
  -Dspring-boot.run.arguments="--import --kommune=4601 --organisasjonsnummer=964338531"

Hva skjer:

  1. MatrikkelenhetService filtrerer på server-side (f.eks. 4,744 IDs)
  2. StoreService henter komplette objekter i batches (500 per batch)
  3. Person-data lastes (eierforhold)
  4. Bygninger/bruksenheter lastes (API-filtrert)
  5. Adresser lastes (API-filtrert)

Ytelse: ~13 sekunder for 4,744 matrikkelenheter (vs minutter for bulk)

To-fase Import

Fase 1: Base import (kun matrikkelenheter + personer)

export $(grep -v '^#' .env | xargs) && mvn spring-boot:run \
  -Dspring-boot.run.arguments="--import --kommune=1103 --personnummer=964965226 --base-import"

Fase 2: Detalj-import (bygninger + adresser fra database-filtrering)

export $(grep -v '^#' .env | xargs) && mvn spring-boot:run \
  -Dspring-boot.run.arguments="--filter-existing --kommune=1103 --personnummer=964965226"

Bulk Import (Uten Filter)

# Last ned ALLE matrikkelenheter for kommune (ikke anbefalt for store kommuner)
export $(grep -v '^#' .env | xargs) && mvn spring-boot:run \
  -Dspring-boot.run.arguments="--import --kommune=4601 --limit=1000"

Arkitektur

Package-struktur

no.matrikkel/
├── config/                    # Spring configuration
│   ├── DatabaseConfig.java
│   ├── SoapClientConfig.java
│   └── MatrikkelProperties.java
├── client/                    # SOAP client wrappers
│   ├── generated/             # Auto-generated from WSDL
│   │   └── nedlastning/       # UNIFIED package (kritisk!)
│   ├── NedlastningClientWrapper.java
│   ├── StoreClientWrapper.java
│   └── ...
├── domain/
│   ├── entity/                # JPA entities
│   │   ├── Matrikkelenhet.java
│   │   ├── Person.java
│   │   ├── FysiskPerson.java
│   │   ├── JuridiskPerson.java
│   │   ├── Eierforhold.java
│   │   └── ...
│   └── dto/                   # Data transfer objects
├── repository/                # Spring Data JPA
│   ├── MatrikkelenhetRepository.java
│   └── ...
├── service/                   # Business logic
│   ├── MatrikkelenhetImportService.java
│   ├── PersonImportService.java
│   └── ...
├── mapper/                    # Entity ↔ DTO converters
│   ├── MatrikkelenhetMapper.java
│   └── ...
└── cli/                       # Command-line interface
    └── ImportCommand.java

To-stegs API Pattern

Kritisk optimalisering for filtrert nedlasting:

// Steg 1: Server-side filtrering (kun IDer)
MatrikkelenhetsokModel model = new MatrikkelenhetsokModel();
model.setKommunenummer(kommunenummer);
model.setNummerForPerson(personnummer);
MatrikkelenhetIdList idList = matrikkelenhetService.findMatrikkelenheter(model, context);

// Steg 2: Batch-hent komplette objekter
MatrikkelBubbleIdList bubbleIdList = convertToMatrikkelBubbleIdList(idList);
MatrikkelBubbleObjectList objects = storeService.getObjects(bubbleIdList, context);

Fordeler:

  • ✅ 99% reduksjon i dataoverføring
  • ✅ Server gjør filtreringen (raskere)
  • ✅ Batch-processing (500 objekter per batch)

Package Consolidation (KRITISK!)

⚠️ VIKTIG: Alle WSDL-tjenester som deler XSD-schemas MÅ bruke samme package-navn!

<!-- pom.xml - ALLE tjenester bruker nedlastning package -->
<execution>
    <id>wsimport-store</id>
    <configuration>
        <packageName>no.matrikkel.client.generated.nedlastning</packageName>
    </configuration>
</execution>
<execution>
    <id>wsimport-nedlastning</id>
    <configuration>
        <packageName>no.matrikkel.client.generated.nedlastning</packageName>
    </configuration>
</execution>

Hvorfor?

  • StoreService og NedlastningService deler samme XSD schema
  • Forskjellige packages → ClassCastException runtime!
  • Unified package → type-safe casting ✅

Se PACKAGE_CONSOLIDATION_FIX.md for detaljer.

Database Schema

Hovedtabeller:

  1. matrikkel_matrikkelenheter - Matrikkelenheter (grunndata)

    • Primærnøkkel: matrikkel_matrikkelenhet_id
    • Business key: (kommunenummer, gardsnummer, bruksnummer, festenummer, seksjonsnummer)
  2. matrikkel_personer - Personer (base-tabell for inheritance)

    • Primærnøkkel: id (JPA auto-generated)
    • API ID: matrikkel_person_id
    • Viktig: nummer inneholder både personnummer OG organisasjonsnummer!
  3. matrikkel_fysiske_personer - Fysiske personer (extends Person)

    • Join: fysisk_person_entity_idmatrikkel_personer.id
  4. matrikkel_juridiske_personer - Juridiske personer (extends Person)

    • Join: juridisk_person_entity_idmatrikkel_personer.id
  5. matrikkel_eierforhold - Eierforhold (linking table)

    • Join til matrikkelenhet: matrikkelenhet_id
    • Join til person: fysisk_person_id eller juridisk_person_entity_id
    • Deprecated fields: person_owner_id, juridisk_person_owner_id, matrikkelenhet_owner_id
  6. matrikkel_bygninger - Bygninger

  7. matrikkel_bruksenheter - Bruksenheter

  8. matrikkel_veger - Veger (må lastes FØR adresser!)

  9. matrikkel_adresser - Adresser (base-tabell)

  10. matrikkel_vegadresser - Vegadresser (extends Adresse)

Se docs/DATABASE_SCHEMA.md for komplett skjema.


Utvikling

Maven Commands

# Kompiler prosjekt og generer SOAP-klienter
mvn clean compile

# Kjør tester
mvn test

# Package til JAR
mvn clean package -DskipTests

# Kjør applikasjon
export $(grep -v '^#' .env | xargs) && mvn spring-boot:run \
  -Dspring-boot.run.arguments="--import --kommune=1103 --personnummer=964965226"

Database-verktøy

# Koble til database
export $(grep -v '^#' .env | xargs)
psql -h $DB_HOST -p $DB_PORT -U $DB_USERNAME -d $DB_NAME

# Tell matrikkelenheter
SELECT COUNT(*) FROM matrikkel_matrikkelenheter;

# Se eierforhold for person
SELECT m.matrikkelnummer_tekst, p.navn, e.andel_teller, e.andel_nevner
FROM matrikkel_eierforhold e
JOIN matrikkel_matrikkelenheter m ON e.matrikkelenhet_id = m.matrikkel_matrikkelenhet_id
JOIN matrikkel_personer p ON e.fysisk_person_id = p.id OR e.juridisk_person_entity_id = p.id
WHERE p.nummer = '964965226'
LIMIT 10;

# Vis alle tabeller med antall rader
SELECT 
    schemaname,
    tablename,
    (xpath('/row/cnt/text()', xml_count))[1]::text::int as row_count
FROM (
    SELECT 
        schemaname, 
        tablename, 
        query_to_xml(format('select count(*) as cnt from %I.%I', schemaname, tablename), false, true, '') as xml_count
    FROM pg_tables
    WHERE schemaname = 'public'
    AND tablename LIKE 'matrikkel_%'
) t
ORDER BY row_count DESC;

Logging

Applikasjonen logger til konsoll med følgende nivåer:

# application-dev.yml
logging:
  level:
    no.matrikkel: DEBUG              # Vår egen kode
    org.springframework.web: INFO   
    org.hibernate.SQL: DEBUG         # SQL-spørringer
    com.sun.xml.ws: WARN             # SOAP client (kun warnings)

Viktige Kode-mønstre

1. Transaksjonshåndtering - Per-Batch Commits

// ❌ FEIL - Outer transaction "svelger" inner commits
@Transactional
public void importAll() {
    for (batch : batches) {
        personService.saveBatch(batch);  // Commit skjer ikke før metoden er ferdig!
    }
}

// ✅ RIKTIG - Ingen outer transaction, inner commits umiddelbart
public void importAll() {
    for (batch : batches) {
        personService.saveBatch(batch);  // @Transactional - commits etter hver batch!
    }
}

2. N+1 Query Prevention

// ❌ FEIL - N+1 queries
for (Matrikkelenhet m : matrikkelenheter) {
    Veg veg = vegRepository.findById(vegId);  // Database query per iteration!
}

// ✅ RIKTIG - Load into Map first (O(1) lookup)
Map<Long, Veg> vegerMap = veger.stream()
    .collect(Collectors.toMap(Veg::getVegId, Function.identity()));

for (Matrikkelenhet m : matrikkelenheter) {
    Veg veg = vegerMap.get(vegId);  // In-memory lookup!
}

3. Person Number Filtering

// ❌ FEIL - Disse feltene er NULL!
fysiskPersonRepository.findByFodselsnummer(nummer);
juridiskPersonRepository.findByOrganisasjonsnummer(nummer);

// ✅ RIKTIG - Bruk Person.nummer (universelt felt)
personRepository.findByNummer(nummer);

Testing

# Kjør alle tester
mvn test

# Kjør spesifikk test
mvn test -Dtest=MatrikkelenhetImportServiceTest

# Kjør med coverage
mvn test jacoco:report

Feilsøking

Problem: ClassCastException ved runtime

nedlastning.Grunneiendom cannot be cast to store.Matrikkelenhet

Løsning: Alle WSDL-tjenester må bruke samme package! Se PACKAGE_CONSOLIDATION_FIX.md


Problem: Flyway migrerer til feil database

# Verifiser at pom.xml bruker environment variables
grep -A 5 "flyway-maven-plugin" pom.xml

# Skal vise: ${env.DB_HOST}, ${env.DB_PORT}, ${env.DB_NAME}

Løsning: Oppdater pom.xml Flyway-konfigurasjon til å bruke environment variables.


Problem: Adresser blir skippet (0 saved)

Mapped 0 adresser: 0 vegadresser, 0 matrikkeladresser (37 skipped)

Løsning: Last ned veger FØR adresser! AdresseMapper krever Veg-entiteter i database.

# RIKTIG rekkefølge:
fetchAndSaveVegData(kommunenummer);        # 1. Last veger
fetchAndSaveAdresseData(matrikkelenheter); # 2. Last adresser

Dokumentasjon


Lisens

Dette prosjektet er utviklet for integrasjon med Kartverkets Matrikkel API.