Zadanie 2 — anotácie a proxy

Ciele

  1. Precvičiť získavanie metadát o kóde z anotácií.
  2. Naučiť sa spracovávať anotácie počas prekladu programu.
  3. Naučiť sa dynamicky vytvárať triedy v Jave pomocou dynamického proxy.

Úloha 1: Metadáta

Pomocou anotácií @Id, @Table a @Column bude možné špecifikovať detaily ukladania:

  • stĺpec, ktorý bude primárnym kľúčom,
  • názov tabuľky,
  • názov stĺpca,
  • podporu prázdnej hodnoty (NULL a NOT NULL),
  • unikátnosť hodnoty (UNIQUE).

Tieto informácie sa použijú pri generovaní SQL príkazov. Nie je nutné implementovať vlastnú validáciu splnenia podmienok (napr. not null), ale ponechať to na databázu.

Anotácie musia byť umiestnené v novom Maven module orm-annotations, v balíku sk.tuke.meta.persistence.annotations. Anotácia @Id nebude mať parametre.

Anotácia @Table má mať jeden parameter (nepovinný):

  • name – názov tabuľky v databáze (ak sa nezadá, použije sa názov triedy)

Anotácia @Column má mať parametre (všetky nepovinné):

  • name – názov stĺpca v databáze (ak sa nezadá, použije sa názov premennej)
  • nullable – môže obsahovať null? (predvolená hodnota true)
  • unique – má byť hodnota unikátna? (predvolená hodnota false)

Triedy definujúce databázovú tabuľku teraz budu musieť mať anotáciu @Table. Členské premenné, ktoré nemajú anotáciu @Column sa nebudú brať do úvahy pri generovaní tabuľky.

Nezabudnite aktualizovať implementáciu tak, aby sa pri generovaní SQL používali názvy špecifikované pomocou anotácií.

Organizácia projektu

Modul orm-annotations môže mať takúto konfiguráciu Maven (pom.xml):

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
                             http://maven.apache.org/xsd/maven-4.0.0.xsd">

  <modelVersion>4.0.0</modelVersion>

  <parent>
    <groupId>sk.tuke.meta.orm</groupId>
    <artifactId>assignment-parent</artifactId>
    <version>1.0-SNAPSHOT</version>
  </parent>

  <artifactId>orm-annotations</artifactId>
  <packaging>jar</packaging>

</project>

Potrebujete ho pridať tiež ako závislosť projektu orm-runtime (orm-runtime/pom.xml):

  <dependencies>
    <dependency>
      <groupId>sk.tuke.meta.orm</groupId>
      <artifactId>orm-annotations</artifactId>
      <version>${project.version}</version>
    </dependency>
  </dependencies>

A tiež do zoznamu modulov v koreňovom pom.xml:

  <modules>
    <module>orm-runtime</module>
    <module>orm-annotations</module>
    <module>example-app</module>
  </modules>

Úloha 2: Anotačný procesor

SQL príkaz pre vytvorenie tabuliek sa má generovať počas kompilácie. Metóda createTables() bude využívať SQL kód vygenerovaný už počas prekladu a bude fungovať aj bez zadania zoznamu tried.

Vygenerovaný SQL príkaz musí byť umiestnený tak, aby po zabalení výsledného programu do JAR a prenesení do iného prostredia, bolo možné ho správne načítať.

Anotačný procesor implementujte v novom Maven module orm-processor.

Poznámka

Podstatné je uvedomiť si, že SQL súbor musí byť uložený tak, aby bolo možné ho použiť aj po tom, ako výslednú aplikáciu v podobe JAR súboru prenesiete do iného prostredia a spustíte. Najlepšie je, aby bol priamo súčasťou JAR súboru. Dá sa to dosiahnuť tak, že ho vytvorite pomocou Filer.createResource(StandardLocation.CLASS_OUTPUT, ...).

Na jeho načítanie v bežiacom programe môžete použiť metódu ClassLoader.getResourceAsStream(), ktorá hľadá súbor v CLASSPATH.

Organizácia projektu

