Skip to content

Added a new feature to disable wrapping of identifiers during proxy serialization #137

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jul 11, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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