• Link zu Xing
  • Link zu LinkedIn
  • Link zu X
  • Link zu Rss dieser Seite
  • Link zu GitHub
  • Newsletter
  • Kontaktieren Sie mich
Frank W. Rahn
  • Meine BlogbeiträgeZeigt meinen Blog an
  • RessourcenZeigt Ihnen eine Auswahl von Ressourcen
    • Franks aktueller IT-Werkzeugkasten
      • Diese Werkzeuge setze ich zur Zeit ein
    • Meine Präsentationen
      • Zeigt Ihnen meine Präsentationen
    • Weblinks
      • Meine Linksammlung
    • Buchtipps
      • Eine Liste von mir empfohlener Literatur
    • XML-Namespace
      • Zeigt meinem XML-Namespace
  • Franks aktueller IT-WerkzeugkastenDiese Werkzeuge setze ich zur Zeit ein
  • Meine PräsentationenZeigt Ihnen meine Präsentationen
  • WeblinksMeine Linksammlung
  • BuchtippsEine Liste von mir empfohlener Literatur
  • XML-NamespaceZeigt meinem XML-Namespace
  • Über mich …Die persönlichen Informationen über den Softwarearchitekt Frank Rahn
  • Click to open the search input field Click to open the search input field Suche
  • Menü Menü
Artikel, Beispiele und Vorträge

Spring mit JPA und Hibernate (Tutorial)

Klassendiagramm dieses Beispiels

Dieser Beitrag ist Teil einer (Tutorial-) Serie über die Einführung in das Spring Framework und beschreibt den Einsatz von JPA (Datenbankzugriff) und Hibernate (OR-Mapper) mit dem Spring Framework.

Inhaltsverzeichnis [anzeigen]

  • Die Struktur des Projektes
  • Die Literaturempfehlungen für dieses Beispiel
  • Das abstrakte und generische Datenzugriffsobjekt
  • Die Spezialisierung des Data Access Objects für JPA
  • Die Definition des Service der Fahrerverwaltung
  • Die Entitäten des Services Fahrerverwaltung
  • Das Data Access Object für die Fahrerverwaltung
  • Die Implementierung des Services der Fahrerverwaltung
  • Die Anwendung mit Logging
  • Der Unit Test
  • Der Quellcode und Download des Beispiels
  • Die Maven Befehl
  • Update: Criteria API
  • Update am 28.09.2012
  • Updates von 06.10.2012 bis zum 14.10.2012

Die Struktur des Projektes

Dieses Beispiel baut auf dem vorherigem Beispiel Spring an einem einfachem Beispiel in der Version 2.0.x auf. Die verwendeten Frameworks und Werkzeuge sind hier beschrieben. Dieses Beispiel ist eine Konsolenanwendung, die eine Fahrerverwaltung umsetzt. In diesem Beispiel werden die folgenden Technologien des Spring Frameworks vorgestellt:

  • Die Definition und die Verwendung von abstrakten und generischen Datenzugriffsobjekten (DAO).
  • Die Spezialisierung von Datenzugriffsobjekten für JPA.
  • Die Definition von Entitäten mit Relationen, Named Queries und Sequenzen.
  • Die Berücksichtigen von Besonderheiten bei Entitäten.
  • Das Erstellen einer XML Konfiguration für JPA.
  • Das Deklarieren eines Datenzugriffsobjekt im Spring Framework.
  • Ein Service im Spring Framework mit Transaktionen versehen.
  • Die Datenbank-spezifische Konfiguration für das Spring Framework erzeugen.
  • Einen JUnit-Test für Datenbankoperationen mit dem Spring Framework erstellen.

Die folgende Bibliotheken werden benötigt:

Die benötigten Bibliotheken (Dependencies)

Die benötigten Bibliotheken - Dependencies (© Frank Rahn)

Die Literaturempfehlungen für dieses Beispiel

  • Referenzdokumentation des Spring Framework’s
  • Spring 3: Framework für die Java-Entwicklung (*)

Das abstrakte und generische Datenzugriffsobjekt

Ein DAO (Data Access Object) dient zum Abstrahieren von der konkreten Persistenz-Technologie und entkoppelt die Geschäftslogik vom Datenbankzugriff. Die folgende Schnittstelle eines Datenzugriffsobjektes (DAO) ist noch unabhängig von einer konkreten Zugriffstechnologie.

Diese Schnittstelle definiert die elementarsten Methoden (CRUD) für den Datenzugriff. Als generische Parametern wird die Klasse der Entität und des primären Schlüssels (Primary Key) definiert.

/**
 * Ein generische DAO-Schnittstelle für eine Entität mit einem primären Key.
 * @author Frank W. Rahn
 * @param <Entity> Die Klasse der Entität
 * @param <PrimaryKey> Die Klasse des primären Key
 */
public interface GenericDAO<Entity, PrimaryKey extends Serializable> {

    /**
     * Liefere den primären Key für das angegebene Objekt.
     * @param persistentObject das persistente Objekt
     * @return den primären Key
     */
    PrimaryKey getPrimaryKey(Entity persistentObject);

    /**
     * Speichere das neue Objekt in der Datenbank.
     * @param newPersistentObject das neue persistente Objekt
     * @return den primären Key
     */
    PrimaryKey create(Entity newPersistentObject);

    /**
     * Aktualisiere das geänderte Objekt in der Datenbank.
     * @param persistentObject das persistente Objekt
     */
    void save(Entity persistentObject);

    /**
     * Lösche das persistente Objekt aus der Datenbank.
     * @param persistentObject das persistente Objekt
     */
    void remove(Entity persistentObject);

    /**
     * Lösche das persistente Objekt aus der Datenbank.
     * @param key der primäre Key
     */
    void remove(PrimaryKey key);

    /**
     * Finde das persistente Objekt an Hand seines primären Keys.
     * @param key der primäre Key
     * @return das persistente Objekt
     */
    Entity findByPrimaryKey(PrimaryKey key);

}

Nun folgt eine abstrakte Implementierung dieser Schnittstelle. In dieser Implementierung wurde …

  • in Zeile 21 der Logger definiert und
  • in Zeile 30 bis 41 im Object-Initializer die Ermittlung der konkreten Klassen der Entität und des primären Schlüssels durchgeführt.
/**
 * Eine abstrakte Implementierung der Schnittstelle {@link GenericDAO}.
 * @author Frank W. Rahn
 * @param <Entity> Die Klasse der Entität
 * @param <PrimaryKey> Die Klasse des primären Key
 */
