使用Spring-Data Repositories和JPQL Query的JPA Discriminator

时间:2016-04-16 13:32:09

标签: java mysql jpa eclipselink spring-data

我在MySQL中有两个实体,如下所示。 nnm_tran的主键是id和source的组合。讨价还价的主要关键是实际上是nnm_tran表的外键链接 Entities. Bargains inherits from nnm_tran

我正在尝试使用JPA继承来表示这些。

nnm_tran实体

@Entity
@Table(name = "nnm_tran")
@IdClass(CommonTransactionKey.class)
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn(name = "bargain_flag", discriminatorType = DiscriminatorType.CHAR)
@DiscriminatorValue("N")
public class CommonTransaction {

    @Id
    @Column(name = "id", nullable = false)
    private String transactionId;

    @Column(name = "plan_number", nullable = false)
    private String planNumber;

    @Column(name = "tran_date")
    private LocalDateTime transactionDatetime;

    @Column(name = "bargain_flag")
    private String bargainFlag;
    ...
}

讨价还价实体

@Data
@EqualsAndHashCode(callSuper = true)
@Entity
@Table(name = "bargains")
@DiscriminatorValue("B")
@PrimaryKeyJoinColumns({ @PrimaryKeyJoinColumn(name = "nnm_tran_id", referencedColumnName = "id"), @PrimaryKeyJoinColumn(name = "nnm_tran_source", referencedColumnName = "source") })
public class Bargain extends CommonTransaction implements Serializable {

    @Column(name = "unit_price")
    private BigDecimal unitPrice;

    @Column(name = "client_price")
    private BigDecimal clientPrice;
    ...
}

我认为到目前为止这一切都是正确的。当我附加一个带有自定义查询的spring-data存储库时,我的问题出现了。

存储库

public interface CommonTransactionRepository extends CrudRepository<CommonTransaction, CommonTransactionKey> {

    @Query("select t from CommonTransaction t left join IoPlan p ON t.planNumber = p.planNumber "
        + "where (p.planNumber is NULL or p.planNumber = '') "
        + "and t.transactionDatetime between ?1 and ?2 "
        + "and t.cancelled = false")
    public Iterable<CommonTransaction> findOrphanedTransactionsByTranDate(LocalDateTime fromDate, LocalDateTime toDate);
   ...
}

当它被代理并且执行该方法时,它会生成SQL语句
SELECT DISTINCT nnm_tran.bargain_flag FROM nnm_tran t1 LEFT OUTER JOIN io_plan t0 ON (t1.plan_number = t0.plan_number) WHERE ((((t0.plan_number IS NULL) OR (t0.plan_number = ?)) AND (t1.tran_date BETWEEN ? AND ?)) AND (t1.CANCELLED = ?))

这个问题是nnm_tran表别名为t1但是鉴别器列引用了完整的表名nnm_tran.bargain_flag结果很可爱

UnitOfWork(17171249)--Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.5.2.v20140319-9ad6abd): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column 'nnm_tran.bargain_flag' in 'field list'

问题是,我做错了什么,或者这是spring-data和/或eclipselink中的错误?

版本:spring-data 1.7.2,Eclipselink 2.5.2,MySQL 5.6.28

1 个答案:

答案 0 :(得分:1)

使用@manish的示例应用程序作为起点,我开始重新分析缺少的复杂性,并迅速发现导致流氓SQL的事情。这归结于我在JPQL中执行的连接

注意:如果您从未来来过这里,请忽略此答案的其余部分,而是使用@Chris的评论。

大多数时候我不需要查看甚至考虑可以在@Query

中看到的IoPlan表
@Query("select t from CommonTransaction t left join IoPlan p ON t.planNumber = p.planNumber "
    + "where (p.planNumber is NULL or p.planNumber = '') "
    + "and t.transactionDatetime between ?1 and ?2 "
    + "and t.cancelled = false")

因此该表不是CommonTransaction实体的一部分作为字段。即使是这个查询的结果也不是很关心,因为它只是CommonTransaction的一次性关注,而IoPlan表中的没有关联连接。

当我将连接添加回来自@manish的示例应用程序时,它的所有内容都与我的应用程序在EclipseLink中的方式相同,但以不同的方式打破了Hibernate。 Hibernate需要一个字段供您加入,如果您要求我违背在@Query中编写联接的目的。实际上在Hibernate中你必须纯粹在JPA中定义连接,所以你也可以使用点符号在JPQL中访问它。

无论如何,按照这个想法,我尝试添加一个虚拟字段来保存IoPlan实体中的CommonTransaction,它几​​乎可以工作。它默认了一些连接逻辑,但它更接近

SELECT DISTINCT t1.bargain_flag FROM nnm_tran t1 LEFT OUTER JOIN io_plan t0 ON ((t0.ID = t1.IOPLAN_ID) AND (t1.plan_number = t0.plan_number)) WHERE ((((t0.plan_number IS NULL) OR (t0.plan_number = ?)) AND (t1.tran_date BETWEEN ? AND ?)) AND (t1.CANCELLED = ?))

在这种情况下,t1.IOPLAN_IDt0.ID不存在。所以我最终在我的CommonTransaction实体

中定义了整个联接
    @OneToOne
    @JoinColumn(insertable = false, updatable = false, name = "plan_number", referencedColumnName = "plan_number")
    private IoPlan ioPlan;
瞧,瞧,它开始工作了。它不漂亮,现在我有一个冗余连接条件

LEFT OUTER JOIN io_plan t1 
ON ((t1.plan_number = t0.plan_number) AND (t0.plan_number = t1.plan_number)) 

但我可以解决这个问题。仍然很烦人,我必须为它定义一个字段,我实际上并不想要或不需要它,更不用说这个查询的结果是返回CommonTransaction没有IoPlan的实体所以该字段将永久为空。