Spring JPA und Repository

BetaUser

Mitglied
Hallo Zusammen,

Ich bin grade Neu beigetreten und habe eine allgemeine Frage zu Spring Boot und JPA.
Ich benötige wohl eine grundlegende Erklärung bei der Nutzung von JPA und Spring.

Ich habe eine Entity klasse diese ist mein Abbild der Datenbank sein soll.(oder so aber egal da liegt nicht das Problem)

Als nächstes benötige ich ein Repository um das Abbild mit der DB zu verbinden.

Dazu benötige ich die application.properties die die Verbindung zur DB ermöglichen. Mithilfe der JPA Information

Java:
spring.datasource.url=jdbc:sqlserver://XXX.XXX.XXX.XXX:XXXX;databaseName=XXXXXXX
spring.datasource.username=XXX
spring.datasource.password=XXX
spring.datasource.driverClassName=com.microsoft.sqlserver.jdbc.SQLServerDriver
spring.jpa.show-sql=true
spring.jpa.hibernate.dialect=org.hibernate.dialect.SQLServer2012Dialect
spring.jpa.hibernate.ddl-auto = none

Ab diesen Zeitpunkt finde ich nur noch Anleitung die, die Initialisierung komplett ignorieren und nur noch mit

Code:
@ Autowired
Repository repository;

Arbeiten.

Im laufe meiner Recherchen habe ich heraus gefunden das ich eine Bean brauche,
doch was die macht und wo die hin kommt kein plan

vielleicht kann mir hier einer helfen
 
In Spring Boot wird sehr viel automatisch gemacht. Diverse Elemente werden automatisch erzeugt und dabei werden Abhängigkeiten automatisch eingefügt.

Das ist sehr hilfreich, denn das nimmt einem sehr viel Arbeit ab.

Die Frage wäre daher erst einmal, was Deine Applikation denn machen soll.

Wenn Du überhaupt keinen solchen Ansatzpunkt hast sondern nur Teile von Spring innerhalb deiner "normalen" Java Applikation nutzen willst, dann könntest Du in der main ansetzen:

Du hast ja den Aufruf von SpringApplication.run - dieser gibt einen ApplicationContext zurück:

Wenn Du dir den in einer Variable merkst, könntest Du dir entsprechende Komponenten ("Beans") geben lassen, also z.B. Dein Repository. getBean wäre da die Methode Deiner Wahl (von der es einige gibt - einfach mal alle ansehen um einen Überblick zu bekommen):

Aber bei den Spring Applikationen, die ich bisher so kenne, braucht es dies nicht. Daher wäre die Frage wichtig, die ich oben gestellt habe: Was willst Du / was soll Deine Applikation denn machen?
 
In der Phase wo ich im Augenblick stehe möchte ich meine Entity abfragen um die daten aus der DB zubekommen. Es soll ein kleiner ETL Prozess werden die es mir erlauft. Einen Artikel abzurufen und diesen auszulesen.

Die Entitys sind von intellij erstellt worden mit Hilfe des persistent mapping. Das ist meine Grund lagen.

Dann habe ich das Repository erstellt

Java:
import org.springframework.data.jpa.repository.JpaRepository;

public interface TArtikelRepository extends JpaRepository {
}

Zur weiter Verarbeitung einen Service
Code:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;

public class TArtikelService {
    @Autowired
    private TArtikelRepository tArtikelRepository;

    public Iterable<TArtikelEntity> getAllArtikel() {
        return tArtikelRepository.findAll();
    }

    public Optional getTArtikel(Long ID) {

        return tArtikelRepository.findById(ID);

    }
}


Zu guter Letzt einen Controller der die abfrage nach meinen Wünschen vorab verarbeiten soll

Code:
import org.springframework.beans.factory.annotation.Autowired;

import java.util.ArrayList;
import java.util.List;

public class TArtikelController {

    @Autowired
    TArtikelService artikelService;

    public List<TArtikelEntity> getAllArtikel() {
        List<TArtikelEntity> result = new ArrayList<>();
        artikelService.getAllArtikel().forEach(result::add);
    return result;
    }
    public TArtikelEntity getById(Long id){
        return (TArtikelEntity) artikelService.getTArtikel(id).get();

    }
}

Die Main Klasse sieht so aus
Code:
import java.util.ArrayList;
import java.util.List;

@SpringBootApplication
public class PreisUpdateApplication {

    public static void main(String[] args) {
        TArtikelController tArtikelController = new TArtikelController();
        System.out.println(tArtikelController.getById(2000L).getcAsin());
        SpringApplication.run(PreisUpdateApplication.class, args);
    }

}
 
