Einführung in ORMLite

Einführung in ORMLite

1. Überblick

ORMLite ist eine kompakte ORM-Bibliothek für Java-Anwendungen. Es bietet Standardfunktionen eines ORM-Tools für die häufigsten Anwendungsfälle,without the added complexity and overhead of other ORM frameworks.

Die Hauptmerkmale sind:

  • Definieren von Entitätsklassen mithilfe von Java-Annotationen

  • erweiterbareDAO Klassen

  • aQueryBuilder Klasse zum Erstellen komplexer Abfragen

  • generierte Klassen zum Erstellen und Löschen von Datenbanktabellen

  • Unterstützung für Transaktionen

  • Unterstützung für Entitätsbeziehungen

In den nächsten Abschnitten werden wir uns ansehen, wie wir die Bibliothek einrichten, Entitätsklassen definieren und Operationen an der Datenbank mithilfe der Bibliothek ausführen können.

2. Maven-Abhängigkeiten

Um ORMLite verwenden zu können, müssen wir die Abhängigkeit vonormlite-jdbczu unserenpom.xmlhinzufügen:


    com.j256.ormlite
    ormlite-jdbc
    5.0

Standardmäßig führt dies auch zu einer Abhängigkeit vonh2. In unseren Beispielen verwenden wir eine speicherinterneH2-Datenbank, sodass wir keinen weiteren JDBC-Treiber benötigen.

Wenn Sie eine andere Datenbank verwenden möchten, benötigen Sie auch die entsprechende Abhängigkeit.

3. Entitätsklassen definieren

Um unsere Modellklassen für die Persistenz mit ORMLite einzurichten, können wir zwei primäre Annotationen verwenden:

  • @DatabaseTable für die Entitätsklasse

  • @DatabaseField für die Eigenschaften

Beginnen wir mit der Definition einerLibrary-Entität mit einemname-Feld und einemlibraryId-Feld, das auch ein Primärschlüssel ist:

@DatabaseTable(tableName = "libraries")
public class Library {

    @DatabaseField(generatedId = true)
    private long libraryId;

    @DatabaseField(canBeNull = false)
    private String name;

    public Library() {
    }

    // standard getters, setters
}

Die Annotation@DatabaseTablehat ein optionales AttributtableName, das den Namen der Tabelle angibt, wenn wir uns nicht auf einen Standardklassennamen verlassen möchten.

Für jedes Feld, das als Spalte in der Datenbanktabelle beibehalten werden soll, müssen wir die Annotation@DatabaseFieldhinzufügen.

Die Eigenschaft, die als Primärschlüssel für die Tabelle dient, kann entweder mit den Attributenid,generatedId odergeneratedSequence gekennzeichnet werden. In unserem Beispiel wählen wir das AttributgeneratedId=true, damit der Primärschlüssel automatisch inkrementiert wird.

Beachten Sie außerdem, dass die Klasse einen Konstruktor ohne Argumente mit mindestenspackage-scopeichtbarkeit haben muss.

Einige andere bekannte Attribute, die wir zum Konfigurieren der Felder verwenden können, sindcolumnName,dataType,defaultValue,canBeNull,unique.

3.1. Verwenden vonJPA Anmerkungen

Zusätzlich zu den ORMLite-spezifischen Annotationen werdenwe can also use JPA-style annotations to define our entities.

Das Äquivalent der EntitätLibrary, die wir vor Verwendung der Standardanmerkungen vonJPAdefiniert haben, wäre:

@Entity
public class LibraryJPA {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long libraryId;

    @Column
    private String name;

    // standard getters, setters
}

Obwohl ORMLite diese Anmerkungen erkennt, müssen wir noch die Abhängigkeit vonjavax.persistence-apihinzufügen, um sie zu verwenden.

Die vollständige Liste der unterstütztenJPA-Anmerkungen lautet @Entity,@Id,@Column,@GeneratedValue, @OneToOne,@ManyToOne, @JoinColumn) s, @Version.

4. ConnectionSource

Um mit den definierten Objekten zu arbeiten, müssen Siewe need to set up a ConnectionSource.

Dazu können wir die KlasseJdbcConnectionSource verwenden, die eine einzelne Verbindung erstellt, oder die KlasseJdbcPooledConnectionSource, die eine einfache gepoolte Verbindungsquelle darstellt:

JdbcPooledConnectionSource connectionSource
  = new JdbcPooledConnectionSource("jdbc:h2:mem:myDb");

// work with the connectionSource

connectionSource.close();

Andere externe Datenquellen mit besserer Leistung können ebenfalls verwendet werden, indem sie in einDataSourceConnectionSource-Objekt eingeschlossen werden.

5. TableUtils Klasse