public abstract class AbstractGenericDAO<Entity, PrimaryKey extends Serializable>
    implements GenericDAO<Entity, PrimaryKey> {

    /** Der zentrale Logger für die DAO's. */
    protected final static Logger logger = getLogger(GenericDAO.class);

    /** Die Klasse der Entität. */
    protected final Class<Entity> entityClass;

    /** Die Klasse des primären Key. */
    protected final Class<PrimaryKey> primaryKeyClass;

    {
        ParameterizedType type =
            (ParameterizedType) getClass().getGenericSuperclass();
        Type[] actualTypeArguments = type.getActualTypeArguments();

        @SuppressWarnings("unchecked")
        Class<Entity> entityClass = (Class<Entity>) actualTypeArguments[0];
        this.entityClass = entityClass;

        @SuppressWarnings("unchecked")
        Class<PrimaryKey> primaryKey =
            (Class<PrimaryKey>) actualTypeArguments[1];
        this.primaryKeyClass = primaryKey;
    }

    /**
     * {@inheritDoc}
     * @see GenericDAO#remove(java.io.Serializable)
     */
    @Override
    public void remove(PrimaryKey key) {
        remove(findByPrimaryKey(key));
    }

}

Die Spezialisierung des Data Access Objects für JPA

Die grundlegende Schnittstelle eines Datenzugriffsobjektes diesmal abhängig von der Zugriffstechnologie Java Persistence API (JPA).

/**
 * Eine Erweiterung der Schnittstelle {@link GenericDAO} für JPA.
 * @author Frank W. Rahn
 * @param <Entity> Die Klasse der Entität
 * @param <PrimaryKey> Die Klasse des primären Key
 * @see de.rahn.db.dao.GenericDAO
 */
public interface GenericJpaDAO<Entity, PrimaryKey extends Serializable> extends
    GenericDAO<Entity, PrimaryKey> {

    // Noch keine spezielle Definitionen.

}

In der folgenden abstrakten Implementierung dieser Schnittstelle wird die JPA Annotation @PersistenceContext verwendet. Der EntityManager wird dadurch durch das Spring Framework injiziert.

/**
 * Eine Implementierung der Schnittstelle {@link GenericJpaDAO} für JPA.
 * @author Frank W. Rahn
 * @param <Entity> Die Klasse der Entität
 * @param <PrimaryKey> Die Klasse des primären Key
 * @see de.rahn.db.dao.AbstractGenericDAO
 */
public abstract class AbstractGenericJpaDAO<Entity, PrimaryKey extends Serializable>
    extends AbstractGenericDAO<Entity, PrimaryKey>
    implements GenericJpaDAO<Entity, PrimaryKey> {

    @PersistenceContext
    private EntityManager entityManager;

    /**
     * @return Liefert den {@link #entityManager}
     */
    protected final EntityManager getEntityManager() {
        return entityManager;
    }

    /**
     * {@inheritDoc}
     * @see GenericDAO#create(java.lang.Object)
     */
    @Override
    public PrimaryKey create(Entity newPersistentObject) {
        entityManager.persist(newPersistentObject);
        return getPrimaryKey(newPersistentObject);
    }

    /**
     * {@inheritDoc}
     * @see GenericDAO#save(java.lang.Object)
     */
    @Override
    public void save(Entity persistentObject) {
        entityManager.merge(persistentObject);
    }

    /**
     * {@inheritDoc}
     * @see GenericDAO#remove(java.lang.Object)
     */
    @Override
    public void remove(Entity persistentObject) {
        entityManager.remove(persistentObject);
    }

    /**
     * {@inheritDoc}
     * @see GenericDAO#findByPrimaryKey(java.io.Serializable)
     */
    @Override
    public Entity findByPrimaryKey(PrimaryKey key) {
        return entityManager.find(entityClass, key);
    }

}

Im folgendem Klassendiagramm ist der Sachverhalt der abstrakten generischen JPA Datenzugriffsobjekte grafisch per UML dargestellt.

Abstrakte generische Datenzugriffsobjekte (DAO) für JPA

Abstrakte generische Datenzugriffsobjekte für JPA (© Frank Rahn)

Die Definition des Service der Fahrerverwaltung

Zunächst wird die Schnittstelle dieses Services definiert. Auch dieses Beispiel ist relativ einfach gehalten.

/**
 * Das Interface zum Service Drivers.
 * @author Frank W. Rahn
 */
public interface Drivers {

    /**
     * Hole alle Fahrer.
     * @return die Liste der Fahrer
     */
    List<Driver> getDrivers();

    /**
     * Hole einen Fahrer.
     * @param id die Id eines Fahrers
     * @return der Fahrer
     */
    Driver getDriver(Long id);

    /**
     * Lege einen Fahrer an.
     * @param name der Name des Fahrers
     * @param firstname der Vorname des Fahrers
     * @return die Id des Fahrers
     */
    Long createDriver(String name, String firstname);

    /**
     * Lege einen Fahrer an.
     * @param driver der Fahrer
     * @return die Id des Fahrers
     */
    Long create(Driver driver);

    /**
     * Speichere den Fahrer.
     * @param driver der Fahrer
     */
    Driver save(Driver driver);

    /**
     * Füge ein Auto zum Fahrer hinzu.
     * @param id die Id des Fahrers
     * @param car das Auto
     * @return der komplette Fahrer
     */
    Driver addCarToDriver(Long id, Car car);

}

Die XML Konfiguration für dieses Modul.

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd
    ">

    <description>
        Dieses ist die zentrale Konfiguration für den Service Drivers.
    </description>

    <!-- Scanne das Package nach Spring Beans -->
    <context:component-scan base-package="de.rahn.services.drivers" />

</beans>

Die Entitäten des Services Fahrerverwaltung

Unsere Fahrerverwaltung besteht aus Fahrern mit ihren Autos. Hier werden nun eine Reihe von Annotationen von JPA verwendet, um das Datenmodell zu definieren.

  1. In den Zeilen 27 bis 29 wird diese Klasse als Entität mit Zugriff auf Attribut-Ebene definiert. Zusätzlich wird das Datenbankschema angegeben. Der Tabellenname rahn.Driver wird aus dem Klassennamen generiert.
  2. In der Zeile 30 wird eine statische und benannte Abfrage mit JPQL definiert. Mit dieser Abfrage können alle Fahrer selektiert werden. Damit wird sogenanntes SQL-Injection ausgeschlossen, da für diese Abfragen ausschließlich PreparedStatement verwendet werden.
  3. In den Zeilen 37 bis 41 wird der primäre Schlüssel für diese Klasse definiert. Dabei wird dieser Identifizierer über eine DB-Sequenz automatisch vergeben.
  4. In der Zeile 54 wird eine gerichtete Relation (1-zu-n) mit weiterreichen der CRUD-Operationen (Cascade) zum Auto definiert.
  5. In der Zeile 55 wird die Spalte driver-id in der Tabelle des Autos definiert. Dieses ist notwendig, da die Klasse Auto den Fahrer nicht kennt – also kein Attribut des Fahrers besitzt. Wenn in der Klasse Auto das Attribut public Driver driver; definiert worden wäre, hatte folgende Anweisung für die Relation ausgereicht:
    @OneToMany(cascade = CascadeType.ALL, mappedBy = "driver").