Korrekt, weil du Klassen - sobald du Autowired drin stehen hast - nicht mehr per new erzeugen darfst, sondern alles Spring überlassen musst.

Das heißt, du musst dafür sorgen, dass deine Klassen (z.B. TArtikelController) auch Spring-Components sind. Also z.B. ein @Service dran (auch an TArtikelService) . Da du keine Web-Anwendung oder ähnliches baust, musst du dafür Sorgen, dass:
  • Zuerst Spring deine gesamten Beans scannt und seinen Context initialisiert
  • Du danach dann die Beans über Spring-Mechanismen laden kannst.

Ein Beispiel ist hier: https://www.baeldung.com/spring-boot-console-app
Deine sollte dann PreisUpdateApplication sollte dann CommandLineRunner implementieren und deinen Code musst du in die Run-Methode verschieben. Anstelle den TArtikelController mittels new zu erzeugen, ergänzt du in PreisUpdateApplication ein Feld für TArtikelController, was ebenfalls @Autowired bekommt.
 
Da einfach mal von meiner Seite ein paar Informationen / Ansätze

a) Ich gehe erst einmal davon aus, dass Du ein Projekt hast, bei dem Spring Boot korrekt eingebunden wurde. Das ist in der Regel am einfachsten (vor allem am Anfang) mit dem Spring Initializr - also start.spring.io wäre da deine Anlaufstelle (So Deine IDE den nicht auch anbietet).

b) Der Start einer Spring Applikation ist in der Regel auch immer gleich und folgt diesem Schema:
Java:
@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

}
(So als minimalen Rahmen - wirst Du aber bei dem Projekt so schon direkt haben!)

Da Du nun nichts haben willst, das direkt aktiv ist, müssen wir nun etwas basteln. Ehe wir damit anfangen machen wir Deine Dinge erst einmal zu entsprechenden Spring Komponenten:

c) Du hast einen Service - dann gib dem einfach einmal die @Service Annotation (Siehe dazu ggf. https://www.baeldung.com/spring-component-repository-service um da paar Annotations zu unterscheiden)

Spring Boot scannt am Anfang alle Klassen in dem Namespace der Application Klasse (und drunter) und schaut nach diesen Annotations. Ohne diese Annotation interessiert sich Spring nicht weiter für die Klasse aber das wollen wir.

d) Dein Controller muss da jetzt auch bekannt gemacht werden. Das ist aber jetzt kein RestController oder so. Also nehmen wir jetzt einfach einmal @Component. Das ist halt einfach eine ganz universelle Komponente (Siehe den genannten Link!)

Nachdem Du nun dafür gesorgt hast, dass diese Klassen von Spring erkannt und verwaltet werden können, können wir nun Deine Applikation etwas nehmen:

Es wurde schon erwähnt: Selbst erstellen darfst Du die Komponenten nicht. Statt dessen sollst Du den ApplicationContext nutzen. Also kannst Du hin gehen und das entsprechend umschreiben:
Code:
    public static void main(String[] args) {
        ApplicationContext context = SpringApplication.run(Application.class, args);
        TArtikelController tArtikelController = context.getBean(TArtikelController.class);
        System.out.println(tArtikelController.getById(2000L).getcAsin());
    }

Hier noch etwas am Rande: Das sieht etwas nach einem Unit-Test statt nach einer Applikation aus. Du bist halt noch dabei, Teile der Applikation zu entwickeln aber Du willst diese direkt austesten. Das kannst Du natürlich auch machen.

Dies hat den Vorteil:
- Bei einem entsprechenden Unit Test musst du kaum etwas machen. Du sagst halt, dass es ein SpringTest ist:
@SpringBootTest(classes = Application.class)
als Annotation auf der Test Klasse.

Und schon kannst Du in der Test Klasse auch DI nutzen mit @Autowired Annotations.

Ich hoffe, dass dies etwas geholfen hat um etwas weiter zu kommen.
 
Wenn ich das richtig verstanden habe, nutze ich die Annotationen nicht richtig und
die ich benötige eine Komponenten-Suchfunktion @ComponentScan(basePackages = "de.XXX") richtig?

@LimDul muss Spring immer eine Web oder CommandLineRunner Applikation sein?

Gruß Beta
 
Ich habe das jetzt so angepasst:
Service:
Java:
@Service
public class TArtikelService {
    @Autowired
    private TArtikelRepository tArtikelRepository;

    public Iterable<TArtikelEntity> getAllArtikel() {
        return tArtikelRepository.findAll();
    }

    public Optional getTArtikel(Long ID) {

        return tArtikelRepository.findById(ID);

    }
}

