Using ThreadLocal for Thread-Specific Variables and DTOs in Java Applications
When the data submitted by the frontend differs significantly from the corresponding properties in the entity class, its advisable to use Data Transfer Objects (DTOs) to encapsulate the data.
In the service layer, where data transmission is required, you can use the following method to copy properties between types:
BeanUtils.copyProperties(frontendDTO, targetObject);
To retrieve the account ID associated with the method execution, ThreadLocal can be employed as a thread-specific variable. This allows you to obtain the current user's ID by setting it in an interceptor and then accessing it in the service layer using BaseContext.getCurrentId(). Here is an example implementation:
public class BaseContext {
private static final ThreadLocal<Long> userThreadLocal = new ThreadLocal<>();
public static void setCurrentUserId(Long userId) {
userThreadLocal.set(userId);
}
public static Long getCurrentUserId() {
return userThreadLocal.get();
}
public static void clearCurrentUserId() {
userThreadLocal.remove();
}
}
Additional Application of ThreadLocal: Addressing Date Format Issues in Pagination Modules
When returning JSON data to the frontend, date-time parameters may sometimes appear as array types, which is not the desired format. Here are two methods to resolve this issue:
Method 1: Add annotations to the properties
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.io.Serializable;
import java.time.LocalDateTime;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Employee implements Serializable {
private static final long serialVersionUID = 1L;
private Long id;
private String username;
private String name;
private String password;
private String phone;
private String sex;
private String idNumber;
private Integer status;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime updateTime;
private Long createUser;
private Long updateUser;
}
Method 2: Add a custom converter to the Spring MVC message converter container
This involves concepts of serialization and deserialization. Below is an example configuration:
// WebMvcConfiguration: This class extends WebMvcConfigurationSupport for message converters
/**
* Description: Extends MVC message converters
*/
@ApiOperation("Extend MVC message converters")
protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
// Create a message converter
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
// Set a custom object mapper for the message converter
converter.setObjectMapper(new CustomObjectMapper());
// Add the custom message converter to the MVC converters list
converters.add(0, converter);
}
Serialization and Deserialization Code
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES;
/**
* Object Mapper: Based on Jackson for converting Java objects to JSON and vice versa.
* Parsing JSON to Java objects is called deserialization.
* Generating JSON from Java objects is called serialization.
*/
public class CustomObjectMapper extends ObjectMapper {
public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm";
public static final String DEFAULT_TIME_FORMAT = "HH:mm:ss";
public CustomObjectMapper() {
super();
// Do not throw exceptions on unknown properties
this.configure(FAIL_ON_UNKNOWN_PROPERTIES, false);
// Handle missing properties during deserialization
this.getDeserializationConfig().withoutFeatures(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
SimpleModule dateTimeModule = new SimpleModule()
.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)))
.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));
// Register the module with custom serializers and deserializers
this.registerModule(dateTimeModule);
}
}