Skip to content

Commit

Permalink
Avoid the unnecessary UPDATE for JsonNode entity mappings #348
Browse files Browse the repository at this point in the history
  • Loading branch information
Victor Noel authored and vladmihalcea committed Oct 9, 2021
1 parent 89cc477 commit bfab823
Show file tree
Hide file tree
Showing 19 changed files with 615 additions and 80 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.vladmihalcea.hibernate.type.util;

import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.type.TypeFactory;
import org.hibernate.internal.util.SerializationHelper;
import org.hibernate.type.SerializationException;
Expand All @@ -22,6 +23,10 @@ public ObjectMapperJsonSerializer(ObjectMapperWrapper objectMapperWrapper) {

@Override
public <T> T clone(T object) {
if (object instanceof JsonNode) {
return (T) ((JsonNode) object).deepCopy();
}

if (object instanceof Collection) {
Object firstElement = findFirstNonNullElement((Collection) object);
if (firstElement != null && !(firstElement instanceof Serializable)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,15 @@ public class ObjectMapperWrapper {

private final ObjectMapper objectMapper;

private JsonSerializer jsonSerializer = new ObjectMapperJsonSerializer(this);
private JsonSerializer jsonSerializer;

public ObjectMapperWrapper() {
this.objectMapper = new ObjectMapper().findAndRegisterModules();
this(new ObjectMapper().findAndRegisterModules());
}

public ObjectMapperWrapper(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
this.jsonSerializer = new ObjectMapperJsonSerializer(this);
}

public void setJsonSerializer(JsonSerializer jsonSerializer) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,23 @@
package com.vladmihalcea.hibernate.type.util;

import com.fasterxml.jackson.annotation.JsonProperty;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotSame;

import com.google.common.base.Objects;
import com.google.common.collect.Lists;
import org.hibernate.annotations.Type;
import org.junit.Test;

import javax.persistence.Column;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.math.BigDecimal;
import java.util.*;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotSame;

public class ObjectMapperJsonSerializerTest {

private ObjectMapperJsonSerializer serializer = new ObjectMapperJsonSerializer(new ObjectMapperWrapper());
private ObjectMapperWrapper mapper = new ObjectMapperWrapper();

private ObjectMapperJsonSerializer serializer = new ObjectMapperJsonSerializer(mapper);

@Test
public void should_clone_serializable_object() {
Expand Down Expand Up @@ -125,8 +122,8 @@ public void should_clone_serializable_complex_object_with_serializable_nested_ob
Map<String, List<SerializableObject>> map = new LinkedHashMap<String, List<SerializableObject>>();
map.put("key1", Lists.newArrayList(new SerializableObject("name1")));
map.put("key2", Lists.newArrayList(
new SerializableObject("name2"),
new SerializableObject("name3")
new SerializableObject("name2"),
new SerializableObject("name3")
));
Object original = new SerializableComplexObject(map);
Object cloned = serializer.clone(original);
Expand All @@ -139,15 +136,25 @@ public void should_clone_serializable_complex_object_with_non_serializable_neste
Map<String, List<NonSerializableObject>> map = new LinkedHashMap<String, List<NonSerializableObject>>();
map.put("key1", Lists.newArrayList(new NonSerializableObject("name1")));
map.put("key2", Lists.newArrayList(
new NonSerializableObject("name2"),
new NonSerializableObject("name3")
new NonSerializableObject("name2"),
new NonSerializableObject("name3")
));
Object original = new SerializableComplexObjectWithNonSerializableNestedObject(map);
Object cloned = serializer.clone(original);
assertEquals(original, cloned);
assertNotSame(original, cloned);
}

@Test
public void should_clone_jsonnode() {
Object original = mapper.getObjectMapper().createArrayNode()
.add(BigDecimal.ONE)
.add(1.0)
.add("string");
Object cloned = serializer.clone(original);
assertEquals(original, cloned);
assertNotSame(original, cloned);
}

private static class SerializableObject implements Serializable {
private final String value;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.vladmihalcea.hibernate.type.util;

import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.type.TypeFactory;
import org.hibernate.internal.util.SerializationHelper;
import org.hibernate.type.SerializationException;
Expand All @@ -22,6 +23,10 @@ public ObjectMapperJsonSerializer(ObjectMapperWrapper objectMapperWrapper) {

@Override
public <T> T clone(T object) {
if (object instanceof JsonNode) {
return (T) ((JsonNode) object).deepCopy();
}

if (object instanceof Collection) {
Object firstElement = findFirstNonNullElement((Collection) object);
if (firstElement != null && !(firstElement instanceof Serializable)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,15 @@ public class ObjectMapperWrapper {

private final ObjectMapper objectMapper;

private JsonSerializer jsonSerializer = new ObjectMapperJsonSerializer(this);
private JsonSerializer jsonSerializer;

public ObjectMapperWrapper() {
this.objectMapper = new ObjectMapper().findAndRegisterModules();
this(new ObjectMapper().findAndRegisterModules());
}

public ObjectMapperWrapper(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
this.jsonSerializer = new ObjectMapperJsonSerializer(this);
}

public void setJsonSerializer(JsonSerializer jsonSerializer) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
package com.vladmihalcea.hibernate.type.json;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.vladmihalcea.hibernate.type.util.AbstractMySQLIntegrationTest;
import com.vladmihalcea.hibernate.type.util.transaction.JPATransactionFunction;
import net.ttddyy.dsproxy.QueryCount;
import net.ttddyy.dsproxy.QueryCountHolder;
import org.hibernate.Session;
import org.hibernate.annotations.NaturalId;
import org.hibernate.annotations.Type;
import org.hibernate.annotations.TypeDef;
import org.junit.Test;

import javax.persistence.*;

import static org.junit.Assert.assertEquals;

/**
* @author Victor Noël
*/
public class MySQLJsonNodePropertyTest extends AbstractMySQLIntegrationTest {

private final ObjectMapper mapper = newMapper();

@Override
protected Class<?>[] entities() {
return new Class<?>[]{
Book.class
};
}


@Override
protected void afterInit() {
doInJPA(new JPATransactionFunction<Void>() {

@Override
public Void apply(EntityManager entityManager) {
try {
entityManager.persist(
new Book()
.setIsbn("978-9730228236")
.setProperties(mapper.readTree("{\"field\": 0.05}"))
);
} catch (Exception e) {
throw new IllegalStateException(e);
}

return null;
}
});
}

@Test
public void test() {
QueryCountHolder.clear();

doInJPA(new JPATransactionFunction<Void>() {

@Override
public Void apply(EntityManager entityManager) {
Book book = (Book) entityManager.unwrap(Session.class)
.bySimpleNaturalId(Book.class)
.load("978-9730228236");
assertEquals(0.05, book.getProperties().get("field").asDouble(), 0.0);

return null;
}
});

QueryCount queryCount = QueryCountHolder.getGrandTotal();
assertEquals(0, queryCount.getUpdate());
}

public static class MyJsonType extends JsonType {
public MyJsonType() {
super(newMapper());
}
}

@Entity(name = "Book")
@Table(name = "book")
@TypeDef(name = "json", typeClass = MyJsonType.class)
public static class Book {

@Id
@GeneratedValue
private Long id;

@NaturalId
private String isbn;

@Type(type = "json")
@Column(columnDefinition = "json")
private JsonNode properties;

public String getIsbn() {
return isbn;
}

public Book setIsbn(String isbn) {
this.isbn = isbn;
return this;
}

public JsonNode getProperties() {
return properties;
}

public Book setProperties(JsonNode properties) {
this.properties = properties;
return this;
}
}

private static ObjectMapper newMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS, true);
return mapper;
}
}
Original file line number Diff line number Diff line change
@@ -1,26 +1,23 @@
package com.vladmihalcea.hibernate.type.util;

import com.fasterxml.jackson.annotation.JsonProperty;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotSame;

import com.google.common.base.Objects;
import com.google.common.collect.Lists;
import org.hibernate.annotations.Type;
import org.junit.Test;

import javax.persistence.Column;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.math.BigDecimal;
import java.util.*;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotSame;

public class ObjectMapperJsonSerializerTest {

private ObjectMapperJsonSerializer serializer = new ObjectMapperJsonSerializer(new ObjectMapperWrapper());
private ObjectMapperWrapper mapper = new ObjectMapperWrapper();

private ObjectMapperJsonSerializer serializer = new ObjectMapperJsonSerializer(mapper);

@Test
public void should_clone_serializable_object() {
Expand Down Expand Up @@ -125,8 +122,8 @@ public void should_clone_serializable_complex_object_with_serializable_nested_ob
Map<String, List<SerializableObject>> map = new LinkedHashMap<String, List<SerializableObject>>();
map.put("key1", Lists.newArrayList(new SerializableObject("name1")));
map.put("key2", Lists.newArrayList(
new SerializableObject("name2"),
new SerializableObject("name3")
new SerializableObject("name2"),
new SerializableObject("name3")
));
Object original = new SerializableComplexObject(map);
Object cloned = serializer.clone(original);
Expand All @@ -139,15 +136,25 @@ public void should_clone_serializable_complex_object_with_non_serializable_neste
Map<String, List<NonSerializableObject>> map = new LinkedHashMap<String, List<NonSerializableObject>>();
map.put("key1", Lists.newArrayList(new NonSerializableObject("name1")));
map.put("key2", Lists.newArrayList(
new NonSerializableObject("name2"),
new NonSerializableObject("name3")
new NonSerializableObject("name2"),
new NonSerializableObject("name3")
));
Object original = new SerializableComplexObjectWithNonSerializableNestedObject(map);
Object cloned = serializer.clone(original);
assertEquals(original, cloned);
assertNotSame(original, cloned);
}

@Test
public void should_clone_jsonnode() {
Object original = mapper.getObjectMapper().createArrayNode()
.add(BigDecimal.ONE)
.add(1.0)
.add("string");
Object cloned = serializer.clone(original);
assertEquals(original, cloned);
assertNotSame(original, cloned);
}

private static class SerializableObject implements Serializable {
private final String value;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.vladmihalcea.hibernate.type.util;

import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.type.TypeFactory;
import org.hibernate.internal.util.SerializationHelper;
import org.hibernate.type.SerializationException;
Expand All @@ -22,6 +23,10 @@ public ObjectMapperJsonSerializer(ObjectMapperWrapper objectMapperWrapper) {

@Override
public <T> T clone(T object) {
if (object instanceof JsonNode) {
return (T) ((JsonNode) object).deepCopy();
}

if (object instanceof Collection) {
Object firstElement = findFirstNonNullElement((Collection) object);
if (firstElement != null && !(firstElement instanceof Serializable)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,15 @@ public class ObjectMapperWrapper {

private final ObjectMapper objectMapper;

private JsonSerializer jsonSerializer = new ObjectMapperJsonSerializer(this);
private JsonSerializer jsonSerializer;

public ObjectMapperWrapper() {
this.objectMapper = new ObjectMapper().findAndRegisterModules();
this(new ObjectMapper().findAndRegisterModules());
}

public ObjectMapperWrapper(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
this.jsonSerializer = new ObjectMapperJsonSerializer(this);
}

public void setJsonSerializer(JsonSerializer jsonSerializer) {
Expand Down
Loading

0 comments on commit bfab823

Please sign in to comment.