und der Controller
Code:
    @Autowired
    TArtikelService artikelService;

    public List<TArtikelEntity> getAllArtikel() {
        List<TArtikelEntity> result = new ArrayList<>();
        artikelService.getAllArtikel().forEach(result::add);
    return result;
    }
    public TArtikelEntity getById(Long id){
        return (TArtikelEntity) artikelService.getTArtikel(id).get();

    }
}
Und Die Main Mit ComponentScan
Code:
@SpringBootApplication
@ComponentScan(basePackages = "de.XXX.PreisUpdate.*")
public class PreisUpdateApplication {

    public static void main(String[] args) {
        TArtikelController tArtikelController = new TArtikelController();
        System.out.println(tArtikelController.getById(2000L).getcAsin());
        SpringApplication.run(PreisUpdateApplication.class, args);
    }

}
Gleicher Fehler wie vorher
 
Also der ComponentScan ist unnötig, so die Klasse im gleichen Namespace oder in einem Namespace "unterhalb" der anderen Klassen ist.

Und Du hast erneut eine Instanz des Controllers selbst erstellt. Das ist falsch. Du musst auch den Controller zu einem Bean machen (Punkt d in meinem anderen Post). (Hier nebenbei noch erwähnt: Und zu dem Zeitpunkt "läuft Spring Boot" auch noch nicht. Denn das hast Du ja ans Ende gesetzt. Du hast also die Klassen zwar im Classpath, Deine Klassen haben irgendwelche Annotations aber da ist noch nichts weiter im Programmablauf geschehen!)

Und dann musst Du Dir die Instanz 2von Spring erzeugen" lassen. Denn nur dann werden auch die Dependencies eingefügt. Wie das aussehen kann, hatte ich ja auch aufgezeigt.

Dann sollte es funktionieren - wobei ich im Augenblick nicht sicher bin, ob nicht noch weitere Dinge notwendig waren (Das hatte ich in der Vergangenheit einmal gemacht ehe ich dann Spring Boot aber auch wieder rausgeworfen hatte. Spring Boot nur für ein DI Framework war doch etwas Overkill damals)

Aber generell solltest Du es in jeder Java Anwendung einbauen können. Es kann also auch eine Swing oder JavaFX Anwendung sein. Aber in der Regel sind es wohl Backend Anwendungen, die dann nach außen Services anbieten wie Webseiten, WebServices und Co. Das ist aber lediglich ein Gefühl von mir - ich kenne keine statistischen Auswertungen und es gibt keinen technischen Grund, es nicht auch in andere Java Anwendungen einzubauen.
 
Nur noch einmal ganz schnell selbst ausprobiert, ob das beschriebene wirklich schon ausreicht.

1. start.spring.io -> Direkt Projekt erzeugen ohne weitere Abhängigkeiten und so.

2. minimalen Service erstellt:
Java:
package com.example.demo;

import org.springframework.stereotype.Service;

@Service
public class GreetingService {
    public String greet(String name) {
        if (name == null || name.isEmpty()) {
            return "Hello World!";
        } else {
            return "Hello " + name + "!";
        }
    }
}

3. DemoApplication angepasst:
Code:
    public static void main(String[] args) {
        ApplicationContext context = SpringApplication.run(DemoApplication.class, args);
        GreetingService greetingService = context.getBean(GreetingService.class);
        System.out.println(greetingService.greet(null));
        System.out.println(greetingService.greet("Konrad"));
    }