/**
 * Die Klasse eines Fahrers.
 * @author Frank W. Rahn
 */
@Entity
@Table(schema = "rahn")
@Access(AccessType.FIELD)
@NamedQueries(@NamedQuery(name = Driver.FIND_ALL, query = "from Driver d"))
public class Driver {

    /** Konstante für die NamedQuery. */
    public static final String FIND_ALL = "Driver.findAll";

    /** Der Identifizierer des Fahrers. */
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "DriverSEQ")
    @SequenceGenerator(name = "DriverSEQ", sequenceName = "DriverSEQ",
        schema = "rahn")
    @Basic(optional = false)
    private Long id;

    /** Der Name des Fahrers. */
    @Basic(optional = false)
    @Column(nullable = false)
    private String name;

    /** Der Vorname des Fahrers. */
    @Basic
    private String firstname;

    /** Die Autos die der Fahrer fährt. */
    @OneToMany(cascade = CascadeType.ALL)
    @JoinColumn(name = "driver_id", nullable = false)
    private Set<Car> cars = new HashSet<>();
...

Im folgendem Abschnitt werden die von Eclipse IDE generierten Methoden für hashCode() und equals() dargestellt. Diese Methoden müssen überladen werden, da eine Entität eine eindeutige Identität besitzt, die über die beiden Methoden bestimmt werden muss.

Zwei eigentlich gleiche Entitäten werden als ungleich bewertet, wenn …

  • die Entitäten von unterschiedlichen EntityManager gelesen wurden oder
  • die eine Entität per Eager Loading (direkt beim Ausführen des Selects) und die andere Entität per Lazy Loading (durch einen Proxy beim Zugriff auf das Feld) geladen wurden.

Der automatische erzeugte Schlüssel (id) wird erst beim Speichern der Entitäten gesetzt. Die beiden Methoden hashCode() und equals() müssen von denselben signifikanten Attributen abhängen.

  • In der Zeile 67 und 99 wird mit Persistence.getPersistenceUtil().isLoaded(cars) geprüft, ob die Autos schon geladen wurden. Wenn nicht würden die Entitäten des Autos geladen oder einen Fehler auslösen, wenn der EntityManager schon geschlossen wurde.
    /**
     * @see java.lang.Object#hashCode()
     */
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        if (cars != null && Persistence.getPersistenceUtil().isLoaded(cars)) {
            result = prime * result + cars.hashCode();
        } else {
            result = prime * result;
        }
        result =
            prime * result + (firstname == null ? 0 : firstname.hashCode());
        result = prime * result + (id == null ? 0 : id.hashCode());
        result = prime * result + (name == null ? 0 : name.hashCode());
        return result;
    }

    /**
     * @see java.lang.Object#equals(java.lang.Object)
     */
    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (!(obj instanceof Driver)) {
            return false;
        }
        Driver other = (Driver) obj;
        if (cars == null) {
            if (other.cars != null) {
                return false;
            }
        } else {
            if (Persistence.getPersistenceUtil().isLoaded(cars)
                && Persistence.getPersistenceUtil().isLoaded(other.cars)) {
                if (!cars.equals(other.cars)) {
                    return false;
                }
            }
        }
        if (firstname == null) {
            if (other.firstname != null) {
                return false;
            }
        } else if (!firstname.equals(other.firstname)) {
            return false;
        }
        if (id == null) {
            if (other.id != null) {
                return false;
            }
        } else if (!id.equals(other.id)) {
            return false;
        }
        if (name == null) {
            if (other.name != null) {
                return false;
            }
        } else if (!name.equals(other.name)) {
            return false;
        }
        return true;
    }

    /**
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        return new StringBuilder().append("Driver [id=").append(id)
            .append(", name=").append(name).append(", firstname=")
            .append(firstname).append("]").toString();
    }
...

Als nächstes folgt die Klasse für die Autos. Das Auto kennt seinen Fahrer nicht, daher gibt es auch kein Attribut für den Fahrer in der Klasse.

  • Der primäre Schlüssel in Zeile 22 wird von Außen vorgegeben (Kennzeichen).
/**
 * Die Klasse eines Autos.
 * @author Frank W. Rahn
 */
@Entity
@Table(schema = "rahn")
@Access(AccessType.FIELD)
public class Car {

    /** Die Identität eines angemeldeten Autos. */
    @Id
    @Basic(optional = false)
    private String id;

    /** Der Typ des Autos. */
    @Basic(optional = false)
    private String type;
...

Es fehlt noch die XML Konfiguration für die Persistenz, die nicht sehr umfangreich ist.

  • In der Zeile 11 wird angegeben, dass die Anwendung die Transaktionen der Datenbank verwenden soll:
    EntityTransaction etx = entityManager.getTransaction();
    Im Gegensatz zu der Angabe JTA, wo der Transaktionsmanager des Java EE Containers verwendet würde:
    UserTransaction utx = (UserTransaction) (new InitialContext()).lookup("java:comp/UserTransaction");
<?xml version="1.0" encoding="UTF-8"?>

<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
        http://java.sun.com/xml/ns/persistence
            http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd
    ">

    <persistence-unit
        name="test-spring-drivers" transaction-type="RESOURCE_LOCAL" />

</persistence>

Das Data Access Object für die Fahrerverwaltung

Die Definition des konkreten Datenzugriffsobjekt für den Service der Fahrerverwaltung.

  • In Zeile 16 wird die Annotation @Repository verwendet. Durch diese Annotation wird diese Klasse zu einem Spring Bean mit einer Behandlung für Ausnahmen der Persistenzschicht. Diese Ausnahmen werden in eine Unterklasse von org.springframework.dao.DataAccessException übersetzt.
  • Ab der Zeile 32 wird eine Methode findAll() implementiert, die per @NamedQuery definierten Abfrage alle Instanzen dieser Klasse lädt.
/**
 * Ein DAO für den Fahrer.
 * @author Frank W. Rahn
 * @param <Entity> Die Klasse der Entität
 * @param <PrimaryKey> Die Klasse des primären Key
 */
@Repository
public class DriverDAO extends AbstractGenericJpaDAO<Driver, Long> {

    /**
     * {@inheritDoc}
     * @see GenericDAO#getPrimaryKey(java.lang.Object)
     */
    @Override
    public Long getPrimaryKey(Driver persistentObject) {
        return persistentObject.getId();
    }

