【问题标题】:JOIN with JPA: what queries should I write to get the same result I get in JDBC?JOIN with JPA:我应该编写哪些查询才能获得与 JDBC 相同的结果?
【发布时间】:2018-02-20 04:59:57
【问题描述】:

我是 JPA 的新手,我正在尝试将我在 JDBC 中使用的方法转换为 JPA 作为练习。 这应该计算我的数据库中所有恒星的所有质量并更新相对质量列。为了计算质量,我需要一颗恒星的温度和它在 350 微米波段的通量值。问题是我将这些数据保存在数据库的不同表中,因此我必须使用 JOIN。

JDBC 方法

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 方法尝试

在这里,我需要在Star 对象和Flux 对象之间创建一个JOIN

这是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);
        }
    }
}

我应该写什么查询?

【问题讨论】:

  • 发布您的实体的代码。并且不要使用原始类型。泛型自 Java 5 开始就存在于 Java 中,13 年前的 2004 年发布。是时候赶上来了。
  • @JBNizet 按照您的建议添加了代码 :)
  • 好的。要使用连接,您需要实体之间的关联。 Flux 的源应该是 Star 类型,并使用 ManyToOne 或 OneToOne 注释(取决于基数)。您错过了 JPA 的要点,它将数据库映射为实体的互连图:员工属于公司,公司有地址,链接到城市,包含地区等。

标签: java postgresql jpa jdbc


【解决方案1】:

您的模型未正确映射。如果要使用 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

}

然后您将 Flux 类标记为具有单独的 IdClass:

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

  @Id
  private Source source;

  @Id
  private BigDecimal band;

 // With constructors, getters and setters

}

警告绝对有必要为它实现一个正确的 equals() 方法,以确保对象在持久化后与自身相同并且如果从数据库中检索则相同。 p>

确保它看起来像这样:

  @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% 确定最后一个的语法。)

【讨论】:

  • 感谢您非常有用的回答,我正要测试它!我有一个问题:一旦我执行查询并获得List,我如何访问结果?到目前为止,我一直习惯于只获取我已经拥有的对象,而不是为查询“定制”。 (所以我会运行 list.get(index) 并将其转换为我需要的类)。 [例如。相当于 JDBC 中的 resultSet.getBigDecimal("columnName")]
  • 如果您在 JPQL 查询中选择多个表达式,您将获得一个对象数组。如果您不希望这样,只需返回 SELECT DISTINCT s。我建议编写一个单元测试来处理 JPQL 查询。我不认为有任何好的用户界面(但如果你找到了请告诉我)
【解决方案2】:

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 <> ?"

【讨论】:

    猜你喜欢
    • 2011-12-04
    • 2020-10-31
    • 1970-01-01
    • 2021-09-26
    • 2020-08-03
    • 2023-01-28
    • 1970-01-01
    • 1970-01-01
    • 2015-02-20
    相关资源
    最近更新 更多