Skip to content

Commit c377b05

Browse files
chore(migration): migrate from Jackson 2 to Jackson 3 (tools.jackson.*)
Spring Boot 4 ships Jackson 3 under the tools.jackson.* groupId. Updated pom.xml, all production sources, and test files to use the new packages and renamed API surface: - jackson-databind groupId: com.fasterxml.jackson.core → tools.jackson.core - Removed jackson-datatype-jsr310 (Java Time support is built-in to databind 3.x) - ObjectMapper: getFactory().createGenerator/Parser → createGenerator/createParser - JsonGenerator: writeFieldName → writeName, writeObject → writePOJO, writeXxxField → writeXxxProperty, writeObjectFieldStart/writeArrayFieldStart → writeName + writeStartObject/writeStartArray - JsonMapper.builder(): removed .disable(WRITE_DATES_AS_TIMESTAMPS) (ISO-8601 is the default in Jackson 3, no flag needed) - Test setUp(): removed .addModule(new JavaTimeModule()) (module no longer exists) All 61 unit tests pass. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent f7bf8a8 commit c377b05

8 files changed

Lines changed: 47 additions & 55 deletions

File tree

extensions/saas/sources/migration/pom.xml

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -100,18 +100,12 @@
100100
<artifactId>spring-boot-autoconfigure</artifactId>
101101
</dependency>
102102

103-
<!-- Jackson Databind: ObjectMapper, JsonGenerator, JsonParser -->
103+
<!-- Jackson 3: ObjectMapper, JsonGenerator, JsonParser (Java Time built-in) -->
104104
<dependency>
105-
<groupId>com.fasterxml.jackson.core</groupId>
105+
<groupId>tools.jackson.core</groupId>
106106
<artifactId>jackson-databind</artifactId>
107107
</dependency>
108108

109-
<!-- Jackson JavaTime module: LocalDate, LocalDateTime serialization -->
110-
<dependency>
111-
<groupId>com.fasterxml.jackson.datatype</groupId>
112-
<artifactId>jackson-datatype-jsr310</artifactId>
113-
</dependency>
114-
115109
<!-- Jakarta APIs (provided by container) -->
116110
<dependency>
117111
<groupId>jakarta.servlet</groupId>

extensions/saas/sources/migration/src/main/java/tools/dynamia/modules/saas/migration/config/AccountMigrationConfig.java

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@
1111
package tools.dynamia.modules.saas.migration.config;
1212