    /**
     * Suche alle Fahrer.
     * @return alle Fahrer
     */
    public List<Driver> findAll() {
        return getEntityManager().createNamedQuery(Driver.FIND_ALL,
            Driver.class).getResultList();
    }

}

Die Implementierung des Services der Fahrerverwaltung

Die Standard-Implementierung der Fahrerverwaltung.

  • Durch die Annotation @Service("drivers") in Zeile 19 wird diese Implementierung als Spring Bean mit dem Namen drivers definiert.
  • In der Zeile 24 wird das Datenzugriffsobjekt DriverDAO verwendet.
  • In der Zeile 20 wird definiert, dass alle Methoden in einer Transaktion ausgeführt werden.
  • In Zeile 31 und 41 wird durch die Annotation angegeben, dass diese Methoden eine existierende Transaktion unterstützen, aber keine eigene Transaktion benötigen.
/**
 * Die Standard-Implementierung des Services {@link Drivers}.
 * @author Frank W. Rahn
 */
@Service("drivers")
@Transactional
public class StandardDrivers implements Drivers {

    @Autowired
    private DriverDAO driverDAO;

    /**
     * {@inheritDoc}
     * @see Drivers#getDrivers()
     */
    @Override
    @Transactional(propagation = Propagation.SUPPORTS)
    public List<Driver> getDrivers() {
        return driverDAO.findAll();
    }

    /**
     * {@inheritDoc}
     * @see Drivers#getDriver(Long)
     */
    @Override
    @Transactional(propagation = Propagation.SUPPORTS)
    public Driver getDriver(Long id) {
        return driverDAO.findByPrimaryKey(id);
    }

    /**
     * {@inheritDoc}
     * @see Drivers#createDriver(String, String)
     */
    @Override
    public Long createDriver(String name, String firstname) {
        Driver driver = new Driver();
        driver.setName(name);
        driver.setFirstname(firstname);

        return create(driver);
    }

    /**
     * {@inheritDoc}
     * @see Drivers#create(Driver)
     */
    @Override
    public Long create(Driver driver) {
        return driverDAO.create(driver);
    }

    /**
     * {@inheritDoc}
     * @see Drivers#save(Driver)
     */
    @Override
    public Driver save(Driver driver) {
        driverDAO.save(driver);
        return driver;
    }

    /**
     * {@inheritDoc}
     * @see Drivers#addCarToDriver(Long, Car)
     */
    @Override
    public Driver addCarToDriver(Long id, Car car) {
        Driver driver = driverDAO.findByPrimaryKey(id);
        driver.getCars().add(car);

        return save(driver);
    }

}

Im folgenden Bild wird der vollständige Service in einem UML-Klassendiagramm dargestellt.

Klassendiagramm dieses Beispiels

Spring mit JPA und Hibernate (© Frank Rahn)

Die Anwendung mit Logging

Sie ist wie in dem Beispiel Spring an einem einfachem Beispiel aufgebaut. Es ändert sich nur die Anwendung, die XML Konfiguration de/rahn/app/application.xml und der Starter bleiben gleich. Die Änderung an der Application sind hier dargestellt.

/**
 * Die Anwendung zum Aufrufen der Fahrerverwaltung.
 * @author Frank W. Rahn
 */
@Component
public class Application implements Runnable {

    private static final Logger logger = getLogger(Application.class);

    @Autowired(required = true)
    private Drivers drivers;

    /**
     * {@inheritDoc}
     * @see Runnable#run()
     */
    @Override
    public void run() {
        // Lege einen Fahrer an
        Long id = drivers.createDriver("Rahn", "Frank");
        logger.info("Einen Fahrer mit der Id '{}' angelegt", id);

        // Hole den Fahrer wieder
        Driver driver = drivers.getDriver(id);
        logger.info("Den Fahrer mit der Id '{}' geholt: {}", id, driver);

        // Lege ein Auto an
        Car car = new Car();
        car.setId("K-XX 4711");
        car.setType("Audi A6");
        driver = drivers.addCarToDriver(id, car);
        logger.info("Den Fahrer mit der Id '{}' geändert: {}", id, driver);

        // Alle Fahrer selektieren
        List<Driver> listDrivers = drivers.getDrivers();
        for (Driver driver2 : listDrivers) {
            logger.info(
                "Fahrer: Id '{}' Name: {} {}",
                new Object[] { driver2.getId(), driver2.getFirstname(),
                    driver2.getName() });
        }
    }

}

Die XML Konfiguration zum Einstieg in die Anwendung muss um die Datenbank- und die Transaktionsdefinitionen erweitert werden.

  • In der Zeile 27 wird auf die Konfiguration mit den Datenbank- und die Transaktionsdefinitionen im gleichen Verzeichnis verwiesen.
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
            http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd
    ">

    <description>
        Dieses ist die zentrale Konfiguration für die Anwendungen.
    </description>

    <!-- Enabling des AspectJ Support -->
    <aop:aspectj-autoproxy proxy-target-class="true" />

    <!-- Das Verwenden von allgemeinen Annotationen ermöglichen -->
    <context:annotation-config />

    <!-- Die projektspezifischen Konfigurationen laden -->
    <import resource="db.xml" />
    <import resource="classpath:/de/rahn/services/drivers/drivers.xml" />
    <import resource="classpath:/de/rahn/app/application.xml" />

</beans>

In der folgenden XML Konfiguration werden die Datenbank- und die Transaktionsdefinitionen vorgenommen. Hier wird erstmalig der p-Namesraum für das Setzen von Properties verwendet.

Die Syntax lautet dabei:
p:<Property-Name>="<Property-Wert>" oder p:<Property-Name>-ref="<Name der referenzierten Bean>"

  • Die Definitionen für den Datenbankzugriff (EntityManagerFactory) in den Zeilen 27 bis 34 und die Behandlung der Transaktionen in den Zeilen 37 bis 42.
  • In den Zeilen 22 bis 24 wird eine Datenbank gestartet und durch das angegebene Skript init.sql initialisiert.
    <!-- Starte die HSQL-Datenbank im Memory -->
    <jdbc:embedded-database id="dataSource" type="HSQL">
        <jdbc:script location="classpath:/META-INF/spring/init.sql" />
    </jdbc:embedded-database>

    <!-- Erzeuge die Persitence-Unit -->
    <bean id="entityManagerFactory"
        class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
        p:dataSource-ref="dataSource" p:persistenceUnitName="test-spring-drivers">
        <property name="jpaVendorAdapter">
            <1bean p:generateDdl="true"
                class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
        </property>
    </bean>

    <!-- Einen Transaktionmanager erzeugen -->
    <bean id="transactionManager"
        class="org.springframework.orm.jpa.JpaTransactionManager"
        p:entityManagerFactory-ref="entityManagerFactory" />

