java - 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;
}
我的餐桌天气:
我的表城市:(目前我的天气数据只来自这一个城市,没错)
这里列出了我迄今为止尝试过的(至少我能记住的)但没有奏效。
- cascade = CascadeType.PERSIST (现在我保留两个对象,否则我会收到“未标记级联 PERSIST”错误)
- 删除生成的值 ID
- 没有 OneToMany(市内)声明
- 仅使用没有任何特定声明的 ManyToOne(如 fetchtype)
- 没有 join.column
这就是我能记得的全部。如果您需要更多信息,请告诉我。
编辑:
我从网络服务获取天气数据。
我的天气阅读器:
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;
}
}
解决方案
问题很简单,对于 JPA,如果您的实体没有任何 ID,那么这意味着如果 ID 为空,它将执行 INSERT,如果 ID 不为空,它将执行更新。
根据我的阅读,您的城市实例始终是一个新对象,或者至少是一个不在持久性上下文中处理的对象。
在您的代码中,您应该首先检查您的数据库中是否已经有一个命名城市,如果是,请将其附加到您的天气中。如果不只是插入它(就像您在 SQL 中所做的那样......)
为了避免奇怪的行为,我还可以建议您在城市名称上添加唯一约束。
让我知道它是否解决了您的问题
推荐阅读
- google-apps-script - 谷歌 apss 脚本时间 - 将下午时间记录为上午
- php - 如何在 url 中将链接作为参数传递
- ios - LongPress on List 确实在 iPad 上重新排序,但如何在 iPhone 上重新排序
- ios - 有没有办法从 iPad 或选定的 iPhone 版本不支持我的应用程序?
- node.js - 将视频+音频(通过 ffmpeg)分段和转码为按需 HLS 流的精确方法?
- android - Proguard minifyEnabled True 应用程序未按预期工作
- sql - 显示距离路线 5 公里处的所有标记
- firebase - 如何为 Firebase 创建 APNS 有效负载?
- ios - Intellij 为 Kotlin 多平台生成的项目总是显示构建失败
- amazon-web-services - 本地应用程序是否可以将消息直接推送(发布)到 AWS Eventbridge /EventBus?