【问题标题】:JPA Criteria Equivalent Query Subquery greatest or maxJPA 标准等效查询子查询最大或最大
【发布时间】:2013-01-19 14:40:04
【问题描述】:

此 SQL 查询完全符合执行时的需要。我正在寻找等效的 JPA 标准?基本上从一对多关系中获取设备实体和最新(最大)的 GPS 坐标

SELECT DISTINCT t0.DEVICE_I, t0.ACTIVE_S, t0.APP_CONFIG_I, t0.PREV_APP_CONFIG_I, 
t0.APP_CONFIG_CONFIRMED_S, t0.APP_CONFIG_RECEIPT_D, t0.APP_CONFIG_SENT_S, t0.CHANNEL_X, 
t0.CREATION_TS, t0.CREATION_USER_I, t0.DEST_QUEUE_C, t0.DIVISION_C, t0.LAN_IP_X, 
t0.LAST_UPDATE_TS, t0.LAST_UPDATE_USER_I, t0.MOBILE_IP_X, t0.ORIGIN_MP_I, 
t0.PREV_CHANNEL_X, t0.TELEPHONE_NUMBER_X, t0.VERSION_X, t1.DEVICE_I, t1.COORDINATE_I, 
t1.CREATION_TS, t1.CREATION_USER_I, t1.GENERATED_TS, t1.GPGGA_X, t1.GPGSA_X, 
t1.GPGSV_X, t1.GPRMC_X, t1.GPVTG_X, t1.LAST_UPDATE_TS, t1.LAST_UPDATE_USER_I, 
t1.WORK_ORDER_NUMBER_I 

FROM DEVICE t0, GPS_COORD t1 

WHERE  t0.DEVICE_I = t1.DEVICE_I AND  
t1.GENERATED_TS IN ( select max(GENERATED_TS)
       from GPS_COORD
      group by DEVICE_I )
ORDER BY t1.DEVICE_I ASC, t1.GENERATED_TS DESC

下面的 java 代码几乎就在那里,并生成以下查询,其中缺少 GpsCoord 所需的连接字段。如果完成获取,则包含这些字段,但稍后在子查询的 where 子句中需要加入:

    CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();

    CriteriaQuery<Device> cq = criteriaBuilder.createQuery(Device.class);

    Root<Device> device = cq.from(Device.class);
    cq.distinct(true);

    Join<Device, GpsCoord> j = device.join(Device_.gpsCoords, JoinType.LEFT);
    //Fetch<Device,GpsCoord> f = device.fetch(Device_.gpsCoords);
    
    CriteriaQuery<Device> select = cq.select(device);


    Subquery<Timestamp> sq = cq.subquery(Timestamp.class);
    Root<GpsCoord> gpsCoord = sq.from(GpsCoord.class);

    sq.select(criteriaBuilder.greatest(gpsCoord.get(GpsCoord_.generatedTs)));
    sq.groupBy(gpsCoord.get(GpsCoord_.device).get(Device_.deviceI));
    select.where(j.get(GpsCoord_.generatedTs).in(sq));
    
    
    TypedQuery<Device> query = this.getEntityManager().createQuery(cq);

生成的查询缺少加入操作中的 GpsCoord 文件

SELECT DISTINCT t0.DEVICE_I, t0.ACTIVE_S, t0.APP_CONFIG_I, t0.PREV_APP_CONFIG_I,     
t0.APP_CONFIG_CONFIRMED_S, t0.APP_CONFIG_RECEIPT_D, t0.APP_CONFIG_SENT_S, 
t0.CHANNEL_X,     t0.CREATION_TS, t0.CREATION_USER_I, t0.DEST_QUEUE_C, t0.DIVISION_C, 
t0.LAN_IP_X, t0.LAST_UPDATE_TS, t0.LAST_UPDATE_USER_I, t0.MOBILE_IP_X, t0.ORIGIN_MP_I, 
t0.PREV_CHANNEL_X, t0.TELEPHONE_NUMBER_X, t0.VERSION_X 

FROM SPW_OWN.DEVICE t0, SPW_OWN.GPS_COORD t1 

WHERE (t1.GENERATED_TS IN (SELECT MAX(t2.GENERATED_TS) FROM SPW_OWN.GPS_COORD t2, 
SPW_OWN.DEVICE t3 WHERE t2.DEVICE_I = t3.DEVICE_I GROUP BY t3.DEVICE_I)) 

AND t0.DEVICE_I = t1.DEVICE_I(+)

像这样使用 CriteriaQuery 中的多选在执行时会生成正确的 SQL,但会出现以下异常

Caused by: java.lang.Exception: java.lang.RuntimeException: Can not find constructor for "class Device" with argument types "[class java.lang.String ... java.sql.Timestamp]" to fill data.   

在调用后被抛出:

CriteriaQuery<Device> select = cq.multiselect(
                    //"deviceI",
                    device.get(Device_.deviceI),
                    //"activeS",
                    device.get(Device_.activeS),
                    //"appConfigConfirmedS",
                    device.get(Device_.appConfigConfirmedS),
                    //"appConfigReceiptD",
                    device.get(Device_.appConfigReceiptD),
                    //"appConfigSentS",
                    device.get(Device_.appConfigSentS),
                    //"channelX",
                    device.get(Device_.channelX),
                    //"destQueueC",
                    device.get(Device_.destQueueC),
                    //"lanIpX",
                    device.get(Device_.lanIpX),
                    //"mobileIpX",
                    device.get(Device_.mobileIpX),
                    //"prevChannelX",
                    device.get(Device_.prevChannelX),
                    //"telephoneNumberX",
                    device.get(Device_.telephoneNumberX),
                    //"versionX"
                    device.get(Device_.versionX),
                    //"device",
                    j.get(GpsCoord_.device),
                    //"gpggaX",
                    j.get(GpsCoord_.gpggaX),
                    //"gprmcX",
                    j.get(GpsCoord_.gprmcX),
                    //"gpgsaX",
                    j.get(GpsCoord_.gpgsaX),
                    //"gpgsvX",
                    j.get(GpsCoord_.gpgsvX),
                    //"gpvtgX"
                    j.get(GpsCoord_.gpvtgX),
                    j.get(GpsCoord_.generatedTs)
                    );

【问题讨论】:

    标签: jpa websphere criteria ejb-3.1 openjpa


    【解决方案1】:

    几个问题,

    • 你打电话给 sq.from(GpsCoord.class);两次
    • 使用 device.fetch(Device_.gpsCoords);还将获取对象,并将其加入两次
    • 你的 IN 不正确,应该是,

    select.where(criteriaBuilder.get(path).in(sq));

    【讨论】:

    • 感谢 James 的提示,尤其是关于反转 .in 部分的提示。除了 GpsCoord 表中的字段没有从 Join 中返回之外,几乎所有内容都有效。通常我会调用有效的 fetch 方法,除了 where 子句需要 Join 对象。如果我包含两个调用,则查询会执行两个连接来终止查询。有没有办法像使用 Join 一样使用 Fetch 对象,或者使用 join 方法来获取所有 GpsCoord 字段?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-01
    • 1970-01-01
    • 2012-07-09
    相关资源
    最近更新 更多