DTO <> Entity Hibernate Spring Boot

OnDemand

Top Contributor
Hallo zusammen,

angenommen ich hab unten stehende Entity (mit wesentlich mehr Feldern) und möchte unter gewissen Umständen nur den Namen und Hersteller aus der DB. Dazu hab ich ein passendes DTO welches nur die beiden Felder hat.

Wie bekomme ich das DTO mit nur den Daten befüllt ohne das gesamte "Data" zu hlen und Data zu Dto zu mappen
sowie
ohne @Query(SELECT new DataDto(d.name, d.hersteller) From....)

Gibt es da keine elegantere Möglichkeit?

Code:
@Entity
@Table(name = "data")
public class Data {

    @EmbeddedId
    private ProductId id;

    @Column
    private String name;
   
    @Column
    private String beschreibung;
   
    @Column
    private String hersteller;
    }
 
Danke, hab es mal mit dem Class Based versucht, aber bekomme eine No converter found capable of converting from type to Type Fehlermeldung

Irgendwas hab ich vergessen oder Spring Boot macht mir einen Strich durch die Rechnung. Kann das vielleicht an den Lombokannotationen liegen? Der baut die hash und equals automatisch

Folgende Klassen hab ich

Code:
@GetMapping("/{dataName}")
    public DataDto getData(@PathVariable String dataName) {
        DataDto dataDto;
        try {
          dataDto = dataRepository.findByDataName(dataName);
        } catch (Exception ex) {
            log.error(ex.getMessage());
            return null;
        }
        return dataDto;
    }

Code:
public interface DataRepository extends CrudRepository<Data, Long> {

    DataDto findByDataName(String dataName);
}

Code:
public class DataDto implements Serializable {
    private static final long serialVersionUID = 1L;
    private String dataName;
    private boolean active;
    private String description;
}

Code:
@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "data")
public class Data {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private int id;

    @Column
    private String dataName;
   
    @Column
    private String description;

    @Column(name = "active")
    private boolean active;
}
 
Upps die hab ich hier vergessen rein zu kopieren. Hatte vorher noch @NoArgsConstructor damit gehts nicht. Wenn ich @NoArgsConstructor entferne, gehts 😳

Find das so einfacher zu mappen als im Query zu wurschteln.
 
Einfacher als das?
Java:
var entity = repository.find...();
var dto = new Dto(entity.foo, entity.bar);
 
holt er da nicht aber alle Daten raus erstmal?
Komm drauf an wie du deine Methoden im Repository schreibst. Prinzipiell verschiebst du damit meiner Meinung nach aber das "Problem" der partiellen Datenabfrage ins Repository und darfst da manuell Queries schreiben ala @Query("SELECT foo, bar FROM entity"). Kann man aber auch so machen. Es gibt viele Wege mit jeweils Vor- und Nachteilen.
 
Meine letzte Antwort galt deiner Frage zu mrBrown's Vorschlag. Spring Data Projections fragen nur die Felder ab, die du in deiner Projection definierst ("holt dein Vorschlag NICHT alle Daten raus"). Und nochmal: Durch manuelles Mapping kannst du dies auch erreichen, aber musst dann eben die Repository-Methoden selbst schreiben aber kommst auch zum Ziel.
Am besten probierst du beide Wege aus und entscheidest dich dann.
 
Danke, Collection hab ich grad keine, aber wird sicher vorkommen.
@mrBrown hast du nen Link wo ich mich zu deiner Variante belesen kann? Hab mit var noch nicht mal was am Hut gehabt 😀
Ich möchte unbedingt nur die Felder lesen, welche ich auch brauche
 
Mit dem anderen Weg geht folgendes (getestet) und basierend auf dem Projekt aus dem Anfangs genannten Tutorial:
Java:
@Entity
public class Address {

    @Id
    private Long id;

    @OneToOne
    private Person person;
    private String state;
    private String city;
    private String street;
    private String zipCode;
}
Java:
@Value
public class AddressPartial {
    Long id;
    String zipCode;
}
Java:
@Repository
public interface AddressRepository extends CrudRepository<Address, Long> {
   @Query("select new fully.qualified.AddressPartial(a.id, a.zipCode) from Address a where a.state = :state" )
   List<AddressPartial> getAddressByState(String state);
}
Java:
@DataJpaTest
@Sql(scripts = "/projection-insert-data.sql")
@Sql(scripts = "/projection-clean-up-data.sql", executionPhase = AFTER_TEST_METHOD)
public class JpaStandardIntegrationTest {