    <!-- Das Verwenden von Annotationen für die Transaktionen ermöglichen -->
    <tx:annotation-driven proxy-target-class="true" />

</beans>

Im folgendem Kasten wird das Skript init.sql dargestellt. Der SQL Dialekt ist HSQL (HyperSQL Database). HSQLDB ist eine vollständig in Java geschrieben relationale Datenbank.

create SCHEMA rahn;
create sequence DriverSEQ;
create table rahn.Driver (
    id bigint not null,
    firstname varchar(255),
    name varchar(255) not null,
    primary key (id));
create table rahn.Car (
    id varchar(255) not null,
    type varchar(255) not null,
    driver_id bigint not null,
    primary key (id));
alter table rahn.Car
    add constraint FK107B45ADEE2FE foreign key (driver_id)
        references rahn.Driver;
insert into rahn.Driver (firstname, name, id)
    values ('Martin', 'Rahn', next value for DriverSEQ);

Der Unit Test

Zunächst erstellen wir einen Test für die Klasse DriverDAO.

  • In der Zeile 25 wird auf die benötigten XML Konfigurationen von Spring verwiesen. Das ist eine spezielle XML Konfiguration für den Test /META-INF/spring/context-test.xml, sie beinhaltet die Konfigurationen für den Datenbankzugriff, und die Konfiguration für die Fahrerverwaltung ../drivers.xml.
/**
 * Die Testklasse für {@link DriverDAO}.
 * @author Frank W. Rahn
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "/META-INF/spring/context-test.xml",
    "../drivers.xml" })
@Transactional
public class DriverDAOTest {

    @Autowired
    private DriverDAO driverDAO;

    /**
     * Test method for {@link DriverDAO#getPrimaryKey(Driver)}.
     */
    @Test
    public void testGetPrimaryKey() {
        Driver driver = new Driver();
        driver.setId(new Long(4711));

        Long id = driverDAO.getPrimaryKey(driver);
        assertThat("Primärer Key unterschiedlich", driver.getId(), is(id));
    }

    /**
     * Test method for {@link DriverDAO#findAll()}.
     */
    @Test
    public void testFindAll() {
        List<Driver> drivers = driverDAO.findAll();
        assertThat("kein Ergebnis geliefert", drivers, notNullValue());
        assertThat("Anzahl der Treffer ungleich", drivers.size(), is(1));

        Driver driver = drivers.get(0);
        assertThat("id ungleich", driver.getId(), is(0L));
        assertThat("firstname ungleich", driver.getFirstname(), is("Martin"));
        assertThat("name ungleich", driver.getName(), is("Rahn"));
    }

    /**
     * Test method for 
     * {@link de.rahn.db.jpa.dao.AbstractGenericJpaDAO#create(Object)}.
     */
    @Test
    public void testCreate() {
        Driver driver = new Driver();
        driver.setName("Rahn");
        driver.setFirstname("Frank");

        Long id = driverDAO.create(driver);
        assertThat("keine id geliefert", id, notNullValue());
        assertThat("ungleiche id", driver.getId(), is(id));

        Driver driver2 = driverDAO.findByPrimaryKey(id);
        assertThat("doch nicht gespeichert", driver2, notNullValue());
        assertThat("ungleicher Fahrer", driver2, sameInstance(driver));
    }

    /**
     * Test method for
     * {@link de.rahn.db.jpa.dao.AbstractGenericJpaDAO#save(Object)}.
     */
    @Test
    public void testSave() {
        Driver driver = driverDAO.findByPrimaryKey(0L);
        assertThat("kein Fahrer gefunden", driver, notNullValue());

        driver.setFirstname("Peter");
        driverDAO.save(driver);

        Driver driver2 = driverDAO.findByPrimaryKey(0L);
        assertThat("kein Fahrer gefunden", driver2, notNullValue());
        assertThat("ungleicher Fahrer", driver2, sameInstance(driver));
    }

    /**
     * Test method for
     * {@link de.rahn.db.jpa.dao.AbstractGenericJpaDAO#remove(Object)}.
     */
    @Test
    public void testRemoveEntity() {
        List<Driver> drivers = driverDAO.findAll();
        assertThat("Anzahl der Treffer ungleich", drivers.size(), is(1));
        Driver driver = drivers.get(0);

        driverDAO.remove(driver);

        drivers = driverDAO.findAll();
        assertThat("Anzahl der Treffer ungleich", drivers.size(), is(0));
    }

    /**
     * Test method for
     * {@link de.rahn.db.jpa.dao.AbstractGenericJpaDAO#findByPrimaryKey(Serializable)}
     * .
     */
    @Test
    public void testFindByPrimaryKey() {
        Driver driver = driverDAO.findByPrimaryKey(0L);
        assertThat("kein Ergebnis geliefert", driver, notNullValue());
        assertThat("id ungleich", driver.getId(), is(0L));
        assertThat("firstname ungleich", driver.getFirstname(), is("Martin"));
        assertThat("name ungleich", driver.getName(), is("Rahn"));
    }

    /**
     * Test method for
     * {@link de.rahn.db.dao.AbstractGenericDAO#remove(Serializable)}.
     */
    @Test
    public void testRemovePrimaryKey() {
        driverDAO.remove(0L);

        List<Driver> drivers = driverDAO.findAll();
        assertThat("kein Ergebnis geliefert", drivers, notNullValue());
        assertThat("Anzahl der Treffer ungleich", drivers.size(), is(0));
    }

}

Für die allgemeinen Spring Beans wurde eine spezielle XML Konfiguration /META-INF/spring/context-test.xml erstellt. Sie lädt nur die Konfigurationen für den Datenbankzugriff.

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd
    ">

    <description>
        Dieses ist die zentrale Konfiguration für die Tests.
    </description>

    <!-- Das Verwenden von allgemeinen Annotationen ermöglichen -->
    <context:annotation-config />

    <!-- Die projektspezifischen Konfigurationen laden -->
    <import resource="db.xml" />

</beans>

Jetzt fehlt nur noch der Unit Test für den Service der Fahrerverwaltung StandardDriver.

