Hallo liebe Community,
wir sollen eine art "tripadvisor" nachprogrammieren, ich verzeweifle gerade an der Berechnung der durchschnittlichen Ratings. Meine Architektur sieht folgendermaßen aus:
Es gibt eine Mutterklasse Root (der Name muss dringend geändert werden, wir konnten uns in der Gruppe nicht einigen worauf):
Dann gibt es eine Klasse Hotel, die von Root erbt
eine Klasse Review
:
und eine Klasse AvgRating, die ein Objekt sein soll, dass später die durchschnittlichen Werte der einzelnen Reviews speichert:
Das Repository ist relativ straighfoward:
und der Knackpunkt ist der Controller. Ich würde gerne das Objekt private AvgRating avgRatingStats in der Klasse Hotel mit den Durchschnittswerten der einzelnen Reviews versehen, sobald es aus der Datenbank gezogen wird...
Um die ganze Sache noch zu verkomplizieren gibt es auch Mock-Daten:
Das Problem scheint, dass das AvgRating Objekt nicht weitergegeben wird....wenn es vom Frontend abgerufen wird.
Zur Info noch den http-Service in Angular:
und in der entsprechenden hotel-list.ts datei steht dann
Wie gesagt, das funktioniert auch alles, die filteredHotelList gibt per json-pipe auch jede menge Infos (die Mock Daten eben) ....aaaber: Das im Controller in Java generierte AvgRating Objekt, das eigentlich per oneToOne in jedem Hotel stecken sollte kommt nicht an! Direkt hinter der reviewList müsste es eigetlich stehen:
Ich vermute irgendwo habe ich einen Denkfehler..ist mein erstes Projekt mit Datenbanken..bin für jeden Tipp und jede Hilfe dankbar!
wir sollen eine art "tripadvisor" nachprogrammieren, ich verzeweifle gerade an der Berechnung der durchschnittlichen Ratings. Meine Architektur sieht folgendermaßen aus:
Es gibt eine Mutterklasse Root (der Name muss dringend geändert werden, wir konnten uns in der Gruppe nicht einigen worauf):
Java:
import java.util.ArrayList;
import java.util.List;
import javax.persistence.*;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
import com.fasterxml.jackson.annotation.JsonManagedReference;
import com.itf.Review.Review;
import lombok.AccessLevel;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "Entity_Type")
public abstract class Root {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@NotBlank
@Column(nullable = false)
@Size(min = 1, max = 40)
private String name;
@NotBlank
@Column(nullable = false)
private String address;
@NotBlank
@Column(nullable = false)
@Size(min = 1, max = 20)
private String telephone;
@NotBlank
@Column(nullable = false)
@Size(min = 1, max = 20)
private String city;
@NotBlank
@Column(nullable = false)
@Size(min = 1, max = 500)
private String description;
private String website;
private Double rating;
@JsonManagedReference
@OneToMany(mappedBy = "rootEntity")
private List<Review> reviewList = new ArrayList<Review>();
}
Dann gibt es eine Klasse Hotel, die von Root erbt
Java:
package com.itf.Entity.Hotel;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
import javax.persistence.OneToOne;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import com.fasterxml.jackson.annotation.JsonBackReference;
import com.itf.Entity.Root;
import com.itf.Review.AvgRating;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@Entity
@DiscriminatorValue("Hotel")
@AllArgsConstructor
@NoArgsConstructor
public class Hotel extends Root {
@NotBlank
private Double price;
private Integer stars;
@NotNull
private Integer rooms;
private Boolean swimmingPool;
private Boolean restaurant;
private String breakfast;
private Boolean wifi;
private String parking;
private String email;
@JsonBackReference
@OneToOne(mappedBy = "matchedReview")
private AvgRating avgRatingStats;
}
eine Klasse Review
Java:
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
import com.fasterxml.jackson.annotation.JsonBackReference;
import com.itf.Entity.Root;
import com.sun.istack.NotNull;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@NoArgsConstructor
@Entity
@Table(name="reviews")
public class Review implements Comparable< Review > {
@Id
@NotNull
@GeneratedValue(strategy = GenerationType.IDENTITY)
Integer id;
private Double averageRating;
@Min(0)
@Max(5)
private Double cleanness;
@Min(0)
@Max(5)
private Double friendliness;
@Min(0)
@Max(5)
private Double foodQuality;
@Min(0)
@Max(5)
private Double ambience;
@Min(0)
@Max(5)
private Double familyFriendly;
@Size(min= 0, max = 255)
private String comment;
@NotBlank
private String author;
public Double getAverageRating() {
return (cleanness+friendliness+foodQuality+ambience+familyFriendly)/5;
}
@JsonBackReference
@ManyToOne
@JoinColumn(name = "establishment_id")
private Root rootEntity;
}
und eine Klasse AvgRating, die ein Objekt sein soll, dass später die durchschnittlichen Werte der einzelnen Reviews speichert:
Code:
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToOne;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Entity
@Getter
@Setter
@NoArgsConstructor
public class AvgRating {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Integer id;
Double avgAmbience = 0.0;
Double avgFriendly = 0.0;
Double avgClean = 0.0;
Double avgFood = 0.0;
Double avgFamily = 0.0;
Double avgTotalRating = 0.0;
@OneToOne
@JoinColumn(name = "review_match")
Review matchedReview;
}
Das Repository ist relativ straighfoward:
Code:
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
public interface HotelRepository extends JpaRepository<Hotel, Integer> {
}
und der Knackpunkt ist der Controller. Ich würde gerne das Objekt private AvgRating avgRatingStats in der Klasse Hotel mit den Durchschnittswerten der einzelnen Reviews versehen, sobald es aus der Datenbank gezogen wird...
Java:
import java.util.List;
import java.util.Optional;
import javax.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.itf.Review.AvgRating;
import com.itf.Review.Review;
@RestController
@RequestMapping("/smart-trip/hotels/")
public class HotelController {
@Autowired
HotelRepository hotelRepository;
Hotel hotel;
List <Hotel> hotelList;
List <Review> reviews;
AvgRating avgRating = new AvgRating();
@GetMapping
public List<Hotel> fetchAll(){
hotelList = hotelRepository.findAll();
for (Hotel hotel : hotelList) {
reviews = hotel.getReviewList();
for (Review review : reviews) {
avgRating.setAvgAmbience(+review.getAmbience());
avgRating.setAvgClean(+review.getCleanness());
avgRating.setAvgFamily(+review.getFamilyFriendly());
avgRating.setAvgFood(+review.getFoodQuality());
avgRating.setAvgFriendly(+review.getFriendliness());
avgRating.setAvgTotalRating(+review.getAverageRating());
}
hotel.setAvgRatingStats(avgRating);
}
return hotelList;
}
}
Um die ganze Sache noch zu verkomplizieren gibt es auch Mock-Daten:
SQL:
--Add Hotels
INSERT INTO root (entity_type,name,address,telephone,city,description,website,rating,price,stars,rooms,swimming_pool,restaurant,breakfast,wifi,parking,email)VALUES('Hotel', 'Hotel am Main','Olof-Staße 19','017630222','Frankfurt','Gemütliche Hotel am Main','www.hotelmain.com',4,20.0,4,90,true,'false',false,true,'true','hotelmain@mail.de');
INSERT INTO root (entity_type,name,address,telephone,city,description,website,rating,price,stars,rooms,swimming_pool,restaurant,breakfast,wifi,parking,email)VALUES('Hotel', 'Wald Hotel','Waldstr.13','017645665','Frankfurt','Günstiges Hotel am Wald','www.hotelWald.com',3,30.0,2,40,true,'false',true,true,'false','waldhotel@mail.de');
INSERT INTO root (entity_type,name,address,telephone,city,description,website,rating,price,stars,rooms,swimming_pool,restaurant,breakfast,wifi,parking,email)VALUES('Hotel', 'Hotel Blankenese','Hauptstraße 19','017630222','Hamburg','Gemütliches Hotel an der Elbe','www.hotelblankenese.com',3.0,40.0,3,90,false,'false',false,true,'false','hotelmain@mail.de');
INSERT INTO root (entity_type,name,address,telephone,city,description,website,rating,price,stars,rooms,swimming_pool,restaurant,breakfast,wifi,parking,email)VALUES('Hotel', 'Kreuzberg Inn','Wiesenstr.13','017645665','Berlin','Hotel im Szeneviertel','www.Kreuzberginn.com',4.5,60.0,5,40,true,'false',true,true,'false','waldhotel@mail.de');
INSERT INTO root (entity_type,name,address,telephone,city,description,website,rating,price,stars,rooms,swimming_pool,restaurant,breakfast,wifi,parking,email)VALUES('Hotel', 'Schlosshotel Dresden','Hauptstaße 19','017630222','Dresden','Gemütliche Hotel an der Elbe','www.hotelmain.com',3.4,70.0,5,90,false,'false',false,true,'false','schlosshotel@mail.de');
INSERT INTO root (entity_type,name,address,telephone,city,description,website,rating,price,stars,rooms,swimming_pool,restaurant,breakfast,wifi,parking,email)VALUES('Hotel', 'Berghotel Frankfurt','Waldstr.13','017645665','Frankfurt','Günstiges Hotel am Wald','www.hotelWald.com',2.7,40.0,3,40,false,'true',false,false,'true','waldhotel@mail.de');
INSERT INTO root (entity_type,name,address,telephone,city,description,website,rating,price,stars,rooms,swimming_pool,restaurant,breakfast,wifi,parking,email)VALUES('Hotel', 'Gala Hotel','Hauptstraße 19','017630222','Frankfurt','Hochklassiges Domizil der Elbe','www.hotel-am-berg.com',3.0,100.0,5,90,false,'false',true,true,'false','hotelmain@mail.de');
INSERT INTO root (entity_type,name,address,telephone,city,description,website,rating,price,stars,rooms,swimming_pool,restaurant,breakfast,wifi,parking,email)VALUES('Hotel', 'Stadthotel Frankfurt','Wiesenstr.13','017645665','Frankfurt','Hotel im Szeneviertel','www.stadthotel.com',4.5,60.0,5,40,true,'true',true,true,'true','waldhotel@mail.de');
--Add Reviews
insert into reviews( cleanness, friendliness, food_quality, ambience, family_friendly, comment, author, establishment_id ) values
(3,5,4,1,5,'Tolles Ding', 'das Bo', 1);
insert into reviews( cleanness, friendliness, food_quality, ambience, family_friendly, comment, author, establishment_id ) values
(3,5,2,3,1,'Tolles Ding', 'der letzte Mohikaner',2);
insert into reviews( cleanness, friendliness, food_quality, ambience, family_friendly, comment, author, establishment_id ) values
(3,4,1,1,4,'Tolles Ding', 'Janette',3);
insert into reviews( cleanness, friendliness, food_quality, ambience, family_friendly, comment, author, establishment_id ) values
(5,2,2,1,2,'Tolles Ding', 'biba-mausi', 4);
insert into reviews( cleanness, friendliness, food_quality, ambience, family_friendly, comment, author, establishment_id ) values
(1,5,2,1,3,'Tolles Ding','Kalle',1);
insert into reviews( cleanness, friendliness, food_quality, ambience, family_friendly, comment, author, establishment_id ) values
(4,5,2,2,1,'Tolles Ding','Mareike E.',3);
Das Problem scheint, dass das AvgRating Objekt nicht weitergegeben wird....wenn es vom Frontend abgerufen wird.
Zur Info noch den http-Service in Angular:
Javascript:
const apiHotelRoot = "http://localhost:8080/smart-trip/hotels/";
@Injectable({
providedIn: 'root'
})
export class HotelService {
public getHotels(): Observable<Hotel[]> {
return this.http.get<Hotel[]>(apiHotelRoot);
}
und in der entsprechenden hotel-list.ts datei steht dann
Javascript:
import { Component, OnInit, Input, SimpleChange } from '@angular/core';
import { Hotel } from '../hotel/model/hotel';
import { HotelService } from '../hotel/hotel.service';
import { Pipe, PipeTransform } from "@angular/core";
import { map } from 'rxjs/operators';
import { Observable, of } from 'rxjs';
@Component({
selector: 'app-hotel-list',
templateUrl: './hotel-list.component.html',
styleUrls: ['./hotel-list.component.css']
})
export class HotelListComponent implements OnInit {
constructor(private hotelService: HotelService) { }
ngOnInit(): void {
this.hotelService.getHotels()
.subscribe(fetchedHotels => (this.filteredHotelList = fetchedHotels));
}
}
Wie gesagt, das funktioniert auch alles, die filteredHotelList gibt per json-pipe auch jede menge Infos (die Mock Daten eben) ....aaaber: Das im Controller in Java generierte AvgRating Objekt, das eigentlich per oneToOne in jedem Hotel stecken sollte kommt nicht an! Direkt hinter der reviewList müsste es eigetlich stehen:
JSON:
[ { "id": 3, "name": "Hotel am Main", "address": "Olof-Staße 19", "telephone": "017630222", "city": "Frankfurt", "description": "Gemütliche Hotel am Main", "website": "www.hotelmain.com", "rating": 4, "reviewList": [ { "id": 3, "averageRating": 2.6, "cleanness": 3, "friendliness": 4, "foodQuality": 1, "ambience": 1, "familyFriendly": 4, "comment": "Tolles Ding", "author": "Janette" }, { "id": 6, "averageRating": 2.8, "cleanness": 4, "friendliness": 5, "foodQuality": 2, "ambience": 2, "familyFriendly": 1, "comment": "Tolles Ding", "author": "Mareike E." } ], "price": 20, "stars": 4, "rooms": 90, "swimmingPool": true, "restaurant": false, "breakfast": "FALSE", "wifi": true, "parking": "true", "email": "hotelmain@mail.de" }
Ich vermute irgendwo habe ich einen Denkfehler..ist mein erstes Projekt mit Datenbanken..bin für jeden Tipp und jede Hilfe dankbar!