1313
import com.fasterxml.jackson.annotation.JsonInclude;
14-
import com.fasterxml.jackson.databind.DeserializationFeature;
15-
import com.fasterxml.jackson.databind.ObjectMapper;
16-
import com.fasterxml.jackson.databind.SerializationFeature;
17-
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
14+
import tools.jackson.databind.DeserializationFeature;
15+
import tools.jackson.databind.ObjectMapper;
16+
import tools.jackson.databind.SerializationFeature;
17+
import tools.jackson.databind.json.JsonMapper;
1818
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
1919
import org.springframework.boot.context.properties.EnableConfigurationProperties;
2020
import org.springframework.context.annotation.Bean;
@@ -53,7 +53,7 @@ public AccountMigrationConfig(AccountMigrationProperties properties) {
5353
*
5454
* <p>Configured to:
5555
* <ul>
56-
* <li>Support Java 8+ date/time types via {@link JavaTimeModule}.</li>
56+
* <li>Java time types supported natively (Jackson 3 built-in, no module needed).</li>
5757
* <li>Not fail on unknown properties during import.</li>
5858
* <li>Not fail on empty beans.</li>
5959
* <li>Exclude null values from output (smaller files).</li>
@@ -65,12 +65,12 @@ public AccountMigrationConfig(AccountMigrationProperties properties) {
6565
@Bean("migrationObjectMapper")
6666
@ConditionalOnMissingBean(name = "migrationObjectMapper")
6767
public ObjectMapper migrationObjectMapper() {
68-
return new ObjectMapper()
69-
.registerModule(new JavaTimeModule())
70-
.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
68+
return JsonMapper.builder()
7169
.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS)
7270
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
73-
.setSerializationInclusion(JsonInclude.Include.NON_NULL);
71+
.changeDefaultPropertyInclusion(
72+
i -> i.withValueInclusion(JsonInclude.Include.NON_NULL))
73+
.build();
7474
}
7575

7676
private void initOutputDirectory() {

extensions/saas/sources/migration/src/main/java/tools/dynamia/modules/saas/migration/pipeline/ExportPipeline.java

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@
1010
*/
1111
package tools.dynamia.modules.saas.migration.pipeline;
1212

13-
import com.fasterxml.jackson.databind.ObjectMapper;
14-
import com.fasterxml.jackson.core.JsonGenerator;
13+
import tools.jackson.databind.ObjectMapper;
14+
import tools.jackson.core.JsonGenerator;
1515
import jakarta.persistence.EntityManagerFactory;
1616
import jakarta.persistence.metamodel.Attribute.PersistentAttributeType;
1717
import jakarta.persistence.metamodel.EntityType;
@@ -133,21 +133,22 @@ public void export(Long accountId,
133133
throw new MigrationException("Failed to set up output stream", e);
134134
}
135135

136-
try (JsonGenerator gen = objectMapper.getFactory().createGenerator(target)) {
136+
try (JsonGenerator gen = objectMapper.createGenerator(target)) {
137137

138138
gen.writeStartObject();
139-
gen.writeStringField(ExportConstants.FIELD_VERSION, ExportConstants.FORMAT_VERSION);
140-
gen.writeStringField(ExportConstants.FIELD_EXPORTED_AT, LocalDateTime.now().toString());
141-
gen.writeNumberField(ExportConstants.FIELD_SOURCE_ACCOUNT_ID, accountId);
142-
gen.writeStringField(ExportConstants.FIELD_IDENTITY_STRATEGY,
139+
gen.writeStringProperty(ExportConstants.FIELD_VERSION, ExportConstants.FORMAT_VERSION);
140+
gen.writeStringProperty(ExportConstants.FIELD_EXPORTED_AT, LocalDateTime.now().toString());
141+
gen.writeNumberProperty(ExportConstants.FIELD_SOURCE_ACCOUNT_ID, accountId);
142+
gen.writeStringProperty(ExportConstants.FIELD_IDENTITY_STRATEGY,
143143
options.getIdentityStrategy().name());
144144

145145
// Serialize AccountDTO as the tenant descriptor
146-
gen.writeFieldName(ExportConstants.FIELD_ACCOUNT);
146+
gen.writeName(ExportConstants.FIELD_ACCOUNT);
147147
objectMapper.writeValue(gen, account.toDTO());
148148

149149
// Entity section
150-
gen.writeObjectFieldStart(ExportConstants.FIELD_ENTITIES);
150+
gen.writeName(ExportConstants.FIELD_ENTITIES);
151+
gen.writeStartObject();
151152

152153
long processed = 0;
153154
for (Class<?> entityClass : ordered) {
@@ -189,7 +190,8 @@ private long exportEntityType(JsonGenerator gen, Class<?> entityClass, Long acco
189190
long processed = 0;
190191
int chunkSize = resolveChunkSize(options);
191192

192-
gen.writeArrayFieldStart(entityClass.getName());
193+
gen.writeName(entityClass.getName());
194+
gen.writeStartArray();
193195

194196
try {
195197
EntityType<?> entityType = emf.getMetamodel().entity(entityClass);
@@ -244,8 +246,8 @@ private void writeEntity(JsonGenerator gen, Object entity, EntityType<?> entityT
244246
// Write ID explicitly
245247
try {
246248
Serializable id = JpaUtils.getJPAIdValue(entity);
247-
gen.writeFieldName("id");
248-
gen.writeObject(id);
249+
gen.writeName("id");
250+
gen.writePOJO(id);
249251
} catch (Exception e) {
250252
log.debug("[Migration/Export] Could not write ID for {}", entity.getClass().getSimpleName());
251253
}
@@ -270,16 +272,16 @@ private void writeEntity(JsonGenerator gen, Object entity, EntityType<?> entityT
270272
|| pt == PersistentAttributeType.ONE_TO_ONE) {
271273
if (value != null) {
272274
Serializable refId = JpaUtils.getJPAIdValue(value);
273-
gen.writeFieldName(name + ExportConstants.REF_ID_SUFFIX);
274-
gen.writeObject(refId);
275+
gen.writeName(name + ExportConstants.REF_ID_SUFFIX);
276+
gen.writePOJO(refId);
275277
}
276278
} else if (pt == PersistentAttributeType.ONE_TO_MANY
277279
|| pt == PersistentAttributeType.MANY_TO_MANY
278280
|| pt == PersistentAttributeType.ELEMENT_COLLECTION) {
279281
// Skip collections — they are reconstructed via child entities
280282
} else {
281283
// BASIC or EMBEDDED
282-
gen.writeFieldName(name);
284+
gen.writeName(name);
283285
objectMapper.writeValue(gen, value);
284286
}
285287

extensions/saas/sources/migration/src/main/java/tools/dynamia/modules/saas/migration/pipeline/ImportPipeline.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@
1010
*/
1111
package tools.dynamia.modules.saas.migration.pipeline;
1212

13-
import com.fasterxml.jackson.core.JsonParser;
14-
import com.fasterxml.jackson.core.JsonToken;
15-
import com.fasterxml.jackson.databind.JsonNode;
16-
import com.fasterxml.jackson.databind.ObjectMapper;
13+
import tools.jackson.core.JsonParser;
14+
import tools.jackson.core.JsonToken;
15+
import tools.jackson.databind.JsonNode;
16+
import tools.jackson.databind.ObjectMapper;
1717
import jakarta.persistence.EntityManager;
1818
import jakarta.persistence.EntityManagerFactory;
1919
import jakarta.persistence.PersistenceContext;
@@ -115,7 +115,7 @@ public void importTenant(InputStream input,
115115
throw new MigrationException("Failed to open input stream", e);
116116
}
117117

118-
try (JsonParser parser = objectMapper.getFactory().createParser(source)) {
118+
try (JsonParser parser = objectMapper.createParser(source)) {
119119

120120
expectToken(parser, JsonToken.START_OBJECT, "root object");
121121
long totalProcessed = 0;

extensions/saas/sources/migration/src/main/java/tools/dynamia/modules/saas/migration/services/AccountMigrationJobServiceImpl.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
*/
1111
package tools.dynamia.modules.saas.migration.services;
1212

13-
import com.fasterxml.jackson.databind.ObjectMapper;
13+
import tools.jackson.databind.ObjectMapper;
1414
import org.slf4j.Logger;
1515
import org.slf4j.LoggerFactory;
1616
import org.springframework.beans.factory.annotation.Qualifier;

extensions/saas/sources/migration/src/test/java/tools/dynamia/modules/saas/migration/api/OptionsFluentBuilderTest.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,8 @@
1010
*/
1111
package tools.dynamia.modules.saas.migration.api;
1212

13-
import com.fasterxml.jackson.databind.ObjectMapper;
14-
import com.fasterxml.jackson.databind.json.JsonMapper;
15-
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
13+
import tools.jackson.databind.ObjectMapper;
14+
import tools.jackson.databind.json.JsonMapper;
1615
import org.junit.Assert;
1716
import org.junit.Before;
1817
import org.junit.Test;
@@ -28,7 +27,6 @@ public class OptionsFluentBuilderTest {
2827
@Before
2928
public void setUp() {
3029
objectMapper = JsonMapper.builder()
31-
.addModule(new JavaTimeModule())
3230
.build();
3331
}
3432

extensions/saas/sources/migration/src/test/java/tools/dynamia/modules/saas/migration/pipeline/ExportConstantsTest.java

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@
1010
*/
1111
package tools.dynamia.modules.saas.migration.pipeline;
1212

13-
import com.fasterxml.jackson.databind.JsonNode;
14-
import com.fasterxml.jackson.databind.ObjectMapper;
13+
import tools.jackson.databind.JsonNode;
14+
import tools.jackson.databind.ObjectMapper;
1515
import org.junit.Assert;
1616
import org.junit.Test;
1717

@@ -56,16 +56,16 @@ public void minimalExportJsonStructureIsValid() throws IOException {
5656
// Build the minimal JSON skeleton that ImportPipeline expects
5757
ObjectMapper mapper = new ObjectMapper();
5858
ByteArrayOutputStream out = new ByteArrayOutputStream();
59-
var gen = mapper.getFactory().createGenerator(out);
59+
var gen = mapper.createGenerator(out);
6060

6161
gen.writeStartObject();
62-
gen.writeStringField(ExportConstants.FIELD_VERSION, ExportConstants.FORMAT_VERSION);
63-
gen.writeStringField(ExportConstants.FIELD_EXPORTED_AT, "2026-06-15T10:00:00");
64-
gen.writeNumberField(ExportConstants.FIELD_SOURCE_ACCOUNT_ID, 1L);
65-
gen.writeStringField(ExportConstants.FIELD_IDENTITY_STRATEGY, "KEEP_IDS");
66-
gen.writeObjectFieldStart(ExportConstants.FIELD_ACCOUNT);
62+
gen.writeStringProperty(ExportConstants.FIELD_VERSION, ExportConstants.FORMAT_VERSION);
63+
gen.writeStringProperty(ExportConstants.FIELD_EXPORTED_AT, "2026-06-15T10:00:00");
64+
gen.writeNumberProperty(ExportConstants.FIELD_SOURCE_ACCOUNT_ID, 1L);
65+
gen.writeStringProperty(ExportConstants.FIELD_IDENTITY_STRATEGY, "KEEP_IDS");
66+
gen.writeName(ExportConstants.FIELD_ACCOUNT); gen.writeStartObject();
6767
gen.writeEndObject();
68-
gen.writeObjectFieldStart(ExportConstants.FIELD_ENTITIES);
68+
gen.writeName(ExportConstants.FIELD_ENTITIES); gen.writeStartObject();
6969
gen.writeEndObject();
7070
gen.writeEndObject();
7171
gen.close();

extensions/saas/sources/migration/src/test/java/tools/dynamia/modules/saas/migration/pipeline/ImportPipelineMapperResolutionTest.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,8 @@
1010
*/
1111
package tools.dynamia.modules.saas.migration.pipeline;
1212

13-
import com.fasterxml.jackson.databind.ObjectMapper;
14-
import com.fasterxml.jackson.databind.json.JsonMapper;
15-
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
13+
import tools.jackson.databind.ObjectMapper;
14+
import tools.jackson.databind.json.JsonMapper;
1615
import jakarta.persistence.EntityManagerFactory;
1716
import org.junit.Assert;
1817
import org.junit.Before;
@@ -59,7 +58,6 @@ public class ImportPipelineMapperResolutionTest {
5958
public void setUp() {
6059
properties = new AccountMigrationProperties();
6160
objectMapper = JsonMapper.builder()
62-
.addModule(new JavaTimeModule())
6361
.build();
6462

6563
// Minimal metamodel: getEntities() returns empty set so no entity processing occurs

0 commit comments

Comments
 (0)