/**
 * Die Testklasse für {@link StandardDrivers}.
 * @author Frank W. Rahn
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "/META-INF/spring/context-test.xml",
    "../drivers.xml" })
@Transactional
public class StandardDriversTest {

    /** CAR_TYPE */
    private static final String CAR_TYPE = "Audi A6";

    /** CAR_ID */
    private static final String CAR_ID = "K-XX 4711";

    @Autowired
    private Drivers drivers;

    /**
     * Test method for {@link StandardDrivers#getDriver(Long)}.
     */
    @Test
    public void testGetDriver() {
        Driver driver = drivers.getDriver(0L);
        assertThat("kein Fahrer geliefert", driver, notNullValue());
        assertThat("id ungleich", driver.getId(), is(0L));
        assertThat("firstname ungleich", driver.getFirstname(), is("Martin"));
        assertThat("name ungleich", driver.getName(), is("Rahn"));
    }

    /**
     * Test method for {@link StandardDrivers#createDriver(String, String)}.
     */
    @Test
    public void testCreateDriver() {
        Long id = drivers.createDriver("Rahn", "Frank");
        assertThat("keine id geliefert", id, notNullValue());

        Driver driver = drivers.getDriver(id);
        assertThat("doch nicht gespeichert", driver, notNullValue());
        assertThat("ungleiche id", driver.getId(), is(id));
        assertThat("firstname ungleich", driver.getFirstname(), is("Frank"));
        assertThat("name ungleich", driver.getName(), is("Rahn"));
    }

    /**
     * Test method for {@link StandardDrivers#addCarToDriver(Long, Car)}.
     */
    @Test
    public void testAddCarToDriver() {
        Car car = new Car();
        car.setId(CAR_ID);
        car.setType(CAR_TYPE);
        Driver driver = drivers.addCarToDriver(0L, car);
        assertThat("doch nicht gespeichert", driver, notNullValue());
        assertThat("id ungleich", driver.getId(), is(0L));
        assertThat("firstname ungleich", driver.getFirstname(), is("Martin"));
        assertThat("name ungleich", driver.getName(), is("Rahn"));
        assertThat("anzahl Autos ungleich", driver.getCars().isEmpty(),
            not(true));
        car = driver.getCars().iterator().next();
        assertThat("id ungleich", car.getId(), is(CAR_ID));
        assertThat("type ungleich", car.getType(), is(CAR_TYPE));
    }

}

Hier noch die entsprechende Erfolgsmeldung.

Die Erfolgsmeldung von JUnit

Die Erfolgsmeldung von JUnit (© Frank Rahn)

Der Quellcode und Download des Beispiels

Quellcode ansehen bei GitHub:
Spring mit JPA und Hibernate

Download einer ZIP-Datei von GitHub:
Spring mit JPA und Hibernate

Die Maven Befehl

Eclipse Konfiguration neu erzeugen: $ mvn eclipse:clean eclipse:eclipse

Anwendung bauen: $ mvn clean install

Anwendung ausführen: $ mvn initialize exec:exec

Update: Criteria API

Es wurde in der pom.xml ein Maven-Plugin für die Generierung des Metamodels der Criteria API aufgenommen. Dieses Metamodel wird in das Verzeichnis target/generated-sources/apt generiert. Mit der Criteria API können Datenbankabfragen objektorientiert im Quellcode programmiert werden.

Nachfolgenden ist die Maven Konfiguration für die Erzeugung der Criteria API mit dem JPA 2 Metamodel Generator von Hibernate dargestellt.

    <plugin>
        <groupId>org.bsc.maven</groupId>
        <artifactId>maven-processor-plugin</artifactId>
        <version>22.2.1</version>
        <executions>
            <execution>
                <goals>
                    <goal>process</goal>
                </goals>
            </execution>
        </executions>
        <configuration>
            <optionMap>
                <addGeneratedAnnotation>true</addGeneratedAnnotation>
                <addSuppressWarningsAnnotation>true</addSuppressWarningsAnnotation>
                <debug>true</debug>
            </optionMap>
            <processors>
                <processor>org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor</processor>
            </processors>
        </configuration>
        <dependencies>
            <dependency>
                <groupId>org.hibernate</groupId>
                <artifactId>hibernate-jpamodelgen</artifactId>
                <version>${hibernate-jpamodelgen-version}</version>
            </dependency>
        </dependencies>
    </plugin>
...

Zusätzlich wurde ein Template, nach dem Muster von Spring, erzeugt. Dadurch kann das abstrakte und generische JPA-Datenzugriffsobjekt die Erzeugung der Factory-Klassen übernehmen.

/**
 * Ein typisches Spring-Template, dass die Erzeugung einiger notwendigen Factory
 * Klassen zur Erzeugung von Queries auf Basis der Criteria API an das
 * {@link AbstractGenericJpaDAO} deligiert.
 * @author Frank W. Rahn
 * @param <Entity> Die Klasse der Entität
 * @see AbstractGenericJpaDAO
 */
public interface CriteriaQueryTemplate<Entity> {

    /**
     * Diese Methode wird durch das {@link AbstractGenericJpaDAO} ausgeführt und
     * stellt einige Standardkomponenten der Criteria API zu Verfügung.
     * @param builder der {@link CriteriaBuilder}
     * @param query die an die Entität gebundene Abfrage
     * @param rootEntity die Projektionsvariable der FROM-Klausel
     */
     void doBuild(CriteriaBuilder builder, CriteriaQuery<Entity> query,
        Root<Entity> rootEntity);

}

Das abstrakte und generische JPA-Datenzugriffsobjekt AbstractGenericJpaDAO, wird um die Methode buildQuery() erweitert.

    /**
     * Erzeuge über die Criteria API eine JPA Query.
     * @param template der Builder
     * @return eine ausführbare JPA Query
     */
    protected TypedQuery<Entity> buildQuery(
        CriteriaQueryTemplate<Entity> template) {

        // Erzeuge eine Builder
        CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();

        // Erzeuge die Query
        CriteriaQuery<Entity> criteriaQuery =
            criteriaBuilder.createQuery(entityClass);

        // Erzeuge die Referenz zur Hauptentität
        Root<Entity> rootEntity = criteriaQuery.from(entityClass);

        // Führe den Build durch
        template.doBuild(criteriaBuilder, criteriaQuery, rootEntity);

        // Erzeuge eine ausführbare JPA Query
        return entityManager.createQuery(criteriaQuery);
    }
...

Das Datenzugriffsobjekt der Fahrer stellt die Methode findByName() bereit.

    /**
     * Suche alle Fahrer mit dem Namen.
     * @param name der Name des Fahrers
     * @return die gefundenen Fahrer
     */
    public List<Driver> findByName(final String name) {
        return buildQuery(new CriteriaQueryTemplate<Driver>() {

            @Override
            public void doBuild(CriteriaBuilder builder,
                CriteriaQuery<Driver> query, Root<Driver> rootEntity) {
                // Erzeuge eine logische Ausdruck
                Predicate predicate =
                    builder.equal(rootEntity.get(Driver_.name), name);

                // Definiere die Abfrage
                query.select(rootEntity).where(predicate).distinct(true);
            }
        }).getResultList();
    }
...

