【问题标题】:How to handle JPA Many-to-One Relation?如何处理 JPA 多对一关系?
【发布时间】:2010-09-29 20:39:14
【问题描述】:

我正在设计一个收集天气数据的应用程序。我有 2 个 POJO 对象“位置”和“记录”。 Location 包含有关纬度和经度以及当前天气状况的信息,Record 包含特定位置随时间推移的所有天气信息,因此与 Location 具有多对一的关系。我拥有的类的定义如下:

位置.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;
}

和 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;
}

而我传入的数据格式为:

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

现在每当我得到一条记录时,我都想将它插入到记录表中。由于我有 Location 的外键,我还将添加 Location 表的 locationId。 另一件事,位置表没有预先填充。因此,每当有新记录出现时,我首先将其插入 Location 表中,然后使用外键填充 Record 表。而且我不想在位置表中重复位置条目。如您所见,位置表还将包含最新的温度、风速和风向数据。

我正在使用以下代码来实现:

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);

}

但是通过这样做,正在发生的事情是位置被复制。这意味着我在位置表中有多个相同经度和纬度但温度、风速数据不同的条目。我想要完成的是为一个纬度和经度设置一个条目,并使用最新数据更新风速、温度和风向字段。

请帮忙!

【问题讨论】:

    标签: java jpa ejb-3.1 many-to-one


    【解决方案1】:

    您将ALL 操作从Record 级联到Location,因此当您合并一个新的Record 它的新父级Location 时,无需合并一个再次瞬态Location(否则你会得到重复的行)。

    我在下面的代码中添加了一些 cmets(我没有解决所有问题,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
    
    }
    

    【讨论】:

    • 您好,感谢您的帮助。我更改了代码(也相应地更新了我的问题中的上述代码)。但它仍然给了我重复的条目。 :(
    • @Vashishtha 我可能遗漏了一些东西,但至少,以上内容不正确。激活 SQL 日志记录以查看发生了什么。我不能为你这样做:)
    • @Pascal 我启用了 SQL 日志记录,我看到的只是一系列插入语句:(如果任何列的任何单个值不同,它就会插入一个新行。
    • @Vashishtha 哪一部分没有按预期工作?当位置已经存在时?什么时候没有?两者都有?
    • @Pascal 两者。我尝试只保存位置以供检查。但这也会使位置表出现重复条目​​。
    【解决方案2】:

    解决了:-)

    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;
        }
    }
    

    我创建了一个新函数来保存Location。 我也有一些同步问题。我在 MDB onMessage() 函数中有这个函数。因此,在一个 onMessage() 完成之前,另一个就开始创建重复条目!

    希望这对将来的人有所帮助!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-07-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-01-28
      • 2019-04-21
      相关资源
      最近更新 更多