    @Autowired
    private AddressRepository addressRepository;

    @Test
    public void whenUsingClosedProjections_thenViewWithRequiredPropertiesIsReturned() {
        AddressPartial addressPartial = addressRepository.getAddressByState("CA").get(0);

        assertThat(addressPartial.getZipCode()).isEqualTo("90001");
    }

}
Code:
Hibernate:
    select
        address0_.id as col_0_0_,
        address0_.zip_code as col_1_0_
    from
        address address0_
    where
        address0_.state=?
 
@mrBrown hast du nen Link wo ich mich zu deiner Variante belesen kann? Hab mit var noch nicht mal was am Hut gehabt 😀
var ist einfach nur Ersatz für den konkreten Typ, für dein Beispiel oben sind diese beiden äquivalent:

Java:
var entity = repository.find...();
var dto = new DataDto(entity.name, entity.hersteller);

Java:
Data entity = repository.find...();
DataDto dto = new DataDto(entity.name, entity.hersteller);
 
Muss nochmal blöd fragen, bin unterwegs und da kam mir noch was.
wenn ich mein DTO mit dem Namen und Hersteller holen will, muss ich die Methode findByIdNameAndHersteller nennen?
🧐
Hab bisher immer meine Entity als Objekt genommen und die weiter gereicht. Will es nun aber mal richtig machen und verstehen
 
wenn ich mein DTO mit dem Namen und Hersteller holen will, muss ich die Methode findByIdNameAndHersteller nennen?
findByIdNameAndHersteller würde einem SELECT * WHERE name=? AND hersteller=? entsprechen 😉

Hab bisher immer meine Entity als Objekt genommen und die weiter gereicht. Will es nun aber mal richtig machen und verstehen
Aus dem Repository ist es auch völlig richtig, Entitys herauszureichen, DTOs nutzt man dann erst an der Netzwerkschnittstelle.
 
Verwirrt🤓

heißt also ich kann Entities innerhalb meines Programms nutzen?
Erst wenn ich was an andere Systeme gebe kann/sollte man DTO nehmen

Oder auch hier: ich möchte ein Objekt holen welches mehrere collections hat die ich aber nicht brauche.
dafür ein DTO mit den nötigen Feldern bauen oder wäre auch hier ausreichend die entity mittels Query zu „beschneiden“ Und im Query im select das wählen was ich brauche?

hab nämlich noch einen Fall da holt es mit jedesmal die collections raus die ich an der Stelle nicht brauche und daher ewig dauert.
An andere Stelle brauch ich die collections aber
 
heißt also ich kann Entities innerhalb meines Programms nutzen?
Erst wenn ich was an andere Systeme gebe kann/sollte man DTO nehmen
Jap.

Oder auch hier: ich möchte ein Objekt holen welches mehrere collections hat die ich aber nicht brauche.
dafür ein DTO mit den nötigen Feldern bauen oder wäre auch hier ausreichend die entity mittels Query zu „beschneiden“ Und im Query im select das wählen was ich brauche?

hab nämlich noch einen Fall da holt es mit jedesmal die collections raus die ich an der Stelle nicht brauche und daher ewig dauert.
An andere Stelle brauch ich die collections aber
Die Collections sind doch hoffentlich Lazy? Dann sollten die ersten bei Zugriff geladen werden - wenn du sie nicht nutzt, werden sie auch nicht geladen.
 
Mit dem Zugriff versteh ich nicht. Wenn ich das Objekt lade, werden die collections mir geladen. Wie sage ich dem System „ich will auch collections“

über einen anderenrepository Zugriff?
 
JPA-Magie 🙂

Wenn deine Entität grob so aussieht:
Java:
class Entity {
     String foo;
     Collection<Bar> bars;
}

Dann wird erstmal nur das Feld foo initialisiert.

Erst wenn du auf entity.bars zugreifst, wird im Hintergrund Automatsch eine zweite SQL-Query ausgeführt, die die Collection lädt.
 
Moin,

meine Collections sind nicht Lazy, weil ich die Entitiy an eine REST Schnittstelle liefere wo alle Daten gebraucht werden.

Da das andere System alle Daten braucht, wüsste ich nicht wie man das mit Lazy macht 🙄
 

Zurück
Oben