首页 > 解决方案 > ManyToOne 为每个新对象在两个表中创建新条目

问题描述

我有城市和天气。天气应该指向数据库中的城市对象。一个城市可以有多个天气条目。我的问题是,每次我向我的数据库添加天气时,它都会创建一个名称相同但 ID 不同的新城市。

天气实体;

@Entity
public class Weather implements Serializable{

    @Id
    @GeneratedValue
    private int id;
    private static final long serialVersionUID = -3562151396218034738L;
    private LocalDateTime timeStamp;
    private Float windSpeed;
    private Float windDir;
    private Float humidity;
    private Float temperature;
    private String weatherDescription;
    @ManyToOne(fetch=FetchType.LAZY, cascade = CascadeType.PERSIST)
    @JoinColumn(name="city_id")
    private City city;

    public Weather(){}

    public Weather(LocalDateTime timeStamp, Float windSpeed, Float windDir, Float humidity, Float temperature, String weatherDescription, City city){
        this.timeStamp = timeStamp;
        this.windSpeed = windSpeed;
        this.windDir = windDir;
        this.humidity = humidity;
        this.temperature = temperature;
        this.weatherDescription = weatherDescription;
        this.city = city;
    }
}

城市实体:

@Entity
public class City implements Serializable {

    @Id
    @GeneratedValue
    private int id;
    private static final long serialVersionUID = 1L;
    private String cityName;
    private String cityPostalcode;
    @OneToMany(mappedBy = "city")
    private List<Weather> weather;

    public City(){}

    public City(String cityName, String cityPostalcode){
    this.cityName = cityName;
    this.cityPostalcode = cityPostalcode;
    }

我将天气添加到我的数据库的两种方法。

    @Override
    public Weather addWeather(Weather weather) throws Exception {

        EntityManager em = JpaUtil.createEntityManager();
        try {
            em.getTransaction().begin();
            em.persist(weather);
            em.persist(weather.getCity());
            em.getTransaction().commit();
        } catch (Exception e) {
            if (em.getTransaction().isActive()) {
                em.getTransaction().rollback();
            }
            throw new RuntimeException(e);
        } finally {
            if (em.isOpen()) {
                em.close();
                LOG.info("New Weather entry for " + weather.getCity().getCityName());
            }
        }
        return weather;

    }

    @Override
    public List<Weather> addWeatherWithList(List<Weather> weather) throws Exception {

        for (int i = 0; i < weather.size() - 1; i++) {
                EntityManager em = JpaUtil.createEntityManager();
                em.getTransaction().begin();
                try {
                    em.persist(weather.get(i));
                    em.persist(weather.get(i).getCity());
                    em.getTransaction().commit();
                } catch (Exception e) {
                    if (em.getTransaction().isActive()) {
                        em.getTransaction().rollback();
                    }
                    throw new RuntimeException(e);
                } finally {
                    if (em.isOpen()) {
                        em.close();
                        LOG.info(weather.get(i).toString() + " added to db");
                    }
                }
        }
        return weather;

    }

我的餐桌天气:

在此处输入图像描述

我的表城市:(目前我的天气数据只来自这一个城市,没错)

在此处输入图像描述

这里列出了我迄今为止尝试过的(至少我能记住的)但没有奏效。

这就是我能记得的全部。如果您需要更多信息,请告诉我。

编辑:

我从网络服务获取天气数据。

我的天气阅读器:

public class WeatherReader extends RESTReader {


    private RESTReader client = new RESTReader();

    public List<Weather> getAllWeatherData() {

        try {
            ObjectMapper mapper = new ObjectMapper();
            List<City> citiesToBeCalled = client.deserialize(); // list resulting from deserialization
            ArrayList<List<Weather>> allWeatherList = new ArrayList<>();
            for (int i = 0; i < 1; i++) { //TODO: replace 1 with citiesToBeCalled.size() to have all cities
                String json = client.weatherData(citiesToBeCalled.get(i).getCityName());
                List<RawWeatherData> rawWeatherList = Arrays.asList(mapper.readValue(json, RawWeatherData[].class));

                List<Weather> weatherList = new ArrayList<>();

                for (int j = 0; j < rawWeatherList.size(); j++){
                    weatherList.add(rawWeatherList.get(j).convertToWeather());
                }

                allWeatherList.add(weatherList);
            }
            return allWeatherList.stream().flatMap(x -> x.stream()).collect(Collectors.toList());
        } catch (Exception e) {
            System.out.println("Error:" + e.getMessage());
            return null;
        }
    }
}

我的 RestReader.class:

public class RESTReader {

    private String masterDataCityFilePath = "t.tmp";
    private static final String BASE_URI = "removed because of privacy reasons";
    private HttpClient httpClient = HttpClient.newHttpClient();
    private String mimeType = "application/json";