4. Ausführung:
Code:
C:\Apps\jdk-17\bin\java.exe -XX:TieredStopAtLevel=1 -Dspring.output.ansi.enabled=always -Dcom.sun.management.jmxremote -Dspring.jmx.enabled=true -Dspring.liveBeansView.mbeanDomain -Dspring.application.admin.enabled=true -javaagent:C:\Users\A419779\AppData\Local\JetBrains\Toolbox\apps\IDEA-U\ch-0\222.3739.54\lib\idea_rt.jar=56787:C:\Users\A419779\AppData\Local\JetBrains\Toolbox\apps\IDEA-U\ch-0\222.3739.54\bin -Dfile.encoding=UTF-8 -classpath C:\Projects\temp\demo\target\classes;C:\Users\A419779\.m2\repository\org\springframework\boot\spring-boot-starter\2.7.3\spring-boot-starter-2.7.3.jar;C:\Users\A419779\.m2\repository\org\springframework\boot\spring-boot\2.7.3\spring-boot-2.7.3.jar;C:\Users\A419779\.m2\repository\org\springframework\spring-context\5.3.22\spring-context-5.3.22.jar;C:\Users\A419779\.m2\repository\org\springframework\spring-aop\5.3.22\spring-aop-5.3.22.jar;C:\Users\A419779\.m2\repository\org\springframework\spring-beans\5.3.22\spring-beans-5.3.22.jar;C:\Users\A419779\.m2\repository\org\springframework\spring-expression\5.3.22\spring-expression-5.3.22.jar;C:\Users\A419779\.m2\repository\org\springframework\boot\spring-boot-autoconfigure\2.7.3\spring-boot-autoconfigure-2.7.3.jar;C:\Users\A419779\.m2\repository\org\springframework\boot\spring-boot-starter-logging\2.7.3\spring-boot-starter-logging-2.7.3.jar;C:\Users\A419779\.m2\repository\ch\qos\logback\logback-classic\1.2.11\logback-classic-1.2.11.jar;C:\Users\A419779\.m2\repository\ch\qos\logback\logback-core\1.2.11\logback-core-1.2.11.jar;C:\Users\A419779\.m2\repository\org\apache\logging\log4j\log4j-to-slf4j\2.17.2\log4j-to-slf4j-2.17.2.jar;C:\Users\A419779\.m2\repository\org\apache\logging\log4j\log4j-api\2.17.2\log4j-api-2.17.2.jar;C:\Users\A419779\.m2\repository\org\slf4j\jul-to-slf4j\1.7.36\jul-to-slf4j-1.7.36.jar;C:\Users\A419779\.m2\repository\jakarta\annotation\jakarta.annotation-api\1.3.5\jakarta.annotation-api-1.3.5.jar;C:\Users\A419779\.m2\repository\org\springframework\spring-core\5.3.22\spring-core-5.3.22.jar;C:\Users\A419779\.m2\repository\org\springframework\spring-jcl\5.3.22\spring-jcl-5.3.22.jar;C:\Users\A419779\.m2\repository\org\yaml\snakeyaml\1.30\snakeyaml-1.30.jar;C:\Users\A419779\.m2\repository\org\slf4j\slf4j-api\1.7.36\slf4j-api-1.7.36.jar com.example.demo.DemoApplication

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v2.7.3)

2022-09-01 08:28:45.891  INFO 1924 --- [           main] com.example.demo.DemoApplication         : Starting DemoApplication using Java 17 on DESKTOP-09OS8T5 with PID 1924 (C:\Projects\temp\demo\target\classes started by A419779 in C:\Projects\temp\demo)
2022-09-01 08:28:45.894  INFO 1924 --- [           main] com.example.demo.DemoApplication         : No active profile set, falling back to 1 default profile: "default"
2022-09-01 08:28:46.368  INFO 1924 --- [           main] com.example.demo.DemoApplication         : Started DemoApplication in 0.788 seconds (JVM running for 1.319)
Hello World!
Hello Konrad!

Process finished with exit code 0

Somit läuft es tatsächlich genau so wie beschrieben. Es fehlt kein weiterer Schritt.
 
Java:
import org.springframework.data.jpa.repository.JpaRepository;

public interface TArtikelRepository extends JpaRepository {
    }

Mal was Anderes? Fehlt bei JpaRepository nicht etwas? Oder kann man die Parameter <T, ID> einfach weglassen?

Java:
import org.springframework.data.jpa.repository.JpaRepository;

public interface TArtikelRepository extends JpaRepository <T, ID> {
    }
 
vielen danke für die Hilfe.
Die Lösung für mein Problem was einfach als gedacht.
Java:
 ConfigurableApplicationContext applicationContext =
                SpringApplication.run(PreisbearbeitungApplication.class, args);
TArtikelService tArtikelService = applicationContext.getBean(TArtikelService.class)
das war das puzzle stück was gefehlt hat.
 
Mal was Anderes? Fehlt bei JpaRepository nicht etwas? Oder kann man die Parameter <T, ID> einfach weglassen?

Java:
import org.springframework.data.jpa.repository.JpaRepository;

public interface TArtikelRepository extends JpaRepository <T, ID> {
    }
Das ist Dir gut aufgefallen. Das sollte so nicht sein. Prinzipiell ist es aus Java Sicht denkbar, dass man beim Ableiten den Generic weg lässt, aber Sprint Boot soll ja das Repository erzeugen und dazu werden diese Informationen ja auch durchaus gebraucht. Ich habe es jetzt nicht ausprobiert, aber ich würde da zur Laufzeit Probleme erwarten. Spring Boot sollte ja wissen, dass es um ein Repository für TArtikelEntity geht und die Information ist da so erst einmal ja nicht vorhanden.
 

Zurück
Oben