Basierend aufConnectionSource,we can use static methods from the TableUtils class to perform operations on the database schema:

  • createTable() - zum Erstellen einer Tabelle basierend auf einer Entitätsklassendefinition oder einemDatabaseTableConfig-Objekt

  • createTableIfNotExists() - ähnlich der vorherigen Methode, außer dass die Tabelle nur erstellt wird, wenn sie nicht vorhanden ist. Dies funktioniert nur in Datenbanken, die dies unterstützen

  • dropTable() - um eine Tabelle zu löschen

  • clearTable () - um die Daten aus einer Tabelle zu löschen

Mal sehen, wie wirTableUtils verwenden können, um die Tabelle für unsereLibrary-Klasse zu erstellen:

TableUtils.createTableIfNotExists(connectionSource, Library.class);

6. DAO Objekte

ORMLite enthälta DaoManager class that can create DAO objects for us with CRUD functionality:

Dao libraryDao
  = DaoManager.createDao(connectionSource, Library.class);

DaoManager generiert die Klasse nicht für jeden nachfolgenden Aufruf voncreateDao(), neu, sondern verwendet sie stattdessen für eine bessere Leistung.

Als nächstes können wir CRUD-Operationen fürLibrary-Objekte ausführen:

Library library = new Library();
library.setName("My Library");
libraryDao.create(library);

Library result = libraryDao.queryForId(1L);

library.setName("My Other Library");
libraryDao.update(library);

libraryDao.delete(library);

DAO ist auch ein Iterator, der alle Datensätze durchlaufen kann:

libraryDao.forEach(lib -> {
    System.out.println(lib.getName());
});

ORMLite schließt die zugrunde liegende SQL-Anweisung jedoch nur, wenn die Schleife bis zum Ende durchläuft. Eine Ausnahme oder eine return-Anweisung kann zu einem Ressourcenleck in Ihrem Code führen.

Aus diesem Grund wird in der ORMLite-Dokumentation empfohlen, den Iterator direkt zu verwenden:

try (CloseableWrappedIterable wrappedIterable
  = libraryDao.getWrappedIterable()) {
    wrappedIterable.forEach(lib -> {
        System.out.println(lib.getName());
    });
 }

Auf diese Weise können wir den Iterator mit einemtry-with-resources- oder einemfinally-Block schließen und Ressourcenlecks vermeiden.

6.1. Benutzerdefinierte DAO-Klasse

Wenn wir das Verhalten der bereitgestelltenDAO-Objekte erweitern möchten, können wir eine neue Schnittstelle erstellen, die denDao-Typ erweitert:

public interface LibraryDao extends Dao {
    public List findByName(String name) throws SQLException;
}

Fügen wir dann eine Klasse hinzu, die diese Schnittstelle implementiert und die KlasseBaseDaoImplerweitert:

public class LibraryDaoImpl extends BaseDaoImpl
  implements LibraryDao {
    public LibraryDaoImpl(ConnectionSource connectionSource) throws SQLException {
        super(connectionSource, Library.class);
    }

    @Override
    public List findByName(String name) throws SQLException {
        return super.queryForEq("name", name);
    }
}

Beachten Sie, dass wir einen Konstruktor dieses Formulars benötigen.

Um unsere benutzerdefiniertenDAO,zu verwenden, müssen wir den Klassennamen zur Klassendefinition vonLibraryhinzufügen:

@DatabaseTable(tableName = "libraries", daoClass = LibraryDaoImpl.class)
public class Library {
    // ...
}

Auf diese Weise können wirDaoManager verwenden, um eine Instanz unserer benutzerdefinierten Klasse zu erstellen:

LibraryDao customLibraryDao
  = DaoManager.createDao(connectionSource, Library.class);

Dann können wir alle Methoden aus der StandardklasseDAOowie unsere benutzerdefinierte Methode verwenden:

Library library = new Library();
library.setName("My Library");

customLibraryDao.create(library);
assertEquals(
  1, customLibraryDao.findByName("My Library").size());

7. Entitätsbeziehungen definieren

ORMLite verwendet das Konzept von "fremden" Objekten oder Sammlungen, um Beziehungen zwischen Entitäten für die Persistenz zu definieren.

Schauen wir uns an, wie wir jeden Feldtyp definieren können.

7.1. Fremdobjektfelder

Wir können eine unidirektionale Eins-zu-Eins-Beziehung zwischen zwei Entitätsklassen erstellen, indem wir das Attributforeign=true in einem mit@DatabaseField versehenen Feld verwenden. Das Feld muss von einem Typ sein, der auch in der Datenbank erhalten bleibt.

Definieren wir zunächst eine neue Entitätsklasse mit dem NamenAddress:

@DatabaseTable(tableName="addresses")
public class Address {
    @DatabaseField(generatedId = true)
    private long addressId;

    @DatabaseField(canBeNull = false)
    private String addressLine;

    // standard getters, setters
}

Als nächstes können wir unsererLibrary-Klasse ein Feld vom TypAddress hinzufügen, das alsforeign markiert ist:

@DatabaseTable(tableName = "libraries")
public class Library {
    //...

