0

i am using Hibernate OGM (5.2.0.Alpha1) with 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()
}

DAO Layer:

@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;
    }

Service Layer:

@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    //?????????????????????
    }
}

output:

id before update : 5ab8830b191bb91e78f9e221
code before update: INR

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

Description:

Currency Master has one to many mapping(bidirectional) with purchase order.

In Service Layer,

  1. first i executed query to get "id,code,total" from purchase order and successfully got all the fields.

  2. then i updated purchase order.

  3. then i again executed same query ( to get "id,code,total" from purchase order) after update then i can get id of currency master but can't get code of currency master.

Is Am i Right???

vishvas4u
  • 41
  • 9

1 Answers1

0

I think the problem is that you are using projection in your native query and trying to create the entity without retrieving all the information needed. I would remove the projection part from the native query and only use:

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

This happens because Hibernate OGM caches results, so you have to make sure to initialize entities correctly.

Or, even better, why don't you try using a JPQL query?

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

By the way, Hibernate OGM shouldn't let you do this kind of things and will throw an exception in follow-up versions.

Last, I would recommend to update to Hibernate OGM 5.3.0.Final or at least to 5.2.0.Final (if there is any reason for you to stick to stick to the 5.2 family).

Davide D'Alto
  • 7,421
  • 2
  • 16
  • 30
  • hi david, query (db.purchase_order.find( {'_id' : ObjectId("5ab88323191bb91e78f9e33d") } );) get all fields from table. but i want only specific field then why i should you this above query. if table has 100 fields and i just want 2 fields then why should i waste time for fetching other fields. hibernate ogm even not throw excepton in such kind of scenario.. – vishvas4u Mar 26 '18 at 11:28
  • means , if i update any record first and then apply query to fetch same object then ogm do not let you fetch inner fields of mapped object. is this correct??? is it bug in current version??? i dont understand that "query is different and i executed query independently from previous query , but result is depend on previous query" – vishvas4u Mar 26 '18 at 11:32
  • here in above example, i did not update currency table.. according to hibernate ogm cache issue, if currency object is saved in cache before update then why i can not get it after update even if i did not update it. – vishvas4u Mar 26 '18 at 12:04
  • My point was that you are using partial initialized entities and that won't work. I'm not sure about the details. In your specific example you can keep the projection but avoid mapping the result to the entity using .createNativeQuery(query.toString()). This will return an Object containing the three fields that you need stored in an array. This question is more more about design and this is not the right place to discuss ti. Maybe we can continue in the hibernate ogm forum? https://discourse.hibernate.org/c/hibernate-ogm If you can describe your example there we can check what's going on. – Davide D'Alto Mar 26 '18 at 12:50
  • In Hibernate ORM you can use result transformers to map the result of a query to any bean but I'm not sure this is supported by Hibernate OGM. – Davide D'Alto Mar 26 '18 at 12:54
  • What does this line do? T newT = this.getById(mapUpdatedFields.get("id").toString(), t); – Davide D'Alto Mar 26 '18 at 12:54
  • [this.getById(mapUpdatedFields.get("id").toString(), t)] will get query using query like "sbQuery.append("{ $query : { _id : ObjectId('").append(id).append("')}}");" – vishvas4u Mar 27 '18 at 04:04
  • Your Comment: ". In your specific example you can keep the projection but avoid mapping the result to the entity using .createNativeQuery(query.toString())." if i avoid this mapping then how can i get benefit of hibernate mapping.. – vishvas4u Mar 27 '18 at 04:05
  • It doesn't seem like you are familiar with JPA, I would recommend to have a look at the specification and maybe Hibernate ORM documentation. Your update example can be written like this: https://gist.github.com/DavideD/e5dfa6d12525dc22527994c91f65131d – Davide D'Alto Mar 27 '18 at 10:25
  • Native queries are a tool introduced to help the user when the underlying db offer options not cover by the specification. Hibernate cannot keep track of changes that you do when you use them and therefore you end up with results dthat are not consistent if you are not careful. – Davide D'Alto Mar 27 '18 at 10:28
  • Getting back to the mapping issue, there are way to do what you need, mapping an object into something different than an entity but currently they are not supported by OGM. – Davide D'Alto Mar 27 '18 at 10:30
  • i know entity.find() is working, but currently hibernate ogm does not support jpa criteria query. so i used native query for that... so i used query like "db.currency_master.find({'_id' : ObjectId("5ab9ea222dba34285c5e4886")})" which is same.. ok but i got answer that "currency ogm does not support".. so no worries.. will wait next version.. – vishvas4u Mar 27 '18 at 11:37
  • Criteria and em.find(..) are two different things. The code I linked will work with Hibernate OGM – Davide D'Alto Mar 27 '18 at 12:54
  • Also HQL will work, you could use "select po.purchaseOrderNo, po.id, po.total from FROM PurchaseOrder po WHERE po.id = :id" – Davide D'Alto Mar 27 '18 at 12:57
  • For HQL though there are some limitations highlighted in the documentation – Davide D'Alto Mar 27 '18 at 12:59