Zum Abschluss darf natürlich der Test nicht fehlen.

    /**
     * Test der Criteria API.
     */
    @Test
    public void testCriteriaAPI() {
        List<Driver> drivers = driverDAO.findByName("Rahn");
        assertThat("keine Ergebnis geliefert", drivers, notNullValue());
        assertThat("Anzahl der Treffer ungleich", drivers.size(), is(1));

        Driver driver = drivers.get(0);
        assertThat("id ungleich", driver.getId(), is(0L));
        assertThat("firstname ungleich", driver.getFirstname(), is("Martin"));
        assertThat("name ungleich", driver.getName(), is("Rahn"));
    }
...

Update am 28.09.2012

Es wurden folgende Änderungen an allen Projekten vorgenommen.

  1. Die Projekte wurden aus meinem lokalen Apache Subversion Repository in meine öffentlichen GitHub Repositories verschoben.
    • Projekt test-spring-simple
    • Projekt test-spring-jpa
    • Projekt test-spring-web
  2. Die folgenden typischen Anpassungen an Git wurden an allen Projekten durchgeführt.
    • .directory gelöscht
    • .gitignore hinzugefügt
    • README.md hinzugefügt
    • COPYRIGHT.md hinzugefügt
    • In jedem Repository wurde für jeden Beitrag der Serie ein Branch angelegt.
      • develop-spring-an-einem-einfachen-beispiel
      • develop-spring-mit-aop
      • develop-spring-mit-jpa-und-hibernate
      • develop-spring-mit-einer-einfachen-webanwendung
      • develop-spring-mit-einer-webanwendung-mit-jpa-und-validierung
      • develop-spring-security-mit-einer-webanwendung
      • develop-spring-mit-restful-webservice

Updates von 06.10.2012 bis zum 14.10.2012

Es wurden folgende Änderungen an allen Projekten vorgenommen.

  1. Aktualisierung des OpenJDK auf die Version 1.7.0.
  2. Aktualisierung der Entwicklungsumgebung Eclipse auf die Version 4.2.1.
    Die benötigten Plugins aus dem Eclipse Marketplace:
    • EGit – Git Team Provider, Version 2.1.0
    • GitHub Mylyn Connector, Version 2.0
    • Eclipse m2e – Maven support in Eclipse IDE
    • Spring IDE, Version 3.0.0.RELEASE
    • AspectJ Development Tools (AJDT), Version 2.2.0
    • Markdown Text Editor, Version 0.9.0
  3. Aktualisierung der Datei pom.xml:
    • Anpassungen an die OpenJDK Version
    • GitHub Einträge (SCM und URL) hinzugefügt
    • Aktualisierung der Libraries auf aktuellerer Versionen (z. B. Spring Version 3.1.2.RELEASE, Hibernate Version 4.1.7.Final, JUnit Version 4.10, mockito Version 1.9.0, …)
      Die genaueren Versionen bitte aus den jeweiligen pom.xml auf GitHub entnehmen.

Weitere Beiträge dieser Serie. [verstecken]

  • Einführung in das Spring Framework
    • Spring an einem einfachem Beispiel
    • Spring mit AOP
    • Spring mit JPA und Hibernate
    • Spring mit einer einfachen Webanwendung
    • Spring mit einer Webanwendung mit JPA und Validierung
    • Spring Security mit einer Webanwendung
    • Spring mit RESTful Webservice
    • Spring und Stored Procedures mit User-defined Types

  • Über
  • Letzte Artikel
Frank Rahn
Frank Rahn
Frank Rahn ist Softwarearchitekt. Er unterstützt bei der Konzeption von Softwarearchitekturen mit Java-Technologie. Folge Sie ihm auf Facebook oder Twitter.

Benötigen Sie Unterstützung? Kontaktieren Sie ihn.

Hat Ihnen dieser Beitrag gefallen? Wir würden uns über Ihren Kommentar freuen! Bitte verwenden Sie Ihren bürgerlichen Namen.
Frank Rahn
Letzte Artikel von Frank Rahn (Alle anzeigen)
  • Wer ist der optimale Java Bean Mapper? - Freitag, 22. September 2023
  • Spring Boot Webanwendung: Die ersten Schritte (Tutorial) - Montag, 28. März 2016
  • Mainframe-Zugriff via Java - Sonntag, 04. Mai 2014
0 Kommentare/von Frank Rahn
Schlagworte: Architektur, CRUD, DAO, DI, IoC, Java, Java SE, JPA, Open Source Software, POJO, Serviceorientierte, Spring, SQL, XML
Eintrag teilen
  • Teilen auf X
  • Teilen auf WhatsApp
  • Teilen auf LinkedIn
  • Per E-Mail teilen
  • Teilen auf Xing
https://www.frank-rahn.de/wp-content/uploads/Spring-mit-JPA-und-Hibernate.png 1166 1840 Frank Rahn /wp-content/uploads/logo.png Frank Rahn2011-08-03 21:06:072025-02-02 20:34:01Spring mit JPA und Hibernate (Tutorial)
Das könnte Dich auch interessieren
Spring Boot Webanwendung Spring Boot Webanwendung: Die ersten Schritte (Tutorial)
Die Stored Procedure "searchPersons" mit User-defined Types (UDT) Stored Procedure mit User-defined Types unter PostgreSQL
Der Service der Fahrerverwaltung Spring mit einer Webanwendung mit JPA und Validierung (Tutorial)
Klassendiagramm dieses Beispiels Spring mit AOP (Tutorial)
Klassendiagramm dieses Beispiels Spring an einem einfachem Beispiel (Tutorial)
Grobe Übersicht üder den Spring Framework Container Einführung in das Spring Framework
0 Kommentare

Hinterlasse einen Kommentar

An der Diskussion beteiligen?
Hinterlasse uns deinen Kommentar!

Schreibe einen Kommentar Antwort abbrechen

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Ihre E-Mail-Adresse wird nicht veröffentlicht. Ihr Kommentar wird verschlüsselt an meinen Server gesendet. Erforderliche Felder sind mit * markiert.

Weitere Informationen und Widerrufshinweise finden Sie in meiner Datenschutzerklärung.

Wollen Sie kein neuen Beiträge mehr verpassen?
Dann abonnieren Sie bitte meinen Newsletter.
Meinen Newsletter abonnieren

Themen

  • Wer ist der optimale Java Bean Mapper?
  • Einführung in das Spring Framework, Boot, Batch, Data, REST, Security, Web, …
  • Franks aktueller IT-Werkzeugkasten
  • Git, GitHub, EGit, …

Navigation

  • Buchtipps
  • Newsletter
  • Weblinks
Search Search

Werbung

  • JProfiler