    @DatabaseField(foreign=true, foreignAutoCreate = true,
      foreignAutoRefresh = true)
    private Address address;

    // standard getters, setters
}

Beachten Sie, dass wir der Annotation@DatabaseField zwei weitere Attribute hinzugefügt haben:foreignAutoCreate undforeignAutoRefresh, beide auftrue. gesetzt

Das AttributforeignAutoCreate=true bedeutet, dass beim Speichern einesLibrary-Objekts mit einemaddress-Feld auch das Fremdobjekt gespeichert wird, vorausgesetzt, seinid ist nicht null und hat eingeneratedId=true Attribut.

Wenn wirforeignAutoCreate auffalse setzen, was der Standardwert ist, müssen wir das Fremdobjekt explizit beibehalten, bevor wir dasLibrary-Objekt speichern, das darauf verweist.

In ähnlicher Weise gibt das AttributforeignAutoRefresh =true an, dass beim Abrufen einesLibrary-Objekts auch das zugehörige Fremdobjekt abgerufen wird. Andernfalls müssen wir es manuell aktualisieren.

Fügen wir ein neuesLibrary-Objekt mit einemAddress-Feld hinzu und rufen Sie dielibraryDao auf, um beide beizubehalten:

Library library = new Library();
library.setName("My Library");
library.setAddress(new Address("Main Street nr 20"));

Dao libraryDao
  = DaoManager.createDao(connectionSource, Library.class);
libraryDao.create(library);

Dann können wir dieaddressDao aufrufen, um zu überprüfen, ob auch dieAddress gespeichert wurden:

Dao addressDao
  = DaoManager.createDao(connectionSource, Address.class);
assertEquals(1,
  addressDao.queryForEq("addressLine", "Main Street nr 20")
  .size());

7.2. Ausländische Sammlungen

Für diemany-Seite einer Beziehung können wir die TypenForeignCollection<T> oderCollection<T> mit einer@ForeignCollectionField-Annotation verwenden.

Erstellen wir eine neueBook-Entität wie oben und fügen dann eine Eins-zu-Viele-Beziehung in dieLibrary-Klasse ein:

@DatabaseTable(tableName = "libraries")
public class Library {
    // ...

    @ForeignCollectionField(eager=false)
    private ForeignCollection books;

    // standard getters, setters
}

Darüber hinaus müssen wir der KlasseBook ein Feld vom TypLibrary hinzufügen:

@DatabaseTable
public class Book {
    // ...
    @DatabaseField(foreign = true, foreignAutoRefresh = true)
    private Library library;

    // standard getters, setters
}

The ForeignCollection has add() and remove() methods, die mit den Datensätzen vom TypBook: arbeiten

Library library = new Library();
library.setName("My Library");
libraryDao.create(library);

libraryDao.refresh(library);

library.getBooks().add(new Book("1984"));

Hier haben wir einlibrary-Objekt erstellt und dann dembooks-Feld ein neuesBook-Objekt hinzugefügt, das es auch in der Datenbank beibehält.

Note that since our collection is marked as lazily loaded (eager=false), we need to call the refresh() method, bevor das Buchfeld verwendet werden kann.

Sie können die Beziehung auch erstellen, indem Sie das Feldlibrary in der KlasseBook festlegen:

Book book = new Book("It");
book.setLibrary(library);
bookDao.create(book);

Um zu überprüfen, ob beideBook-Objekte zu denlibrary hinzugefügt wurden, können wir diequeryForEq()-Methode verwenden, um alleBook-Datensätze mit den angegebenenlibrary_id: zu finden

assertEquals(2, bookDao.queryForEq("library_id", library).size());

Hier istlibrary_id der Standardname der Fremdschlüsselspalte, und der Primärschlüssel wird aus dem Objektlibraryabgeleitet.

8. QueryBuilder

JedesDAO kann verwendet werden, um einQueryBuilder-Objekt zu erhalten, das wir dann zum Erstellen leistungsfähigerer Abfragen nutzen können.

Diese Klasse enthält Methoden, die allgemeinen Operationen entsprechen, die in einer SQL-Abfrage verwendet werden, z. B.:selectColumns(), where(), groupBy(),having(), countOf(), distinct(), orderBy(), join().

Sehen wir uns ein Beispiel an, wie wir alleLibrary-Datensätze finden können, denen mehr als einBookzugeordnet ist:

List libraries = libraryDao.queryBuilder()
  .where()
  .in("libraryId", bookDao.queryBuilder()
    .selectColumns("library_id")
    .groupBy("library_id")
    .having("count(*) > 1"))
  .query();

9. Fazit

In diesem Artikel haben wir gesehen, wie wir Entitäten mit ORMLite definieren können, sowie die Hauptfunktionen der Bibliothek, mit denen wir Objekte und die zugehörigen relationalen Datenbanken bearbeiten können.

Der vollständige Quellcode des Beispiels istover on GitHub.