Hibernate Ogm 2-й запрос на основе 1-го результата запроса, а также не может получить сопоставленные поля объекта (кроме id) после обновления

я использую Hibernate OGM (5.2.0.Alpha1) с MongoDB (3.6)

@Entity
@Table(name = "currency_master)
@JsonInclude(Include.NON_EMPTY)
public class CurrencyMaster{

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @JsonSerialize(using = ToStringSerializer.class)
    @Column(name = "CURRENCY_ID", unique = true, nullable = false)
    private ObjectId id;

    private String code;

    @OneToMany(mappedBy = "currencyMaster")
    private Set<PurchaseOrder> setOfPurchaseOrder;

    getter()
    setter()
}

@Entity
@Table(name = "purchase_order)
@JsonInclude(Include.NON_EMPTY)
public class PurchaseOrder {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @JsonSerialize(using = ToStringSerializer.class)
    @Column(name = "PURCHASE_ORDER_ID", unique = true, nullable = false)
    private ObjectId id;

    private String purchaseOrderNo;
    private Double total;

    @ManyToOne
    @JsonIgnore
    private CurrencyMaster currencyMaster;

    getter()
    setter()
}

Слой ДАО:

@SuppressWarnings("unchecked")
@Override
public <T> void update(T t) {
    try {
        Field[] fields = t.getClass().getDeclaredFields();
        Map<String, Object> mapUpdatedFields = new HashMap<String, Object>();
        for (Field field : fields) {
            field.setAccessible(true);
            mapUpdatedFields.put(field.getName(), field.get(t));
        }



        T newT = this.getById(mapUpdatedFields.get("id").toString(), t);

        mapUpdatedFields.remove("id");

        mapUpdatedFields.forEach((k, v) -> {
            if (!AccountingMethodUtils.isObjectisNullOrEmpty("update()", v)) {
                AccountingMethodUtils.setValueToField(newT, k, v);
            }
        });

        entityManager.merge(newT);

        } catch (Exception e) {
            e.printStackTrace();
            LOGGER.error(
                    "update() of DAO : (Error in outer try..catch) Error in updating record of {} and Error is {}.",
                    t.getClass(), e);
        }
    }


@Override
    public <T> List<T> executeQuery(String query, Integer startPosition, Integer noOfRecords, T t) {

        List<T> listOfT = new ArrayList<>();

        if (AccountingMethodUtils.isObjectisNullOrEmpty(startPosition, noOfRecords)) {
            listOfT = entityManager.createNativeQuery(query.toString(), t.getClass()).getResultList();
        } else {

            listOfT = entityManager.createNativeQuery(query.toString(), t.getClass()).setFirstResult(startPosition)
                    .setMaxResults(noOfRecords).getResultList();
        }
        return AccountingMethodUtils.isListIsNullOrEmpty(listOfT) ? new ArrayList<>() : listOfT;
    }

Сервисный уровень:

@Override
@Transactional
public String updatePurchaseOrder(AccountingRequestBody input) {

    PurchaseOrder purchaseOrder = AccountingMethodUtils.getObjectMapper().convertValue(input.getJsonOfObject(),
            PurchaseOrder.class);

    // Query : db.purchase_order.find( {'_id' : ObjectId("5ab88323191bb91e78f9e33d") } ,  { 'purchaseOrderNo' : 1, 'currencyMaster_CURRENCY_ID' : 1 , 'total' : 1 })
    StringBuilder sb = new StringBuilder().append("db.").append(AccountingVariableUtils.TABLE_NAME_FOR_PURCHASE_ORDER)
    .append(".find( {'_id' : ObjectId('" + purchaseOrder.getId().toString()
            + "') } ,  { 'purchaseOrderNo' : 1, 'currencyMaster_CURRENCY_ID' : 1 , 'total' : 1 })");

    List<PurchaseOrder> poFromDB = purchaseOrderDao.executeQuery(sb.toString(), null, null, new PurchaseOrder());

    if (!AccountingMethodUtils.isListIsNullOrEmpty(poFromDB)) {
            System.out.println("id before update : " + poFromDB.get(0).getCurrencyMaster().getId());      // Output:  5ab8830b191bb91e78f9e221
            System.out.println("code before update : " + poFromDB.get(0).getCurrencyMaster().getCode());  // Output:  INR
    }

    purchaseOrderDao.update(purchaseOrder);

    poFromDB = purchaseOrderDao.executeQuery(sb.toString(), null, null, new PurchaseOrder());

    if (!AccountingMethodUtils.isListIsNullOrEmpty(poFromDB)) {
            System.out.println("id after update : " + poFromDB.get(0).getCurrencyMaster().getId());       // Output:  5ab8830b191bb91e78f9e221
            System.out.println("code after update : " + poFromDB.get(0).getCurrencyMaster().getCode());   // Output:  null    //?????????????????????
    }
}

выход:

id before update : 5ab8830b191bb91e78f9e221
code before update: INR

id after update : 5ab8830b191bb91e78f9e221
code afterupdate: null ?????????????????????

Описание:

Валютный мастер имеет сопоставление «один ко многим» (двунаправленное) с заказом на покупку.

На сервисном уровне

  1. сначала я выполнил запрос, чтобы получить «id, code, total» из заказа на покупку и успешно получил все поля.

  2. затем я обновил заказ на покупку.

  3. затем я снова выполнил тот же запрос (чтобы получить «id, code, total» из заказа на покупку) после обновления, после чего я могу получить идентификатор мастера валюты, но не могу получить код мастера валюты.

Я прав???


person vishvas4u    schedule 26.03.2018    source источник


Ответы (1)


Я думаю, проблема в том, что вы используете проекцию в своем собственном запросе и пытаетесь создать сущность, не получая всей необходимой информации. Я бы удалил часть проекции из собственного запроса и использовал только:

db.purchase_order.find( {'_id' : ObjectId("5ab88323191bb91e78f9e33d") } );

Это происходит из-за того, что Hibernate OGM кэширует результаты, поэтому вы должны убедиться, что сущности правильно инициализированы.

Или, что еще лучше, почему бы вам не попробовать использовать JPQL-запрос?

PurchaseOrder order = em.createQuery( "FROM PurchaseOrder po WHERE po.id = :id", PurchaseOrder.class )
                    .setParameter( "id", "5ab88323191bb91e78f9e33d" )
                    .getSingleResult();

Кстати, Hibernate OGM не должен позволять вам делать такие вещи и будет генерировать исключение в последующих версиях.

Наконец, я бы порекомендовал обновиться до Hibernate OGM 5.3.0.Final или хотя бы до 5.2.0.Final (если у вас есть какие-либо причины придерживаться семейства 5.2).

person Davide    schedule 26.03.2018
comment
привет, Дэвид, запрос (db.purchase_order.find({'_id': ObjectId(5ab88323191bb91e78f9e33d)});) получить все поля из таблицы. но мне нужно только конкретное поле, тогда зачем мне этот запрос выше. если в таблице 100 полей, а мне нужны только 2 поля, то зачем мне тратить время на получение других полей. hibernate ogm даже не бросает исключение в таком сценарии. - person vishvas4u; 26.03.2018
comment
означает, что если я сначала обновлю какую-либо запись, а затем применю запрос для получения того же объекта, тогда ogm не позволит вам получить внутренние поля сопоставленного объекта. это правильно??? это баг текущей версии??? я не понимаю, что запрос отличается, и я выполнил запрос независимо от предыдущего запроса, но результат зависит от предыдущего запроса - person vishvas4u; 26.03.2018
comment
здесь, в приведенном выше примере, я не обновлял таблицу валют.. в соответствии с проблемой кеша hibernate ogm, если объект валюты сохраняется в кеше перед обновлением, то почему я не могу получить его после обновления, даже если я его не обновлял. - person vishvas4u; 26.03.2018
comment
Я хотел сказать, что вы используете частично инициализированные объекты, и это не сработает. Я не уверен в деталях. В вашем конкретном примере вы можете сохранить проекцию, но не сопоставлять результат с сущностью, используя .createNativeQuery(query.toString()). Это вернет объект, содержащий три поля, которые вам нужно сохранить в массиве. Этот вопрос больше касается дизайна, и это не подходящее место для его обсуждения. Может быть, мы можем продолжить на форуме hibernate ogm? discourse.hibernate.org/c/hibernate-ogm Если вы можете описать свой пример там мы можем проверить, что происходит. - person Davide; 26.03.2018
comment
В Hibernate ORM вы можете использовать преобразователи результатов для сопоставления результата запроса с любым bean-компонентом, но я не уверен, что это поддерживается Hibernate OGM. - person Davide; 26.03.2018
comment
Что делает эта строка? T newT = this.getById(mapUpdatedFields.get(id).toString(), t); - person Davide; 26.03.2018
comment
[this.getById(mapUpdatedFields.get(id).toString(), t)] получит запрос, используя такой запрос, как sbQuery.append({ $query : { _id : ObjectId(').append(id).append(') }}); - person vishvas4u; 27.03.2018
comment
Твой комментарий: . В вашем конкретном примере вы можете сохранить проекцию, но не сопоставлять результат с сущностью, используя .createNativeQuery(query.toString()). если я избегаю этого сопоставления, то как я могу получить выгоду от сопоставления спящего режима. - person vishvas4u; 27.03.2018
comment
Не похоже, что вы знакомы с JPA, я бы порекомендовал взглянуть на спецификацию и, возможно, документацию Hibernate ORM. Ваш пример обновления может быть написан следующим образом: gist.github.com/DavideD/e5dfa6d12525dc22527994c91f65131d - person Davide; 27.03.2018
comment
Собственные запросы — это инструмент, введенный, чтобы помочь пользователю, когда базовые параметры предложения базы данных не охватываются спецификацией. Hibernate не может отслеживать изменения, которые вы делаете, когда используете их, и поэтому вы получаете результаты, которые не являются последовательными, если вы не будете осторожны. - person Davide; 27.03.2018
comment
Возвращаясь к проблеме отображения, есть способ сделать то, что вам нужно, отображая объект во что-то отличное от сущности, но в настоящее время они не поддерживаются OGM. - person Davide; 27.03.2018
comment
я знаю, что entity.find() работает, но в настоящее время hibernate ogm не поддерживает запрос критериев jpa. поэтому я использовал собственный запрос для этого... поэтому я использовал такой запрос, как db.currency_master.find({'_id': ObjectId(5ab9ea222dba34285c5e4886)}), который такой же.. хорошо, но я получил ответ, что валюта ogm не поддерживает.. так что не беспокойтесь .. буду ждать следующую версию .. - person vishvas4u; 27.03.2018
comment
Критерии и em.find(..) - это две разные вещи. Код, который я связал, будет работать с Hibernate OGM. - person Davide; 27.03.2018
comment
Также будет работать HQL, вы можете использовать select po.purchaseOrderNo, po.id, po.total из FROM PurchaseOrder po WHERE po.id = :id - person Davide; 27.03.2018
comment
Для HQL есть некоторые ограничения, отмеченные в документации. - person Davide; 27.03.2018