我在MySQL中有两个实体,如下所示。 nnm_tran的主键是id和source的组合。讨价还价的主要关键是实际上是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
答案 0 :(得分:1)
使用@manish的示例应用程序作为起点,我开始重新分析缺少的复杂性,并迅速发现导致流氓SQL的事情。这归结于我在JPQL中执行的连接
注意:如果您从未来来过这里,请忽略此答案的其余部分,而是使用@Chris的评论。
大多数时候我不需要查看甚至考虑可以在@Query
@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_ID
和t0.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
的实体所以该字段将永久为空。