我们必须使用 JPASQLQueries 来处理在任意列和子查询上进行连接的复杂查询。一个这样的查询从数据库返回一个字符串,并将其投影到一个枚举到 DTO 中。当尝试为该特定值调用 DTO 上的设置器时,此映射因“非法参数类型”而失败。
映射到与 DTO 工作结构相同的实体。
这是我使用的查询:
final QOrderHeader orderHeader = QOrderHeader.orderHeader;
QOrderHeader orderGroup = new QOrderHeader("orderGroup");
QOrderPosition orderPosition = QOrderPosition.orderPosition;
SQLTemplates templates = new OracleTemplates();
JPASQLQuery orderQuery = new JPASQLQuery(em, templates);
Predicate preds = OrderHeaderPredicate.matchesSearchCriteria(searchCriteria);
// select from entity metadata, bind onto DTO, use entity metadata
List<OrderHeaderDTO> orderHeaderWithInfos = orderQuery //
.from(orderHeader) //
.list(Projections.bean(OrderHeaderDTO.class, //
.. some values left out
orderHeader.orderGroup, //
orderHeader.orderTypeCode, //
orderHeader.stateCode, //
orderHeader.stateReason, //
orderHeader.testNumber0,
orderHeader.testNumber1
)); //
尝试在 DTO (stateCode dito) 上设置 orderTypeCode 时查询失败。
这是实体:
@Entity
@Table(name = "ORDERHEADER", uniqueConstraints = { @UniqueConstraint(columnNames = { "orderCode" }) })
public class OrderHeader extends BaseEntity {
protected String orderGroup; // no problem
protected OrderTypeCode orderTypeCode; // illegal argument type when calling setter
protected OrderHeaderState stateCode; // illegal argument type when calling setter
protected float testNumber0;
protected double testNumber1;
public OrderHeader() {
//DO NOT REMOVE. REQUIRED BY HIBERNATE.
}
public OrderHeader(OrderHeaderDTO dto) {
super(dto);
orderGroup = dto.getOrderGroup();
orderTypeCode = dto.getOrderTypeCode();
stateCode = dto.getStateCode();
testNumber0 = dto.getTestNumber0();
testNumber1 = dto.getTestNumber1();
}
@Override
@Id
@Column(name = "ID")
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "HibSOrderheader")
@SequenceGenerator(name = "HibSOrderheader", sequenceName = "SOrderheader", allocationSize = 1)
public long getId() {
return id;
}
@Size(max = 30)
@Column(name = "ORDERGROUP", length = 30)
public String getOrderGroup() {
return orderGroup;
}
public void setOrderGroup(String orderGroup) {
this.orderGroup = orderGroup;
}
@Enumerated(EnumType.STRING)
@Column(name = "ORDERTYPECODE")
public OrderTypeCode getOrderTypeCode() {
return orderTypeCode;
}
public void setOrderTypeCode(OrderTypeCode orderTypeCode) {
this.orderTypeCode = orderTypeCode;
}
@Enumerated(EnumType.STRING)
@Column(name = "STATECODE", columnDefinition = "varchar2(20)")
public OrderHeaderState getStateCode() {
return stateCode;
}
protected void setStateCode(OrderHeaderState stateCode) {
this.stateCode = stateCode;
}
@Column(name = "TESTNUMBER0")
public float getTestNumber0() {
return testNumber0;
}
public void setTestNumber0(float testNumber0) {
this.testNumber0 = testNumber0;
}
@Column(name = "TESTNUMBER1")
public double getTestNumber1() {
return testNumber1;
}
public void setTestNumber1(double testNumber1) {
this.testNumber1 = testNumber1;
}
}
和枚举:
public enum OrderTypeCode {
NORMAL,
INVENTORY,
TRANSFER,
RELOCATE
}
和 DTO:
public class OrderHeaderDTO extends BaseEntityDTO {
private String orderGroup;
private OrderTypeCode orderTypeCode;
private OrderHeaderState stateCode;
private float testNumber0;
private double testNumber1;
public OrderHeaderDTO() {
super();
}
@QueryProjection
public OrderHeaderDTO(IBaseEntity base, String orderCode,
String orderGroup, OrderTypeCode orderTypeCode, int hostId, int priority, String creationMode,
String shippingMode, String stagingArea, Date requestedDeliveryTime, String deliveryNote,
String deliveryCode, String customerCode, String customerOrderCode, String headerText,
OrderHeaderState stateCode, String stateReason, Date stateTime, List<OrderPositionDTO> orderPosition, float testNumber0,
double testNumber1) {
super(base);
this.orderGroup = orderGroup;
this.orderTypeCode = orderTypeCode;
this.stateCode = stateCode;
this.testNumber0 = testNumber0;
this.testNumber1 = testNumber1;
}
@QueryProjection
public OrderHeaderDTO(long id, long version, Date creationTime, Date lastUpdateTime,
String orderGroup, OrderTypeCode orderTypeCode, OrderHeaderState stateCode,
float testNumber0, double testNumber1) {
super(id, version, creationTime, lastUpdateTime);
this.orderGroup = orderGroup;
this.orderTypeCode = orderTypeCode;
this.stateCode = stateCode;
this.testNumber0 = testNumber0;
this.testNumber1 = testNumber1;
}
/**
* Copy constructor (shallow copy)
* @param arg Original value
*/
public OrderHeaderDTO(OrderHeaderDTO arg) {
super(arg);
orderGroup = arg.orderGroup;
orderTypeCode = arg.orderTypeCode;
creationTime = arg.creationTime;
stateCode = arg.stateCode;
testNumber0 = arg.testNumber0;
testNumber1 = arg.testNumber1;
}
public String getOrderGroup() {
return orderGroup;
}
@Enumerated(EnumType.STRING)
public OrderTypeCode getOrderTypeCode() {
return orderTypeCode;
}
public OrderHeaderState getStateCode() {
return stateCode;
}
public void setOrderGroup(String orderGroup) {
this.orderGroup = orderGroup;
}
public void setOrderTypeCode(OrderTypeCode orderTypeCode) {
this.orderTypeCode = orderTypeCode;
}
public void setStateCode(OrderHeaderState stateCode) {
this.stateCode = stateCode;
}
public float getTestNumber0() {
return testNumber0;
}
public void setTestNumber0(float testNumber0) {
this.testNumber0 = testNumber0;
}
public double getTestNumber1() {
return testNumber1;
}
public void setTestNumber1(double testNumber1) {
this.testNumber1 = testNumber1;
}
}
最后抛出堆栈跟踪:
org.springframework.dao.InvalidDataAccessApiUsageException: argument type mismatch; nested exception is java.lang.IllegalArgumentException: argument type mismatch
at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:296)
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:107)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:403)
at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:58)
at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:213)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:163)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.data.jpa.repository.support.LockModeRepositoryPostProcessor$LockModePopulatingMethodIntercceptor.invoke(LockModeRepositoryPostProcessor.java:92)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:91)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:204)
at com.sun.proxy.$Proxy55.searchOrderWithInfoForCriteria(Unknown Source)
at com.stoecklin.wms.dbunittest.implementation.repository.OrderHeaderCustomRepoIT.test_noCriteria(OrderHeaderCustomRepoIT.java:188)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:74)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:83)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:231)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:88)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:71)
at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:174)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
Caused by: java.lang.IllegalArgumentException: argument type mismatch
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at com.mysema.util.BeanMap.put(BeanMap.java:330)
at com.mysema.util.BeanMap.put(BeanMap.java:51)
at com.mysema.query.types.QBean.newInstance(QBean.java:250)
at com.mysema.query.support.NumberConversions.newInstance(NumberConversions.java:65)
at com.mysema.query.jpa.FactoryExpressionTransformer.transformTuple(FactoryExpressionTransformer.java:50)
at org.hibernate.hql.internal.HolderInstantiator.instantiate(HolderInstantiator.java:95)
at org.hibernate.loader.custom.CustomLoader.getResultList(CustomLoader.java:431)
at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2342)
at org.hibernate.loader.Loader.list(Loader.java:2337)
at org.hibernate.loader.custom.CustomLoader.list(CustomLoader.java:338)
at org.hibernate.internal.SessionImpl.listCustomQuery(SessionImpl.java:1827)
at org.hibernate.internal.AbstractSessionImpl.list(AbstractSessionImpl.java:231)
at org.hibernate.internal.SQLQueryImpl.list(SQLQueryImpl.java:157)
at org.hibernate.ejb.QueryImpl.getResultList(QueryImpl.java:268)
at com.mysema.query.jpa.sql.AbstractJPASQLQuery.getResultList(AbstractJPASQLQuery.java:193)
at com.mysema.query.jpa.sql.AbstractJPASQLQuery.list(AbstractJPASQLQuery.java:224)
at com.stoecklin.wms.repository.implementation.OrderHeaderRepositoryImpl.searchJPASQLIntoDTO(OrderHeaderRepositoryImpl.java:347)
at com.stoecklin.wms.repository.implementation.OrderHeaderRepositoryImpl.searchOrderWithInfoForCriteria(OrderHeaderRepositoryImpl.java:517)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.executeMethodOn(RepositoryFactorySupport.java:344)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:319)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:96)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:260)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:94)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:155)
... 37 more
现在有以下问题:
- 我们如何为投影中的某些列(即枚举)提供类型转换表达式?
- 将生成的实体元数据(来自 apt-maven-plugin)用于 JPASQL 是否可行?
任何帮助表示赞赏。
我还探索了各种其他变体,但效果并不好:
- 使用本机 SQL 元数据(来自 querydsl-maven-plugin)进行投影并没有太大变化,但揭示了额外的类型映射问题。
- 从本机 SQL 元数据中选择具有相同的行为。
- 投影到实体中是可行的,但这不是目的,因为我们希望直接填充 DTO(也使用合成值)。如果我们使用实体,那么我们必须在之后将实体复制到 DTO 中。
编辑:使用的版本:
QueryDSL:3.2.4
Spring:3.2.2.RELEASE
Hibernate:4.2.6.Final
我们要使用的最终查询应该是这样的(仅限 SQL):
SELECT
oh.*,
(select count(id) -- correlated subquery
from orderposition p
where p.orderHeaderID = oh.id) as nofPosPerOrder,
og.mostImportantPrioInGroup,
og.earliestDeliveryTime,
og.earliestCreationTime
FROM orderHeader oh LEFT JOIN -- left join with aggregating query for order group
(SELECT g.orderGroup,
min(g.priority) as mostImportantPrioInGroup,
min(g.requestedDeliveryTime as earliestDeliveryTime,
min(g.requestedCreationTime as earliestCreationTime
FROM orderHeader g
GROUP by g.orderGroup) og
ON oh.orderGroup = og.orderGroup
WHERE ?????
ORDER BY .