Skip to content

Commit a7098ce

Browse files
committed
improve SERIALIZE_IDENTIFIER_FOR_LAZY_NOT_LOADED_OBJECTS feature
- don't require session or mapping for ids mapped with property access - add a testcase - improve documentation
1 parent a71591e commit a7098ce

File tree

3 files changed

+79
-5
lines changed

3 files changed

+79
-5
lines changed

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

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,19 @@ public enum Feature {
3131
USE_TRANSIENT_ANNOTATION(true),
3232

3333
/**
34-
* If FORCE_LAZY_LOADING is false lazy-loaded object should be serialized as map IdentifierName=>IdentifierValue
35-
* instead of null (true); or serialized as nulls (false)
34+
* If FORCE_LAZY_LOADING is false, this feature serializes uninitialized lazy loading proxies as
35+
* <code>{"identifierName":"identifierValue"}</code> rather than <code>null</code>.
3636
* <p>
37-
* Default value is false.
37+
* Default value is false.
38+
* <p>
39+
* Note that the name of the identifier property can only be determined if
40+
* <ul>
41+
* <li>the {@link Mapping} is provided to the Hibernate4Module, or </li>
42+
* <li>the persistence context that loaded the proxy has not yet been closed, or</li>
43+
* <li>the id property is mapped with property access (for instance because the {@code @Id}
44+
* annotation is applied to a method rather than a field)</li>
45+
* </ul>
46+
* Otherwise, the entity name will be used instead.
3847
*/
3948
SERIALIZE_IDENTIFIER_FOR_LAZY_NOT_LOADED_OBJECTS(false),
4049

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

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package com.fasterxml.jackson.datatype.hibernate4;
22

33
import java.io.IOException;
4+
import java.lang.reflect.Field;
5+
import java.lang.reflect.Method;
46
import java.util.HashMap;
57

68
import com.fasterxml.jackson.core.*;
@@ -18,6 +20,7 @@
1820
import org.hibernate.engine.spi.SessionImplementor;
1921
import org.hibernate.proxy.HibernateProxy;
2022
import org.hibernate.proxy.LazyInitializer;
23+
import org.hibernate.proxy.pojo.BasicLazyInitializer;
2124

2225
/**
2326
* Serializer to use for values proxied using {@link org.hibernate.proxy.HibernateProxy}.
@@ -174,15 +177,18 @@ protected Object findProxied(HibernateProxy proxy)
174177
LazyInitializer init = proxy.getHibernateLazyInitializer();
175178
if (!_forceLazyLoading && init.isUninitialized()) {
176179
if (_serializeIdentifier) {
177-
final String idName;
180+
String idName;
178181
if (_mapping != null) {
179182
idName = _mapping.getIdentifierPropertyName(init.getEntityName());
180183
} else {
181184
final SessionImplementor session = init.getSession();
182185
if (session != null) {
183186
idName = session.getFactory().getIdentifierPropertyName(init.getEntityName());
184187
} else {
185-
idName = init.getEntityName();
188+
idName = ProxyReader.getIdentifierPropertyName(init);
189+
if (idName == null) {
190+
idName = init.getEntityName();
191+
}
186192
}
187193
}
188194
final Object idValue = init.getIdentifier();
@@ -194,4 +200,40 @@ protected Object findProxied(HibernateProxy proxy)
194200
}
195201
return init.getImplementation();
196202
}
203+
204+
// Alas, hibernate offers no public api to access this information, so we must resort to ugly hacks ...
205+
protected static class ProxyReader {
206+
207+
// static final so the JVM can inline the lookup
208+
private static final Field getIdentifierMethod;
209+
210+
static {
211+
try {
212+
getIdentifierMethod = BasicLazyInitializer.class.getDeclaredField("getIdentifierMethod");
213+
getIdentifierMethod.setAccessible(true);
214+
} catch (Exception e) {
215+
// should never happen: the field exists in all versions of hibernate 4 and 5
216+
throw new RuntimeException(e);
217+
}
218+
}
219+
220+
/**
221+
* @return the name of the identifier property, or null if the name could not be determined
222+
*/
223+
static String getIdentifierPropertyName(LazyInitializer init) {
224+
try {
225+
Method idGetter = (Method) getIdentifierMethod.get(init);
226+
if (idGetter == null) {
227+
return null;
228+
}
229+
String name = idGetter.getName();
230+
if (name.startsWith("get")) {
231+
name = Character.toLowerCase(name.charAt(3)) + name.substring(4);
232+
}
233+
return name;
234+
} catch (Exception e) {
235+
throw new RuntimeException(e);
236+
}
237+
}
238+
}
197239
}

hibernate4/src/test/java/com/fasterxml/jackson/datatype/hibernate4/LazyLoadingTest.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66
import javax.persistence.EntityManagerFactory;
77
import javax.persistence.Persistence;
88

9+
import com.fasterxml.jackson.core.JsonProcessingException;
910
import com.fasterxml.jackson.databind.ObjectMapper;
11+
import com.fasterxml.jackson.datatype.hibernate4.Hibernate4Module.Feature;
1012
import com.fasterxml.jackson.datatype.hibernate4.data.Customer;
1113
import com.fasterxml.jackson.datatype.hibernate4.data.Payment;
1214

@@ -54,4 +56,25 @@ public void testGetCustomerJson() throws Exception
5456
emf.close();
5557
}
5658
}
59+
60+
@Test
61+
public void testSerializeIdentifierFeature() throws JsonProcessingException {
62+
Hibernate4Module module = new Hibernate4Module();
63+
module.enable(Feature.SERIALIZE_IDENTIFIER_FOR_LAZY_NOT_LOADED_OBJECTS);
64+
ObjectMapper objectMapper = new ObjectMapper().registerModule(module);
65+
66+
EntityManagerFactory emf = Persistence.createEntityManagerFactory("persistenceUnit");
67+
try {
68+
EntityManager em = emf.createEntityManager();
69+
Customer customerRef = em.getReference(Customer.class, 103);
70+
em.close();
71+
assertFalse(Hibernate.isInitialized(customerRef));
72+
73+
String json = objectMapper.writeValueAsString(customerRef);
74+
assertFalse(Hibernate.isInitialized(customerRef));
75+
assertEquals("{\"customerNumber\":103}", json);
76+
} finally {
77+
emf.close();
78+
}
79+
}
5780
}

0 commit comments

Comments
 (0)