How can I prevent Hibernate from updating NULL values
Asked Answered
R

4

21

Is there a setting in hibernate to ignore null values of properties when saving a hibernate object?

NOTE
In my case I am de-serializing JSON to a Hibernate Pojo via Jackson.

The JSON only contains some of the fields of the Pojo. If I save the Pojo the fields that were not in the JSON are null in the Pojo and hibernate UPDATES them.

I came accross the setting updateable=false, but this isn't a 100% solution. http://docs.jboss.org/hibernate/stable/annotations/reference/en/html_single/#entity-mapping-property

Maybe somebody has another idea...

NOTE 2:

According to the Hibernate Docs the dynamicUpdate annotation does exactly that

dynamicInsert / dynamicUpdate (defaults to false):
specifies that INSERT / UPDATE SQL should be generated at runtime and contain only the columns whose values are not null.

http://docs.jboss.org/hibernate/core/3.6/reference/en-US/html_single/#mapping-declaration-class

Funny enough if you define it in XML via dynamic-update the docu do not mention the hanlding of NULL values.

dynamic-update (optional - defaults to false):
specifies that UPDATE SQL should be > generated at runtime and can contain only those columns whose values have changed.

Due to the fact that I'm using both annotations AND xml configuration, hibernate seems to ignores my dynamicUpdate=true annotation.

Rhumb answered 22/9, 2011 at 15:41 Comment(1)
you can use dynamic-update to avoid properties coming in SQL which haven't been updated but it is generally advised to use this setting only when the entity has too properties (columns > 50) [reference : Java Persistence with Hibernate 2006 edition]. @hvgotcodes is right in pointing out that you dont have to map fields if they are never going to be persisted.Thereinto
D
15

You should first load the object using the primary key from DB and then copy or deserialize the JSON on top of it.

There is no way for hibernate to figure out whether a property with value null has been explicitly set to that value or it was excluded.

If it is an insert then dynamic-insert=true should work.

Dominquedominquez answered 22/9, 2011 at 17:49 Comment(3)
lead me to the right direction. Best is to load the existing object and update this object accordingly. Jackson even has a default behavior for that wiki.fasterxml.com/JacksonFeatureUpdateValueRhumb
I don't have any null value in my db. So nobody will never set to null anything explicitly. Is there any other solution for me? @DominquedominquezOveruse
Issuing SELECT before UPDATE is an additional load on DB and a decline in response time SLA. It is ridiculous that the framework has such limited ways to address that.Limnology
I
0

I have googled a lot about this,but there is no the very solution for me.So,I used a not graceful solution to cover it.

  public void setAccount(Account a) throws HibernateException {
    try {
        Account tmp = (Account) session.
                get(Account.class, a.getAccountId());
        tmp.setEmail(getNotNull(a.getEmail(), tmp.getEmail()));            
        ...
        tmp.setVersion(getNotNull(a.getVersion(), tmp.getVersion()));
        session.beginTransaction();
        session.update(tmp);
        session.getTransaction().commit();
    } catch (HibernateException e) {
        logger.error(e.toString());
        throw e;
    }
}

public static <T> T getNotNull(T a, T b) {
    return b != null && a != null && !a.equals(b) ? a : b;
}

I receive an Object a which contains a lot of fields.Those field maybe null,but I don't want to update them into mysql. I get an tmp Obejct from db, and change the field by method getNotNull,then update the Object.

a Chinese description edition

Isotope answered 8/1, 2016 at 3:22 Comment(0)
H
0

I bumped into this problem and I did a work-around to help myself through this. May be a little ugly but may work just fine for you too. Kindly if someone feels there's an adjustment that's good to have feel free to add. Note that the work-around is meant for valid entity classes and whose some fields include nullable attributes. Advantage with this one is it reduces the number of queries.

public String getUpdateJPQL(Object obj, String column, Object value) throws JsonProcessingException, IOException {

//obj -> your entity class object
//column -> field name in the query clause
//value -> value of the field in the query clause

    ObjectMapper mapper = new ObjectMapper();
    String json = mapper.writeValueAsString(obj);
    Map<String, Object> map = mapper.readValue(json, Map.class);
    Map format = new HashMap();
    if (value instanceof String) {
        value = "'" + value + "'";
    }

    map.keySet()
            .forEach((str) -> {
                Object val = map.get(str);
                if (val != null) {
                    format.put("t.".concat(str), "'" + val + "'");
                }
            });
    String formatStr = format.toString();
    formatStr = formatStr.substring(1, formatStr.length() - 1);

    return "update " + obj.getClass()
            .getSimpleName() + " t set " + formatStr + " where " + column + " = " + value + "";

}

Example: For entity type User with fields: userId, userName & userAge; the result query of getUpdateJPQL(user, userId, 2) should be update User t set t.userName = 'value1', t.userAge = 'value2' where t.userId = 2 Note that you can use json annotations like @JsonIgnore on userId field to exclude it in deserialization i.e. in the generated query. You can then run your query using entityManager or hibernate sessions.

Heptagonal answered 20/8, 2019 at 10:24 Comment(0)
F
0

Try this

DTO

@Data
public class ProductRequest {

    private Optional<String> name;

    private Optional<String> unitSale;

}

Endpoint

 @PUT
    @Path("/{id}")
    public Response update(@PathParam("id") Long productId, @NotNull ProductRequest productModel) {
        productService.update(productId, productModel);
        return Response.status(Status.ACCEPTED).build();
    }

Service

@Transactional
    public void update(Long productId, @Valid ProductRequest productRequest) {

        ProductEntity productEntity = productRepository.findByIdOptional(productId)
                .orElseThrow(() -> new EntityNotFoundException("Product ID not found"));

        if (productRequest.getName() != null) {
            productEntity.name = unwrapOptional(productRequest.getName());
        }

        if (productRequest.getUnitSale() != null) {
            productEntity.unitSale = unwrapOptional(productRequest.getUnitSale());
        }

    }

    public <T> T unwrapOptional(final Optional<T> o) {
        return o.isPresent() ? o.get() : null;
    }

The secret was to use Optional<> in the DTO class, since it can distinguish between an explicit null in the request body vs a null due to omission of parameters in the request.

Fallacious answered 31/5 at 22:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.