JOA与JPA:我应该写什么查询以获得与JDBC相同的结果?

问题描述:

我是JPA的新手,我试图将我在JDBC中使用的方法转换为JPA作为练习。 这应该是计算我的数据库中所有恒星的所有质量并更新相对质量列。为了计算质量,我需要一颗恒星的温度和它在350um波段的通量值。问题是我将这些数据保存在我的数据库的不同表中,因此我必须使用JOIN。JOA与JPA:我应该写什么查询以获得与JDBC相同的结果?

JDBC APPROACH

public void updateAllMasses() { 
    String selectQuery = "SELECT s.starid, s.temperature, f.value" + 
      " FROM star s JOIN flux f " + 
      "ON s.starid = f.source " + 
      "WHERE f.band = 350.00 AND f.value != 0"; 
    try { 
     ResultSet resultSet = databaseUtilJDBC.dbExecuteQuery(selectQuery); 
     while (resultSet.next()) { 
      String starId = resultSet.getString("starid"); 
      Double flux350 = resultSet.getDouble("value"); 
      Double temp = resultSet.getDouble("temperature"); 
      if (temp != 0) { 
       String updateQuery = "UPDATE star " + 
         "SET mass = 0.053 * " + flux350 + " * (100) * (EXP(41.14/" + temp + ") - 1) " 
         + "WHERE starid = '" + starId + "';"; 
       databaseUtilJDBC.dbExecuteUpdate(updateQuery); 
      } 
     } 
    } catch (SQLException e) { 
     e.printStackTrace(); 
    } 
} 

JPA APPROACH未遂

在这里,我需要做出某种方式对象JOIN之间Star对象和Flux

以下是Star类的草稿。

@Entity 
@Table(name = "star") 
public class Star implements Serializable { 
    private static final long serialVersionUID = 1L; 
    @Id 
    @Column(name = "starid", nullable = false) 
    private String starId; 
    @Column(name = "temperature") 
    private BigDecimal temperature; 
    @Column(name = "mass") 
    private BigDecimal mass; 

    // With all constructors, getters and setters 

} 

下面是我使用助焊剂的实体类:

@Entity 
@Table(name = "flux") 
public class Flux implements Serializable { 

    private static final long serialVersionUID = 1L; 

    @EmbeddedId private FluxId fluxId = new FluxId(); 
    @Column(name = "value") 
    private BigDecimal value; 

    // With constructors, getters and setters 

} 

凭借其idclass:

@Embeddable 
public class FluxId implements Serializable { 
    private static final long serialVersionUID = 1L; 
    @Column(name = "source", nullable = false) 
    private String source; 
    @Column(name = "band", nullable = false) 
    private BigDecimal band; 

    // With constructors, getters and setters 

} 

这里是最后我尝试使用JPA:

public void updateAllMasses() { 
    String query = "???"; // Query to get all values I need 
    List list = databaseUtilJPA.dbExecuteQuery(query); 
    for (Object aList : list) { 
     Star star = (Star) aList; // Here I would just get a Star object... while I would need also it's flux value at 350 band! 
     if (!star.getTemperature().equals(BigDecimal.valueOf(0))) { 
      query = "UPDATE Star star SET star.mass = 0.053 * flux.value * 100 * (EXP(41.14/star.temperature) - 1)" + 
        " WHERE star.starId = '" + star.getStarId() + "'"; 
      databaseUtilJPA.dbExecuteUpdate(query); 
     } 
    } 
} 

我应该写什么查询?

+0

发布您的实体的代码。并且不要使用原始类型。 Java自从13年前发布于2004年的Java 5以来就一直存在于泛型中。赶上时间。 –

+0

@JBNizet按照你的建议添加了代码:) – Robb1

+1

好的。要使用连接,您需要实体之间的关联。 Flux的源应该是Star类型的,并且可以使用ManyToOne或OneToOne注释(取决于基数)。您错过了JPA的观点,即将数据库映射为实体的互连图:员工属于公司,该公司拥有地址,链接到城市,包含地区等。 –

您的模型不正确映射。如果您想使用来自JPA的连接,则必须正确声明它们。您FLuxId有看起来像这样:

@Embeddable 
public class FluxId implements Serializable { 

    @ManyToOne 
    @JoinColumn(name="source") 
    private Star star; 

    @Column(name = "band", nullable = false) 
    private BigDecimal band; 

    // With constructors, getters and setters 

} 

然后你标记您的流量类为具有独立IdClass:

@Entity 
@IdClass(FluxId.class) 
public class Flux {   

    @Id 
    private Source source; 

    @Id 
    private BigDecimal band; 

// With constructors, getters and setters 

} 

警告这是绝对必要的,你执行一个正确的equals()方法吧确保持久化后的对象与其自身相同如果从数据库中检索,则是相同的。

确保它看起来是这样的:

@Override 
    public boolean equals(Object obj) { 

    if (obj == this) return true;  

    if (obj == null || !(obj.getClass().equals(this.getClass()))) 
     return false; 

    FluxID otherFluxId = (FluxID)obj; 

    if (this.getSource() == null 
      || otherFluxId.getSource() == null 
      || this.getBand() == null 
      || otherFluxId.getBand() == null) 
     return false; 

    return this.getSource().equals(otherFluxId.getSource()) && 
      this.getBand().equals(otherFluxId.getBand()); 
} 

@Override 
public int hashCode() { 

    if (this.getSource() == null && this.getBand() == null) return super.hashCode(); 

    return (this.getSource() != null ? this.getSource().hashCode() : 0)^
      (this.getBand() != null ? this.getBand().hashCode() : 0); 

} 

请注意,这个实现存在缺陷坚持它比坚持之后不同之前的hashCode()。我不知道如何避免这种情况,但如果您在保存实体之前将其保存在某个集合中,则必须小心。

一旦你做这一切的查询变为:

SELECT f.source.id, f.source.temperature, f.value 
FROM Flux f 
WHERE f.band = 350.00 AND f.value != 0 

如果你愿意,你可以与对象的工作对象,太:肯定有关语法

SELECT f.source 
FROM Flux f 
WHERE f.band = 350.00 AND f.value != 0 

(不是100% )

+0

感谢您的非常有用的答案,我即将测试它! 我有一个问题:一旦我执行查询并获得'List',我该如何访问结果?到目前为止,我已经习惯于只获取我已有的对象,而不是为查询定制的对象。 (所以我会有'list.get(index)'并将其转换为我需要的类)。 [例如。相当于JDBC中的'resultSet.getBigDecimal(“columnName”)'] – Robb1

+0

如果您在JPQL查询中选择多个表达式,您将获得一组对象。如果你不想那么简单地返回SELECT DISTINCT s。我会建议编写一个单元测试来玩JPQL查询。我不认为有任何好的用户界面(但请让我知道,如果你找到一个) – fhossfel

JPA也支持原生JDBC查询。

但是,如果你有一个one-to-many关系,那么你可以在jpa使用注释定义它,无论是在你的StarFlux类或两者兼而有之。

@Entity 
public class Star { 
    @OneToMany 
    private List<Flux> fluxes = new ArrayList<>(); 
} 

@Entity 
public class Flux { 
    @ManyToOne 
    private Star star; 
} 

所以,那么你的查询可以:

"select star s from Star join s.fluxes f where f.band = ? and f.value <> ?"