Modul orm-processor môže mať takúto konfiguráciu (pom.xml):

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
                             http://maven.apache.org/xsd/maven-4.0.0.xsd">

  <modelVersion>4.0.0</modelVersion>

  <parent>
    <groupId>sk.tuke.meta.orm</groupId>
    <artifactId>assignment-parent</artifactId>
    <version>1.0-SNAPSHOT</version>
  </parent>

  <artifactId>orm-processor</artifactId>
  <packaging>jar</packaging>

  <dependencies>
    <dependency>
      <groupId>sk.tuke.meta.orm</groupId>
      <artifactId>orm-annotations</artifactId>
      <version>${project.version}</version>
    </dependency>
    <dependency>
      <groupId>sk.tuke.meta.orm</groupId>
      <artifactId>orm-runtime</artifactId>
      <version>${project.version}</version>
    </dependency>
  </dependencies>

</project>

Je potrebné ho pridať aj do zoznamu projektov v koreňovom pom.xml:

  <modules>
    <module>orm-runtime</module>
    <module>orm-annotations</module>
    <module>orm-processor</module>
    <module>example-app</module>
  </modules>

A zapnúť vykonávanie anotačného procesora pri kompilácií example-app:

  <build>
    <finalName>app</finalName>
    <plugins>

      <!-- Configure annotation processor -->
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <configuration>
          <annotationProcessorPaths>
            <path>
              <groupId>sk.tuke.meta.orm</groupId>
              <artifactId>orm-processor</artifactId>
              <version>${project.version}</version>
            </path>
          </annotationProcessorPaths>
        </configuration>
      </plugin>

      <!-- Create executable JAR with all dependencies -->
      <plugin>...</plugin>

    </plugins>
  </build>

Úloha 3: Oneskorené načítanie

Aby sa predišlo načítavaniu referovaných objektov, ktoré nebudú používané, knižnica zabezpečí oneskorené načítanie (lazy fetching) – referovaný objekt bude načítaný až pri jeho prvom použití.

Pre konfiguráciu oneskoreného načítavania pridajte do anotácie @Column parametre

  • lazyFetch (boolean, predvolene false)
  • targetClass (typ Class, trieda, objekt ktorej sa má načítať)

Oneskorené načítanie stačí podporovať len pre prípady, kedy sa na objekt referuje cez rozhranie. To, ktorá konkrétna trieda má byť vytvorená pri načítaní z databázy, sa uvedie pomocou parametra anotácie, napríklad:

    @Column(lazyFetch = true, targetClass = Department.class)
    private IDepartment department;

Pri načítaní objektu, obsahujúceho takúto referenciu, sa referovaný objekt nebude načítavať z databázy, kým sa nepoužije. Napríklad:

Person p = manager.get(Person.class, 1);  // only Person is loaded
IDepartment d = p.getDepartment();        // d is just a proxy
d.getName();                              // now the real object is loaded

Nezabudnite ošetriť ukladanie objektov obsahujúcich proxy namiesto referencie.

Celková štruktúra projektu

.
├── example-app
│   └── ...
├── orm-annotations
│   ├── pom.xml
│   └── src
│       └── main
│           └── java
│               └── sk
│                   └── tuke
│                       └── meta
│                           └── persistence
│                               └── annotations
│                                   ├── Column.java
│                                   ├── Id.java
│                                   └── Table.java
├── orm-processor
│   ├── pom.xml
│   └── src
│       └── main
│           ├── java
│           │   └── sk
│           │       └── tuke
│           │           └── meta
│           │               └── persistence
│           │                   └── ...
│           └── resources
│               └── META-INF
│                   └── services
│                       └── javax.annotation.processing.Processor
├── orm-runtime
│   ├── pom.xml
│   └── src
│       └── main
│           └── java
│               └── sk
│                   └── tuke
│                       └── meta
│                           └── persistence
│                               ├── PersistenceException.java
│                               ├── PersistenceManager.java
│                               ├── PersistenceManagerFactory.java
│                               ├── ReflectivePersistenceManager.java
│                               └── ...
├── mvnw
├── mvnw.cmd
└── pom.xml

Odovzdanie

Zadanie je potrebné vypracovať v priradenom projekte v GitLabe do konca 12. apríla.