Skip to content

Commit cacdb5b

Browse files
joboskjbosca
andauthored
Added a new feature to disable wrapping of identifiers during proxy serialization (#137)
Add a new feature to disable wrapping of identifiers during proxy serialization. Co-authored-by: jbosca <[email protected]>
1 parent be09287 commit cacdb5b

File tree

6 files changed

+131
-63
lines changed

6 files changed

+131
-63
lines changed

hibernate4/src/main/java/com/fasterxml/jackson/datatype/hibernate4/Hibernate4Module.java

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -28,21 +28,21 @@ public enum Feature {
2828
* Default value is true.
2929
*/
3030
USE_TRANSIENT_ANNOTATION(true),
31-
31+
3232
/**
3333
* If FORCE_LAZY_LOADING is false, this feature serializes uninitialized lazy loading proxies as
34-
* <code>{"identifierName":"identifierValue"}</code> rather than <code>null</code>.
34+
* <code>{"identifierName":"identifierValue"}</code> rather than <code>null</code>.
3535
* <p>
3636
* Default value is false.
3737
* <p>
38-
* Note that the name of the identifier property can only be determined if
38+
* Note that the name of the identifier property can only be determined if
3939
* <ul>
4040
* <li>the {@link Mapping} is provided to the Hibernate4Module, or </li>
41-
* <li>the persistence context that loaded the proxy has not yet been closed, or</li>
41+
* <li>the persistence context that loaded the proxy has not yet been closed, or</li>
4242
* <li>the id property is mapped with property access (for instance because the {@code @Id}
4343
* annotation is applied to a method rather than a field)</li>
4444
* </ul>
45-
* Otherwise, the entity name will be used instead.
45+
* Otherwise, the entity name will be used instead.
4646
*/
4747
SERIALIZE_IDENTIFIER_FOR_LAZY_NOT_LOADED_OBJECTS(false),
4848

@@ -58,7 +58,7 @@ public enum Feature {
5858
* <p>
5959
* Default value is false, meaning that laziness is considered to be the
6060
* default value.
61-
*
61+
*
6262
* @since 2.4
6363
*/
6464
REQUIRE_EXPLICIT_LAZY_LOADING_MARKER(false),
@@ -78,16 +78,24 @@ public enum Feature {
7878
* @since 2.8.2
7979
*/
8080
REPLACE_PERSISTENT_COLLECTIONS(false),
81-
81+
8282
/**
8383
* Using {@link #FORCE_LAZY_LOADING} may result in
8484
* `javax.persistence.EntityNotFoundException`. This flag configures Jackson to
8585
* ignore the error and serialize a `null`.
8686
*
8787
* @since 2.10
8888
*/
89-
WRITE_MISSING_ENTITIES_AS_NULL(false)
90-
;
89+
WRITE_MISSING_ENTITIES_AS_NULL(false),
90+
91+
/**
92+
* Feature that may be disables to unwrap the identifier
93+
* of the serialized entity, returning a value instead of
94+
* an object.
95+
*
96+
* @since 2.12
97+
*/
98+
WRAP_IDENTIFIER_IN_OBJECT(true);
9199

92100
final boolean _defaultState;
93101
final int _mask;
@@ -106,7 +114,7 @@ public static int collectDefaults()
106114
}
107115
return flags;
108116
}
109-
117+
110118
private Feature(boolean defaultState) {
111119
_defaultState = defaultState;
112120
_mask = (1 << ordinal());
@@ -118,7 +126,7 @@ private Feature(boolean defaultState) {
118126
}
119127

120128
protected final static int DEFAULT_FEATURES = Feature.collectDefaults();
121-
129+
122130
/**
123131
* Bit flag composed of bits that indicate which
124132
* {@link Feature}s

hibernate4/src/main/java/com/fasterxml/jackson/datatype/hibernate4/HibernateProxySerializer.java

Lines changed: 53 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ public class HibernateProxySerializer
4646
protected final boolean _forceLazyLoading;
4747
protected final boolean _serializeIdentifier;
4848
protected final boolean _nullMissingEntities;
49+
protected final boolean _wrappedIdentifier;
4950
protected final Mapping _mapping;
5051

5152
/**
@@ -62,26 +63,31 @@ public class HibernateProxySerializer
6263

6364
public HibernateProxySerializer(boolean forceLazyLoading)
6465
{
65-
this(forceLazyLoading, false, false, null, null);
66+
this(forceLazyLoading, false, false, true, null, null);
6667
}
6768

6869
public HibernateProxySerializer(boolean forceLazyLoading, boolean serializeIdentifier) {
69-
this(forceLazyLoading, serializeIdentifier, false, null, null);
70+
this(forceLazyLoading, serializeIdentifier, false, true, null, null);
7071
}
7172

7273
public HibernateProxySerializer(boolean forceLazyLoading, boolean serializeIdentifier, Mapping mapping) {
73-
this(forceLazyLoading, serializeIdentifier, false, mapping, null);
74+
this(forceLazyLoading, serializeIdentifier, false, true, mapping, null);
7475
}
7576

7677
public HibernateProxySerializer(boolean forceLazyLoading, boolean serializeIdentifier, boolean nullMissingEntities, Mapping mapping) {
77-
this(forceLazyLoading, serializeIdentifier, nullMissingEntities, mapping, null);
78+
this(forceLazyLoading, serializeIdentifier, nullMissingEntities, true, mapping, null);
7879
}
7980

80-
public HibernateProxySerializer(boolean forceLazyLoading, boolean serializeIdentifier, boolean nullMissingEntities, Mapping mapping,
81-
BeanProperty property) {
81+
public HibernateProxySerializer(boolean forceLazyLoading, boolean serializeIdentifier, boolean nullMissingEntities, boolean wrappedIdentifier, Mapping mapping) {
82+
this(forceLazyLoading, serializeIdentifier, nullMissingEntities, wrappedIdentifier, mapping, null);
83+
}
84+
85+
public HibernateProxySerializer(boolean forceLazyLoading, boolean serializeIdentifier, boolean nullMissingEntities, boolean wrappedIdentifier, Mapping mapping,
86+
BeanProperty property) {
8287
_forceLazyLoading = forceLazyLoading;
8388
_serializeIdentifier = serializeIdentifier;
8489
_nullMissingEntities = nullMissingEntities;
90+
_wrappedIdentifier = wrappedIdentifier;
8591
_mapping = mapping;
8692
_dynamicSerializers = PropertySerializerMap.emptyForProperties();
8793
_property = property;
@@ -90,8 +96,8 @@ public HibernateProxySerializer(boolean forceLazyLoading, boolean serializeIdent
9096
@Override
9197
public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) {
9298
return new HibernateProxySerializer(this._forceLazyLoading, _serializeIdentifier, _nullMissingEntities,
93-
_mapping, property);
94-
}
99+
_wrappedIdentifier, _mapping, property);
100+
}
95101

96102
/*
97103
/**********************************************************************
@@ -103,7 +109,7 @@ public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty
103109
public boolean isEmpty(SerializerProvider provider, HibernateProxy value) {
104110
return (value == null) || (findProxied(value) == null);
105111
}
106-
112+
107113
@Override
108114
public void serialize(HibernateProxy value, JsonGenerator jgen, SerializerProvider provider)
109115
throws IOException
@@ -181,29 +187,21 @@ protected JsonSerializer<Object> findSerializer(SerializerProvider provider, Obj
181187
* Helper method for finding value being proxied, if it is available
182188
* or if it is to be forced to be loaded.
183189
*/
184-
protected Object findProxied(HibernateProxy proxy)
190+
protected Object findProxied(final HibernateProxy proxy)
185191
{
186192
LazyInitializer init = proxy.getHibernateLazyInitializer();
187193
if (!_forceLazyLoading && init.isUninitialized()) {
188194
if (_serializeIdentifier) {
189-
String idName;
190-
if (_mapping != null) {
191-
idName = _mapping.getIdentifierPropertyName(init.getEntityName());
195+
final Object idValue = init.getIdentifier();
196+
final Object result;
197+
if (_wrappedIdentifier) {
198+
final HashMap<String, Object> map = new HashMap<>();
199+
map.put(getIdentifierPropertyName(init), idValue);
200+
result = map;
192201
} else {
193-
final SessionImplementor session = init.getSession();
194-
if (session != null) {
195-
idName = session.getFactory().getIdentifierPropertyName(init.getEntityName());
196-
} else {
197-
idName = ProxyReader.getIdentifierPropertyName(init);
198-
if (idName == null) {
199-
idName = init.getEntityName();
200-
}
201-
}
202+
result = idValue;
202203
}
203-
final Object idValue = init.getIdentifier();
204-
HashMap<String, Object> map = new HashMap<String, Object>();
205-
map.put(idName, idValue);
206-
return map;
204+
return result;
207205
}
208206
return null;
209207
}
@@ -217,12 +215,36 @@ protected Object findProxied(HibernateProxy proxy)
217215
}
218216
}
219217
}
220-
218+
219+
/**
220+
* Helper method to retrieve the name of the identifier property of the
221+
* specified lazy initializer.
222+
* @param init Lazy initializer to obtain identifier property name from.
223+
* @return Name of the identity property of the specified lazy initializer.
224+
*/
225+
private String getIdentifierPropertyName(final LazyInitializer init) {
226+
String idName;
227+
if (_mapping != null) {
228+
idName = _mapping.getIdentifierPropertyName(init.getEntityName());
229+
} else {
230+
final SessionImplementor session = init.getSession();
231+
if (session != null) {
232+
idName = session.getFactory().getIdentifierPropertyName(init.getEntityName());
233+
} else {
234+
idName = ProxyReader.getIdentifierPropertyName(init);
235+
if (idName == null) {
236+
idName = init.getEntityName();
237+
}
238+
}
239+
}
240+
return idName;
241+
}
242+
221243
/**
222244
* Inspects a Hibernate proxy to try and determine the name of the identifier property
223-
* (Hibernate proxies know the getter of the identifier property because it receives special
224-
* treatment in the invocation handler). Alas, the field storing the method reference is
225-
* private and has no getter, so we must resort to ugly reflection hacks to read its value ...
245+
* (Hibernate proxies know the getter of the identifier property because it receives special
246+
* treatment in the invocation handler). Alas, the field storing the method reference is
247+
* private and has no getter, so we must resort to ugly reflection hacks to read its value ...
226248
*/
227249
protected static class ProxyReader {
228250

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

hibernate4/src/main/java/com/fasterxml/jackson/datatype/hibernate4/HibernateSerializers.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ public class HibernateSerializers extends Serializers.Base
1414
protected final boolean _forceLoading;
1515
protected final boolean _serializeIdentifiers;
1616
protected final boolean _nullMissingEntities;
17+
protected final boolean _wrappedIdentifier;
1718
protected final Mapping _mapping;
1819

1920
public HibernateSerializers(int features) {
@@ -25,6 +26,7 @@ public HibernateSerializers(Mapping mapping, int features)
2526
_forceLoading = Feature.FORCE_LAZY_LOADING.enabledIn(features);
2627
_serializeIdentifiers = Feature.SERIALIZE_IDENTIFIER_FOR_LAZY_NOT_LOADED_OBJECTS.enabledIn(features);
2728
_nullMissingEntities = Feature.WRITE_MISSING_ENTITIES_AS_NULL.enabledIn(features);
29+
_wrappedIdentifier = Feature.WRAP_IDENTIFIER_IN_OBJECT.enabledIn(features);
2830
_mapping = mapping;
2931
}
3032

@@ -34,7 +36,7 @@ public JsonSerializer<?> findSerializer(SerializationConfig config,
3436
{
3537
Class<?> raw = type.getRawClass();
3638
if (HibernateProxy.class.isAssignableFrom(raw)) {
37-
return new HibernateProxySerializer(_forceLoading, _serializeIdentifiers, _nullMissingEntities, _mapping);
39+
return new HibernateProxySerializer(_forceLoading, _serializeIdentifiers, _nullMissingEntities, _wrappedIdentifier, _mapping);
3840
}
3941
return null;
4042
}

hibernate5/src/main/java/com/fasterxml/jackson/datatype/hibernate5/Hibernate5Module.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,16 @@ public enum Feature {
8686
*
8787
* @since 2.10
8888
*/
89-
WRITE_MISSING_ENTITIES_AS_NULL(false)
89+
WRITE_MISSING_ENTITIES_AS_NULL(false),
90+
91+
/**
92+
* Feature that may be disables to unwrap the identifier
93+
* of the serialized entity, returning a value instead of
94+
* an object.
95+
*
96+
* @since 2.12
97+
*/
98+
WRAP_IDENTIFIER_IN_OBJECT(true)
9099
;
91100

92101
final boolean _defaultState;

hibernate5/src/main/java/com/fasterxml/jackson/datatype/hibernate5/HibernateProxySerializer.java

Lines changed: 43 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ public class HibernateProxySerializer
4747
protected final boolean _forceLazyLoading;
4848
protected final boolean _serializeIdentifier;
4949
protected final boolean _nullMissingEntities;
50+
protected final boolean _wrappedIdentifier;
5051
protected final Mapping _mapping;
5152

5253
/**
@@ -63,26 +64,36 @@ public class HibernateProxySerializer
6364

6465
public HibernateProxySerializer(boolean forceLazyLoading)
6566
{
66-
this(forceLazyLoading, false, false, null, null);
67+
this(forceLazyLoading, false, false, true, null, null);
6768
}
6869

6970
public HibernateProxySerializer(boolean forceLazyLoading, boolean serializeIdentifier) {
70-
this(forceLazyLoading, serializeIdentifier, false, null, null);
71+
this(forceLazyLoading, serializeIdentifier, false, true, null, null);
7172
}
7273

7374
public HibernateProxySerializer(boolean forceLazyLoading, boolean serializeIdentifier, Mapping mapping) {
74-
this(forceLazyLoading, serializeIdentifier, false, mapping, null);
75+
this(forceLazyLoading, serializeIdentifier, false, true, mapping, null);
7576
}
7677

7778
public HibernateProxySerializer(boolean forceLazyLoading, boolean serializeIdentifier, boolean nullMissingEntities, Mapping mapping) {
78-
this(forceLazyLoading, serializeIdentifier, nullMissingEntities, mapping, null);
79+
this(forceLazyLoading, serializeIdentifier, nullMissingEntities, true, mapping, null);
80+
}
81+
82+
public HibernateProxySerializer(boolean forceLazyLoading, boolean serializeIdentifier, boolean nullMissingEntities, boolean wrappedIdentifier, Mapping mapping) {
83+
this(forceLazyLoading, serializeIdentifier, nullMissingEntities, wrappedIdentifier, mapping, null);
7984
}
8085

8186
public HibernateProxySerializer(boolean forceLazyLoading, boolean serializeIdentifier, boolean nullMissingEntities, Mapping mapping,
8287
BeanProperty property) {
88+
this(forceLazyLoading, serializeIdentifier, nullMissingEntities, true, mapping, property);
89+
}
90+
91+
public HibernateProxySerializer(boolean forceLazyLoading, boolean serializeIdentifier, boolean nullMissingEntities, boolean wrappedIdentifier, Mapping mapping,
92+
BeanProperty property) {
8393
_forceLazyLoading = forceLazyLoading;
8494
_serializeIdentifier = serializeIdentifier;
8595
_nullMissingEntities = nullMissingEntities;
96+
_wrappedIdentifier = wrappedIdentifier;
8697
_mapping = mapping;
8798
_dynamicSerializers = PropertySerializerMap.emptyForProperties();
8899
_property = property;
@@ -187,22 +198,14 @@ protected Object findProxied(HibernateProxy proxy)
187198
LazyInitializer init = proxy.getHibernateLazyInitializer();
188199
if (!_forceLazyLoading && init.isUninitialized()) {
189200
if (_serializeIdentifier) {
190-
String idName;
191-
if (_mapping != null) {
192-
idName = _mapping.getIdentifierPropertyName(init.getEntityName());
201+
final Object idValue = init.getIdentifier();
202+
if (_wrappedIdentifier) {
203+
HashMap<String, Object> map = new HashMap<>();
204+
map.put(getIdentifierPropertyName(init), idValue);
205+
return map;
193206
} else {
194-
idName = ProxySessionReader.getIdentifierPropertyName(init);
195-
if (idName == null) {
196-
idName = ProxyReader.getIdentifierPropertyName(init);
197-
if (idName == null) {
198-
idName = init.getEntityName();
199-
}
200-
}
207+
return idValue;
201208
}
202-
final Object idValue = init.getIdentifier();
203-
HashMap<String, Object> map = new HashMap<String, Object>();
204-
map.put(idName, idValue);
205-
return map;
206209
}
207210
return null;
208211
}
@@ -216,6 +219,28 @@ protected Object findProxied(HibernateProxy proxy)
216219
}
217220
}
218221
}
222+
223+
/**
224+
* Helper method to retrieve the name of the identifier property of the
225+
* specified lazy initializer.
226+
* @param init Lazy initializer to obtain identifier property name from.
227+
* @return Name of the identity property of the specified lazy initializer.
228+
*/
229+
private String getIdentifierPropertyName(final LazyInitializer init) {
230+
String idName;
231+
if (_mapping != null) {
232+
idName = _mapping.getIdentifierPropertyName(init.getEntityName());
233+
} else {
234+
idName = ProxySessionReader.getIdentifierPropertyName(init);
235+
if (idName == null) {
236+
idName = ProxyReader.getIdentifierPropertyName(init);
237+
if (idName == null) {
238+
idName = init.getEntityName();
239+
}
240+
}
241+
}
242+
return idName;
243+
}
219244

220245
/**
221246
* Inspects a Hibernate proxy to try and determine the name of the identifier property

0 commit comments

Comments
 (0)