Skip to content

Commit

Permalink
Added a new feature to disable wrapping of identifiers during proxy s…
Browse files Browse the repository at this point in the history
…erialization (#137)

Add a new feature to disable wrapping of identifiers during proxy serialization.

Co-authored-by: jbosca <jbosca@alfatecsistemas.es>
  • Loading branch information
jobosk and jbosca authored Jul 11, 2020
1 parent be09287 commit cacdb5b
Show file tree
Hide file tree
Showing 6 changed files with 131 additions and 63 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,21 +28,21 @@ public enum Feature {
* Default value is true.
*/
USE_TRANSIENT_ANNOTATION(true),

/**
* If FORCE_LAZY_LOADING is false, this feature serializes uninitialized lazy loading proxies as
* <code>{"identifierName":"identifierValue"}</code> rather than <code>null</code>.
* <code>{"identifierName":"identifierValue"}</code> rather than <code>null</code>.
* <p>
* Default value is false.
* <p>
* Note that the name of the identifier property can only be determined if
* Note that the name of the identifier property can only be determined if
* <ul>
* <li>the {@link Mapping} is provided to the Hibernate4Module, or </li>
* <li>the persistence context that loaded the proxy has not yet been closed, or</li>
* <li>the persistence context that loaded the proxy has not yet been closed, or</li>
* <li>the id property is mapped with property access (for instance because the {@code @Id}
* annotation is applied to a method rather than a field)</li>
* </ul>
* Otherwise, the entity name will be used instead.
* Otherwise, the entity name will be used instead.
*/
SERIALIZE_IDENTIFIER_FOR_LAZY_NOT_LOADED_OBJECTS(false),

Expand All @@ -58,7 +58,7 @@ public enum Feature {
* <p>
* Default value is false, meaning that laziness is considered to be the
* default value.
*
*
* @since 2.4
*/
REQUIRE_EXPLICIT_LAZY_LOADING_MARKER(false),
Expand All @@ -78,16 +78,24 @@ public enum Feature {
* @since 2.8.2
*/
REPLACE_PERSISTENT_COLLECTIONS(false),

/**
* Using {@link #FORCE_LAZY_LOADING} may result in
* `javax.persistence.EntityNotFoundException`. This flag configures Jackson to
* ignore the error and serialize a `null`.
*
* @since 2.10
*/
WRITE_MISSING_ENTITIES_AS_NULL(false)
;
WRITE_MISSING_ENTITIES_AS_NULL(false),

/**
* Feature that may be disables to unwrap the identifier
* of the serialized entity, returning a value instead of
* an object.
*
* @since 2.12
*/
WRAP_IDENTIFIER_IN_OBJECT(true);

final boolean _defaultState;
final int _mask;
Expand All @@ -106,7 +114,7 @@ public static int collectDefaults()
}
return flags;
}

private Feature(boolean defaultState) {
_defaultState = defaultState;
_mask = (1 << ordinal());
Expand All @@ -118,7 +126,7 @@ private Feature(boolean defaultState) {
}

protected final static int DEFAULT_FEATURES = Feature.collectDefaults();

/**
* Bit flag composed of bits that indicate which
* {@link Feature}s
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ public class HibernateProxySerializer
protected final boolean _forceLazyLoading;
protected final boolean _serializeIdentifier;
protected final boolean _nullMissingEntities;
protected final boolean _wrappedIdentifier;
protected final Mapping _mapping;

/**
Expand All @@ -62,26 +63,31 @@ public class HibernateProxySerializer

public HibernateProxySerializer(boolean forceLazyLoading)
{
this(forceLazyLoading, false, false, null, null);
this(forceLazyLoading, false, false, true, null, null);
}

public HibernateProxySerializer(boolean forceLazyLoading, boolean serializeIdentifier) {
this(forceLazyLoading, serializeIdentifier, false, null, null);
this(forceLazyLoading, serializeIdentifier, false, true, null, null);
}

public HibernateProxySerializer(boolean forceLazyLoading, boolean serializeIdentifier, Mapping mapping) {
this(forceLazyLoading, serializeIdentifier, false, mapping, null);
this(forceLazyLoading, serializeIdentifier, false, true, mapping, null);
}

public HibernateProxySerializer(boolean forceLazyLoading, boolean serializeIdentifier, boolean nullMissingEntities, Mapping mapping) {
this(forceLazyLoading, serializeIdentifier, nullMissingEntities, mapping, null);
this(forceLazyLoading, serializeIdentifier, nullMissingEntities, true, mapping, null);
}

public HibernateProxySerializer(boolean forceLazyLoading, boolean serializeIdentifier, boolean nullMissingEntities, Mapping mapping,
BeanProperty property) {
public HibernateProxySerializer(boolean forceLazyLoading, boolean serializeIdentifier, boolean nullMissingEntities, boolean wrappedIdentifier, Mapping mapping) {
this(forceLazyLoading, serializeIdentifier, nullMissingEntities, wrappedIdentifier, mapping, null);
}

public HibernateProxySerializer(boolean forceLazyLoading, boolean serializeIdentifier, boolean nullMissingEntities, boolean wrappedIdentifier, Mapping mapping,
BeanProperty property) {
_forceLazyLoading = forceLazyLoading;
_serializeIdentifier = serializeIdentifier;
_nullMissingEntities = nullMissingEntities;
_wrappedIdentifier = wrappedIdentifier;
_mapping = mapping;
_dynamicSerializers = PropertySerializerMap.emptyForProperties();
_property = property;
Expand All @@ -90,8 +96,8 @@ public HibernateProxySerializer(boolean forceLazyLoading, boolean serializeIdent
@Override
public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) {
return new HibernateProxySerializer(this._forceLazyLoading, _serializeIdentifier, _nullMissingEntities,
_mapping, property);
}
_wrappedIdentifier, _mapping, property);
}

/*
/**********************************************************************
Expand All @@ -103,7 +109,7 @@ public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty
public boolean isEmpty(SerializerProvider provider, HibernateProxy value) {
return (value == null) || (findProxied(value) == null);
}

@Override
public void serialize(HibernateProxy value, JsonGenerator jgen, SerializerProvider provider)
throws IOException
Expand Down Expand Up @@ -181,29 +187,21 @@ protected JsonSerializer<Object> findSerializer(SerializerProvider provider, Obj
* Helper method for finding value being proxied, if it is available
* or if it is to be forced to be loaded.
*/
protected Object findProxied(HibernateProxy proxy)
protected Object findProxied(final HibernateProxy proxy)
{
LazyInitializer init = proxy.getHibernateLazyInitializer();
if (!_forceLazyLoading && init.isUninitialized()) {
if (_serializeIdentifier) {
String idName;
if (_mapping != null) {
idName = _mapping.getIdentifierPropertyName(init.getEntityName());
final Object idValue = init.getIdentifier();
final Object result;
if (_wrappedIdentifier) {
final HashMap<String, Object> map = new HashMap<>();
map.put(getIdentifierPropertyName(init), idValue);
result = map;
} else {
final SessionImplementor session = init.getSession();
if (session != null) {
idName = session.getFactory().getIdentifierPropertyName(init.getEntityName());
} else {
idName = ProxyReader.getIdentifierPropertyName(init);
if (idName == null) {
idName = init.getEntityName();
}
}
result = idValue;
}
final Object idValue = init.getIdentifier();
HashMap<String, Object> map = new HashMap<String, Object>();
map.put(idName, idValue);
return map;
return result;
}
return null;
}
Expand All @@ -217,12 +215,36 @@ protected Object findProxied(HibernateProxy proxy)
}
}
}


/**
* Helper method to retrieve the name of the identifier property of the
* specified lazy initializer.
* @param init Lazy initializer to obtain identifier property name from.
* @return Name of the identity property of the specified lazy initializer.
*/
private String getIdentifierPropertyName(final LazyInitializer init) {
String idName;
if (_mapping != null) {
idName = _mapping.getIdentifierPropertyName(init.getEntityName());
} else {
final SessionImplementor session = init.getSession();
if (session != null) {
idName = session.getFactory().getIdentifierPropertyName(init.getEntityName());
} else {
idName = ProxyReader.getIdentifierPropertyName(init);
if (idName == null) {
idName = init.getEntityName();
}
}
}
return idName;
}

/**
* Inspects a Hibernate proxy to try and determine the name of the identifier property
* (Hibernate proxies know the getter of the identifier property because it receives special
* treatment in the invocation handler). Alas, the field storing the method reference is
* private and has no getter, so we must resort to ugly reflection hacks to read its value ...
* (Hibernate proxies know the getter of the identifier property because it receives special
* treatment in the invocation handler). Alas, the field storing the method reference is
* private and has no getter, so we must resort to ugly reflection hacks to read its value ...
*/
protected static class ProxyReader {

Expand All @@ -235,7 +257,7 @@ protected static class ProxyReader {
getIdentifierMethodField.setAccessible(true);
} catch (Exception e) {
// should never happen: the field exists in all versions of hibernate 4 and 5
throw new RuntimeException(e);
throw new RuntimeException(e);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public class HibernateSerializers extends Serializers.Base
protected final boolean _forceLoading;
protected final boolean _serializeIdentifiers;
protected final boolean _nullMissingEntities;
protected final boolean _wrappedIdentifier;
protected final Mapping _mapping;

public HibernateSerializers(int features) {
Expand All @@ -25,6 +26,7 @@ public HibernateSerializers(Mapping mapping, int features)
_forceLoading = Feature.FORCE_LAZY_LOADING.enabledIn(features);
_serializeIdentifiers = Feature.SERIALIZE_IDENTIFIER_FOR_LAZY_NOT_LOADED_OBJECTS.enabledIn(features);
_nullMissingEntities = Feature.WRITE_MISSING_ENTITIES_AS_NULL.enabledIn(features);
_wrappedIdentifier = Feature.WRAP_IDENTIFIER_IN_OBJECT.enabledIn(features);
_mapping = mapping;
}

Expand All @@ -34,7 +36,7 @@ public JsonSerializer<?> findSerializer(SerializationConfig config,
{
Class<?> raw = type.getRawClass();
if (HibernateProxy.class.isAssignableFrom(raw)) {
return new HibernateProxySerializer(_forceLoading, _serializeIdentifiers, _nullMissingEntities, _mapping);
return new HibernateProxySerializer(_forceLoading, _serializeIdentifiers, _nullMissingEntities, _wrappedIdentifier, _mapping);
}
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,16 @@ public enum Feature {
*
* @since 2.10
*/
WRITE_MISSING_ENTITIES_AS_NULL(false)
WRITE_MISSING_ENTITIES_AS_NULL(false),

/**
* Feature that may be disables to unwrap the identifier
* of the serialized entity, returning a value instead of
* an object.
*
* @since 2.12
*/
WRAP_IDENTIFIER_IN_OBJECT(true)
;

final boolean _defaultState;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ public class HibernateProxySerializer
protected final boolean _forceLazyLoading;
protected final boolean _serializeIdentifier;
protected final boolean _nullMissingEntities;
protected final boolean _wrappedIdentifier;
protected final Mapping _mapping;

/**
Expand All @@ -63,26 +64,36 @@ public class HibernateProxySerializer

public HibernateProxySerializer(boolean forceLazyLoading)
{
this(forceLazyLoading, false, false, null, null);
this(forceLazyLoading, false, false, true, null, null);
}

public HibernateProxySerializer(boolean forceLazyLoading, boolean serializeIdentifier) {
this(forceLazyLoading, serializeIdentifier, false, null, null);
this(forceLazyLoading, serializeIdentifier, false, true, null, null);
}

public HibernateProxySerializer(boolean forceLazyLoading, boolean serializeIdentifier, Mapping mapping) {
this(forceLazyLoading, serializeIdentifier, false, mapping, null);
this(forceLazyLoading, serializeIdentifier, false, true, mapping, null);
}

public HibernateProxySerializer(boolean forceLazyLoading, boolean serializeIdentifier, boolean nullMissingEntities, Mapping mapping) {
this(forceLazyLoading, serializeIdentifier, nullMissingEntities, mapping, null);
this(forceLazyLoading, serializeIdentifier, nullMissingEntities, true, mapping, null);
}

public HibernateProxySerializer(boolean forceLazyLoading, boolean serializeIdentifier, boolean nullMissingEntities, boolean wrappedIdentifier, Mapping mapping) {
this(forceLazyLoading, serializeIdentifier, nullMissingEntities, wrappedIdentifier, mapping, null);
}

public HibernateProxySerializer(boolean forceLazyLoading, boolean serializeIdentifier, boolean nullMissingEntities, Mapping mapping,
BeanProperty property) {
this(forceLazyLoading, serializeIdentifier, nullMissingEntities, true, mapping, property);
}

public HibernateProxySerializer(boolean forceLazyLoading, boolean serializeIdentifier, boolean nullMissingEntities, boolean wrappedIdentifier, Mapping mapping,
BeanProperty property) {
_forceLazyLoading = forceLazyLoading;
_serializeIdentifier = serializeIdentifier;
_nullMissingEntities = nullMissingEntities;
_wrappedIdentifier = wrappedIdentifier;
_mapping = mapping;
_dynamicSerializers = PropertySerializerMap.emptyForProperties();
_property = property;
Expand Down Expand Up @@ -187,22 +198,14 @@ protected Object findProxied(HibernateProxy proxy)
LazyInitializer init = proxy.getHibernateLazyInitializer();
if (!_forceLazyLoading && init.isUninitialized()) {
if (_serializeIdentifier) {
String idName;
if (_mapping != null) {
idName = _mapping.getIdentifierPropertyName(init.getEntityName());
final Object idValue = init.getIdentifier();
if (_wrappedIdentifier) {
HashMap<String, Object> map = new HashMap<>();
map.put(getIdentifierPropertyName(init), idValue);
return map;
} else {
idName = ProxySessionReader.getIdentifierPropertyName(init);
if (idName == null) {
idName = ProxyReader.getIdentifierPropertyName(init);
if (idName == null) {
idName = init.getEntityName();
}
}
return idValue;
}
final Object idValue = init.getIdentifier();
HashMap<String, Object> map = new HashMap<String, Object>();
map.put(idName, idValue);
return map;
}
return null;
}
Expand All @@ -216,6 +219,28 @@ protected Object findProxied(HibernateProxy proxy)
}
}
}

/**
* Helper method to retrieve the name of the identifier property of the
* specified lazy initializer.
* @param init Lazy initializer to obtain identifier property name from.
* @return Name of the identity property of the specified lazy initializer.
*/
private String getIdentifierPropertyName(final LazyInitializer init) {
String idName;
if (_mapping != null) {
idName = _mapping.getIdentifierPropertyName(init.getEntityName());
} else {
idName = ProxySessionReader.getIdentifierPropertyName(init);
if (idName == null) {
idName = ProxyReader.getIdentifierPropertyName(init);
if (idName == null) {
idName = init.getEntityName();
}
}
}
return idName;
}

/**
* Inspects a Hibernate proxy to try and determine the name of the identifier property
Expand Down
Loading

0 comments on commit cacdb5b

Please sign in to comment.