java 如何处理 JPA 多对一关系?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3825662/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to handle JPA Many-to-One Relation?
提问by Vashishtha Jogi
I am designing an application for collecting weather data. I have 2 POJO objects "Location" and "Record". Location contains information about latitude and longitude and the current weather conditions, and Record contains all the weather information over time for a specific location thus having a Many-to-one relation with Location. The definition of the classes I have is as follows:
我正在设计一个用于收集天气数据的应用程序。我有 2 个 POJO 对象“位置”和“记录”。Location 包含有关纬度和经度以及当前天气状况的信息,Record 包含特定位置随时间的所有天气信息,因此与 Location 具有多对一的关系。我所拥有的类的定义如下:
Location.java
位置.java
@Entity
@Table(name = "location")
@NamedQueries( {
@NamedQuery(name = "findLocations", query = "SELECT e FROM Location e ORDER BY e.longitude, e.latitude"),
@NamedQuery(name = "findLocationByLatLong", query = "SELECT e from Location e WHERE e.latitude = :latitude AND e.longitude = :longitude"),
@NamedQuery(name = "findLocationById", query = "SELECT e from Location e WHERE e.id = :id"),
@NamedQuery(name = "deleteLocationById", query= "DELETE Location e WHERE e.id = :id"),
@NamedQuery(name = "updateLocation", query = "UPDATE Location e SET e.lastModifiedDate = :lastModifiedDate WHERE e.id = :id")})
public class Location implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
protected Long id;
@Column(name="latitude", nullable=false)
protected String latitude;
@Column(name="longitude", nullable=false)
protected String longitude;
@Column(name="lastModifiedDate")
@Temporal(TemporalType.TIMESTAMP)
private Date lastModifiedDate;
@Column(name="windDirection")
private float windDirection;
@Column(name="windSpeed")
private float windSpeed;
@Column(name="temperature")
private float temperature;
}
And Record.java
和Record.java
@Entity
@Table(name = "weatherdata")
@NamedQueries( {
@NamedQuery(name = "findWeatherRecordById", query = "SELECT e from Record e WHERE e.id = :id"),
@NamedQuery(name = "findWeatherRecords", query = "SELECT e from Record e WHERE e.parent = :parent") })
public class Record implements Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name="id")
protected Long id;
@Column(name="mTime")
@Temporal(TemporalType.TIMESTAMP)
private Date mtime;
@Column(name="windDirection")
private float windDirection;
@Column(name="windSpeed")
private float windSpeed;
@Column(name="temperature")
private float temperature;
@ManyToOne(cascade = { CascadeType.ALL }, targetEntity=Location.class, fetch = FetchType.EAGER)
@JoinColumn(name="locationId")
protected Location parent;
}
And my incoming data is in the form of:
我的传入数据采用以下形式:
latitude,longitude,date,winddirection,windspeed,temperature
36.9822,-122.0153,20100907000000.00,158,2.68,20.57
38.1838,-120.54,20100907000000.00,248,0,26.68
38.3495,-121.9688,20100907000000.00,149,0.45,33.9
38.41935,-121.36029,20100907000000.00,322,0.9,33.9
37.91617,-122.286,20100907000000.00,224,0,24.46
38.587,-121.3162,20100907000000.00,315,0,34.46
36.8717,-121.6555,20100907000000.00,294,3.13,18.34
Now whenever I get a record, I want to insert it in Record table. And as I have a foreign key to Location, I will also add the locationId of Location table. Another thing, Location table is not prepopulated. So whenever a new record comes I first insert it in Location table, and then populate the Record table with the foreign key. And I dont want duplication location entries in Location table. Location table will also contain the most latest temperature, windspeed and winddirection data as you can see.
现在每当我得到一条记录时,我都想将它插入到记录表中。由于我有 Location 的外键,我还将添加 Location 表的 locationId。另一件事,位置表没有预先填充。因此,每当有新记录出现时,我首先将其插入 Location 表中,然后使用外键填充 Record 表。而且我不想在位置表中重复位置条目。如您所见,位置表还将包含最新的温度、风速和风向数据。
I am using the following code to accomplish that:
我正在使用以下代码来实现:
Location loc = handler.getLocation(line);
//loc.setTemperature(0);
Location dbLoc = null;
try {
Query q = eManager.createNamedQuery("findLocationByLatLong");
q.setParameter("latitude", loc.getLatitude());
q.setParameter("longitude", loc.getLongitude());
dbLoc = (Location) q.getSingleResult();
} catch (Exception e) {
System.out.println("Location not found! Creating new location");
Logger.getLogger("WeatherRecorderBean.class").log(Level.WARNING, e.getMessage());
}
Record r = handler.getRecord(line);
if(dbLoc!=null) {
r.setParent(dbLoc);
dbLoc.setLastModifiedDate(r.getMtime());//I am doing this so as to know what time the weather change entry is about
dbLoc.setWindDirection(r.getWindDirection());
dbLoc.setWindSpeed(r.getWindSpeed());
dbLoc.setTemperature(r.getTemperature());
eManager.merge(r);
}
else {
dbLoc = new Location();
dbLoc.setLatitude(loc.getLatitude());
dbLoc.setLongitude(loc.getLongitude());
//eManager.persist(dbLoc);
r.setParent(dbLoc);
dbLoc.setLastModifiedDate(r.getMtime());
dbLoc.setWindDirection(r.getWindDirection());
dbLoc.setWindSpeed(r.getWindSpeed());
dbLoc.setTemperature(r.getTemperature());
eManager.merge(r);
//eManager.merge(dbLoc);
}
But by doing this, what is happening is Locations are being duplicated. Meaning I have multiple entries for same longitude and latitude but with different temperature, windspeed data in the Location table. What I want to accomplish is have a single entry for one latitude and longitude and update the windspeed, temperature and winddirection fields with the latest data.
但是通过这样做,正在发生的事情是位置被复制。这意味着我在位置表中有多个相同经度和纬度的条目,但具有不同的温度和风速数据。我想要完成的是一个经纬度的单一条目,并使用最新数据更新风速、温度和风向字段。
Please HELP!
请帮忙!
采纳答案by Vashishtha Jogi
Solved :-)
解决了 :-)
public Location saveLocation(Location loc) {
eManager.clear();
eManager.setFlushMode(FlushModeType.COMMIT);
//eManager.setFlushMode(FlushModeType.COMMIT);
Query q = eManager.createNamedQuery("findLocationByLatLong");
q.setParameter("latitude", loc.getLatitude());
q.setParameter("longitude", loc.getLongitude());
try {
Location dummy = (Location) q.getSingleResult();
eManager.clear();
// eManager.flush();
return dummy;
} catch (NoResultException ex) {
Logger.getLogger("WeatherRecorderBean.class").log(Level.WARNING,
ex.getMessage());
eManager.clear();
eManager.merge(loc);
eManager.flush();
return loc;
} catch (Exception ex) {
Logger.getLogger("WeatherRecorderBean.class").log(Level.WARNING, "Should never get here! "+ex.getMessage());
return null;
}
}
I create a new function for saving Location
.
I also had a few synchronization problems. I had this function in a MDB onMessage()
function. So before one onMessage()
was finished another one started hence creating duplicate entries!
我创建了一个用于保存Location
. 我也有一些同步问题。我在 MDBonMessage()
函数中有这个函数。所以在一个onMessage()
完成之前,另一个开始创建重复的条目!
Hope this helps somebody in future!
希望这对将来的人有所帮助!
回答by Pascal Thivent
You're cascading ALL
operations from Record
to Location
so when you merge a new Record
andits new parent Location
, there is no need to merge a transient Location
again (or you'll get duplicate lines).
您正在ALL
从Record
to级联操作,Location
因此当您合并新的Record
和它的新父级时Location
,无需Location
再次合并瞬态(否则您将获得重复的行)。
I've put some comments in your code below (I didn't fix everything, there are IMO more problems but the suggested changes should at least remove the creation of duplicates Location
entries):
我在下面的代码中添加了一些注释(我没有解决所有问题,IMO 还有更多问题,但建议的更改至少应该删除重复Location
条目的创建):
if(dbLoc==null) {
dbLoc = new Location();
dbLoc.setLatitude(loc.getLatitude());
dbLoc.setLongitude(loc.getLongitude());
r.setParent(dbLoc);
// add changes on the dbLoc here
eManager.merge(r); // here you're merging r and the dbLoc
//loc.setLastModifiedDate(r.getMtime()); // why are you modifying the line from the file?
//loc.setWindDirection(r.getWindDirection());
//loc.setWindSpeed(r.getWindSpeed());
//loc.setTemperature(r.getTemperature());
//eManager.persist(loc);
//System.out.println("Location id : "+loc.getId());
//eManager.merge(dbLoc); // here you're merging a transient dbLoc again
}