    //TODO: this is already good and can be used for the CityReader
    public String allCitiesAsJson() throws Exception {

        HttpRequest req = HttpRequest.newBuilder(URI.create(BASE_URI + "cities")).headers("Accept", mimeType).GET().build();
        System.out.println("REQUEST SENT:" + req);
        HttpResponse<String> res = httpClient.send(req, HttpResponse.BodyHandlers.ofString());

        if (res.statusCode() == 200) {
            return res.body().toString();
        } else {
            throw new Exception("Status-code:" + res.statusCode());
        }
    }

    public String weatherData(String cityname) throws Exception{


        String realcityname = cityname.replace(" ", "%20");

        HttpRequest req = HttpRequest.newBuilder(URI.create(BASE_URI + realcityname)).headers("Accept", mimeType).GET().build();
        System.out.println("REQUEST SENT:" + req);
        HttpResponse<String> res = httpClient.send(req, HttpResponse.BodyHandlers.ofString());

        if (res.statusCode() == 200) {
            return res.body().toString();
        } else {
            throw new Exception("Status-code:" + res.statusCode());
        }
    }

    public void serialize(List<City> cityList) { //Creating
        try {
            FileOutputStream fileOut = new FileOutputStream(masterDataCityFilePath); //(MasterdataCities)
            ObjectOutputStream out = new ObjectOutputStream(fileOut);
            out.writeObject(cityList);
            out.close();
            fileOut.close();
            System.out
                    .println("Master data file saved under: " + masterDataCityFilePath);
        } catch (IOException i) {
            System.err.println("There was an error saving the file!");
            System.err.println("Wrong directory?");
            i.printStackTrace();
        }
    }

    public List<City> deserialize() {
        try {
            FileInputStream fileIn = new FileInputStream(masterDataCityFilePath);
            ObjectInputStream in = new ObjectInputStream(fileIn);
            List<City> cityList = (List<City>) in.readObject();

            in.close();
            System.out
                    .println("Loaded cities from: " + masterDataCityFilePath);

            return cityList;
        } catch (IOException | ClassNotFoundException e) {
            System.err.println("There was an error loading from:" + masterDataCityFilePath);
            System.err.println("Wrong directory?\n");
            System.out.println("Directory is: " + masterDataCityFilePath);
            e.printStackTrace();
        }
        return null;
    }

    public String getMasterDataCityFilePath() {
        return masterDataCityFilePath;
    }

    public void setMasterDataCityFilePath(String masterDataCityFilePath) {
        this.masterDataCityFilePath = masterDataCityFilePath;
    }
}

编辑 2:我的 convertToWeather();

    public Weather convertToWeather(){
            try {
                Weather weather = new Weather();
                weather.setCity(city);

                String str = lastUpdateTime;
                DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
                LocalDateTime dateTime = LocalDateTime.parse(str, formatter);
                weather.setTimeStamp(dateTime);

                //assign wind speed
                String datacontent[] = data.split("#");

                String windSpeedValue[] = datacontent[12].split(":", 2); // specify data content number
                if (windSpeedValue[1].equals("unknown")){
                    weather.setWindSpeed(null);
                } else {
                    weather.setWindSpeed(Float.parseFloat(windSpeedValue[1])); //general just gimme the value specifier
                }

                //assign wind direction
                String windDirValue[] = datacontent[13].split(":", 2); // specify data content number
                if (windDirValue[1].equals("unknown")){
                    weather.setWindDir(null);
                } else {
                    weather.setWindDir(Float.parseFloat(windDirValue[1])); //general just gimme the value specifier
                }

                //assign humidity
                String humidityValue[] = datacontent[11].split(":", 2); // specify data content number
                if (humidityValue[1].equals("unknown")){
                    weather.setHumidity(null);
                } else {
                    weather.setHumidity(Float.parseFloat(humidityValue[1])); //general just gimme the value specifier
                }

                //assign temperature
                String temperatureValue[] = datacontent[9].split(":", 2); // specify data content number
                if (temperatureValue[1].equals("unknown")){
                    weather.setTemperature(null);
                } else {
                    weather.setTemperature(Float.parseFloat(temperatureValue[1])); //general just gimme the value specifier
                }

                //assign description
                String descriptionValue[] = datacontent[8].split(":", 2); // specify data content number
                if (descriptionValue[1].equals("unknown")){
                    weather.setWeatherDescription("unknown");
                } else {
                    weather.setWeatherDescription(descriptionValue[1]); //general just gimme the value specifier
                }

                return weather;

        } catch (Exception e) {
                System.out.println("Error:" + e.toString());
                return null;
            }
    }

标签: javadatabasepersistencemany-to-onepersist

解决方案


问题很简单,对于 JPA,如果您的实体没有任何 ID,那么这意味着如果 ID 为空,它将执行 INSERT,如果 ID 不为空,它将执行更新。

根据我的阅读,您的城市实例始终是一个新对象,或者至少是一个不在持久性上下文中处理的对象。

在您的代码中,您应该首先检查您的数据库中是否已经有一个命名城市,如果是,请将其附加到您的天气中。如果不只是插入它(就像您在 SQL 中所做的那样......)

为了避免奇怪的行为,我还可以建议您在城市名称上添加唯一约束。

让我知道它是否解决了您的问题


推荐阅读