Beliebt
  • Das Klassendiagramm für den Java Bean Mapper Test am Beispiel "ByHand"
    Wer ist der optimale Java Bean Mapper?Freitag, 22. September 2023 - 20:59 Uhr
  • Das offizielle Logo von EGit
    GitHub mit Eclipse (EGit)Freitag, 26. Oktober 2012 - 16:15 Uhr
  • Grobe Übersicht üder den Spring Framework Container
    Einführung in das Spring FrameworkSonntag, 01. Mai 2011 - 18:30 Uhr
  • Dieses Bild zeigt meinen IT-Werkzeugkasten
    Franks aktueller IT-WerkzeugkastenMittwoch, 15. August 2012 - 19:36 Uhr
  • Die Stored Procedure "searchPersons" mit User-defined Types (UDT)
    Spring und Stored Procedure mit User-defined Types (Tut...Freitag, 26. Oktober 2012 - 21:45 Uhr
Schlagworte
Annotations AOP Architektur Autorisierung Cookies CRUD DAO DI Git HTML HTTP IoC Java Java EE Java SE JPA JSR Linux MVC Open Source Software PDF POJO REST (RESTful) ROCA Self-contained Systems Serviceorientierte Shell Sicherheit (Security) SOAP Spring SQL SVN Test Toolchain URI URL URN User-defined Type Versionsverwaltung VPN Webservice WS-* WSDL XML XML-Schema

Blogarchiv

Links

Mastodon
Twitter
LinkedIn
Xing
GitHub

Lizenz

Creative Commons Lizenzvertrag Die Texte (nicht Bilder) von Frank Rahn stehen unter einer Creative Commons Namensnennung - Keine Bearbeitungen 4.0 Deutschland Lizenz.

Affiliate-Links

Die mit (*) gekennzeichnete Links sind sogenannte Affiliate-Links. Kommt über einen solchen Link ein Einkauf zustande, werde ich mit einer Provision beteiligt. Für Sie entstehen dabei keine Mehrkosten. Wo, wann und wie Sie ein Produkt kaufen, bleibt natürlich Ihnen überlassen.

Blogkategorien

Copyright © Frank W. Rahn
  • Impressum / HaftungsausschlussDie notwendigen gesetzlichen Angaben dieser Webseite von Frank Rahn
  • DatenschutzerklärungDie Datenschutzerklärung von Frank Rahn
  • NewsletterKeine neuen Beiträge mehr verpassen!
  • BildnachweisDer komplette Bildnachweis von Frank Rahn
Link to: Spring mit AOP (Tutorial) Link to: Spring mit AOP (Tutorial) Spring mit AOP (Tutorial)Klassendiagramm dieses Beispiels Link to: Spring mit einer einfachen Webanwendung (Tutorial) Link to: Spring mit einer einfachen Webanwendung (Tutorial) Die ApplicationContexte einer einfachen Spring WebanwendungSpring mit einer einfachen Webanwendung (Tutorial)
Nach oben scrollen Nach oben scrollen Nach oben scrollen

Wir setzen auf unserer Webseite verschiedene Arten von Cookies ein, die auf Ihrem Gerät gespeichert werden. Einige dieser Cookies sind für die einwandfreie Funktion der Webseite notwendig, während andere Cookies Ihnen ein besseres Besuchererlebnis bieten.

DatenschutzerklärungImpressumAlle Cookies akzeptierenKeine Cookies akzeptierenIndividuelle Cookie-Einstellungen vornehmen

Cookie- und Datenschutzeinstellungen



Wie wir Cookies verwenden

Wir setzen auf unserer Webseite verschiedene Arten von Cookies ein, die auf Ihrem Gerät gespeichert werden.

Einige dieser Cookies sind für die einwandfreie Funktion der Webseite notwendig, während andere Cookies Ihnen ein besseres Besuchererlebnis bieten.

Klicken Sie links auf die verschiedenen Reitern, um mehr zu erfahren. Sie können auch einige Cookie-Einstellungen individuell anpassen. Beachten Sie, dass das Blockieren einiger Cookies die einwandfreie Funktion unserer Webseite beeinträchtigt.

Technisch notwendige Cookies

Diese Cookies sind unbedingt erforderlich, denn sie ermöglichen grundlegende Funktionen und sind für die einwandfreie Funktion der Webseite erforderlich.

Sie können diese Cookies jederzeit blockieren oder löschen, indem Sie Ihre Browsereinstellungen ändern und die Blockierung aller Cookies auf dieser Webseite erzwingen. Leider werden Sie dann immer wieder gefragt, ob Sie Cookies akzeptieren oder ablehnen wollen, wenn Sie unsere Webseite erneut besuchen.

Wir setzen die Cookies aviaPrivacyEssentialCookiesEnabled, aviaPrivacyMustOptInSetting, aviaPrivacyRefuseCookiesHideBar und aviaCookieConsent ein, um Ihre individuellen Cookie-Einstellungen zu speichern. Diese Informationen geben wir an keinen Drittanbietern weiter.

Diese Cookies haben eine Laufzeit von einem Jahr, dann müssen Sie die Einstellungen wiederholen.

Die VG WORT setzt das Sitzungscookie srp zur Messung von Zugriffen auf Texten, um die Kopierwahrscheinlichkeit des Textes zu erfassen. Damit partizipieren ich an den Ausschüttungen der VG WORT, welche die gesetzliche Vergütung für die Nutzungen urheberrechtlich geschützter Werke gemäß § 53 UrhG sicherstellen. Das Cookie wird dazu verwendet, um den Nutzer zu identifizieren und ggf. Daten mehrerer Aufrufe von Texten miteinander verknüpfen zu können.

Nach Angaben der VG WORT stellt das eingesetzte Verfahren sicher, dass einzelne Nutzer oder ihr Leseverhalten nicht ermittelbar sind, wenn die Anzahl der Textaufrufe gezählt wird. Alle von der VG Wort erfassten Daten werden sofort sicher verschlüsselt. Der Einsatz des Zählpixels wurde durch das Bayerische Landesamt für Datenschutzaufsicht begutachtet und als datenschutzkonform bewertet.

Datenschutzerklärung der VG WORT

Marketing-Cookies

Die Marketing-Cookies werden von Drittanbietern oder Publishern, wie z. B. Google Analytics, verwendet, um personalisierte Werbung anzuzeigen. Sie tun dies, indem sie Besucher über Webseiten hinweg verfolgen (Tracking).

Wir setzen keine Marketing-Cookies ein.

Datenschutzbestimmungen

Sie können unsere Cookies und Datenschutzeinstellungen im Detail in unserer Datenschutzerklärung nachlesen.

Cookie-Einstellungen übernehmenKeine Cookies akzeptieren
Nachrichtenleiste öffnen Nachrichtenleiste öffnen Nachrichtenleiste öffnen