package com.xxx.x.xx.model;

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.Version;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;

import java.util.Date;
@Data
public class MybatisBaseModel implements Serializable {
    private static final long serialVersionUID = 1L;

    @TableField(fill = FieldFill.INSERT)
    private Long createBy;

    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @TableField(fill = FieldFill.INSERT)
    private Date createDt;

    @TableField(fill = FieldFill.INSERT)
    private Long createOrgan;

    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Long updateBy;

    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date updateDt;

    @TableLogic
    @TableField(fill = FieldFill.INSERT)
    private String isDelete;

    @TableField(fill = FieldFill.INSERT)
    private Long sortOrder;

    @Version
    @TableField(fill = FieldFill.INSERT)
    private Long rowVersion;

}

package com.zzz.z.zzz.model;

import com.alibaba.fastjson.annotation.JSONField;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.xxx.x.xx.model.MybatisBaseModel;
import com.purvar.petou.ei.common.BigDecimalSerializer;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;

@Data
@TableName("DEMO_MY_INFO")
public class DemoMyModel extends MybatisBaseModel {

    @ApiModelProperty(value = "主键ID")
    @TableId
    private Long id;

    @ApiModelProperty(value = "金额累计总额(万元)")
    @JSONField(serializeUsing = BigDecimalSerializer.class)
    private BigDecimal totalReturnInvestmentAmount;
 
    @ApiModelProperty(value = "出资基准日")
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd")
    private Date capitalContributionBasisDate;

	@ApiModelProperty(value = 项目个数(个)")
    private Integer projectCount;
  
    @ApiModelProperty(value = "备注")
    private String remarks;

    @ApiModelProperty(value = "其他项目详情")
    @TableField(exist = false)
    private List<ReturnInvestmentProjectsOtherModel> otherModels;
}

我们先看父类里面的注解

@Data

@Data 是 Lombok 框架提供的一个注解,它相当于一键生成实体类中常用的样板代码(getter、setter、toString 等),帮你省去手动编写这些方法的繁琐工作
@Data 是一个组合注解,它相当于同时加上了以下注解:

等价注解作用
@Getter为所有字段生成 getter 方法
@Setter为所有字段生成 setter 方法
@ToString生成 toString() 方法
@EqualsAndHashCode生成 equals() 和 hashCode() 方法
@RequiredArgsConstructor生成包含 final 字段和 @NonNull 字段的构造方法

@Data 经常和其他 Lombok 注解一起使用:

import lombok.*;

@Data
@NoArgsConstructor          // 无参构造
@AllArgsConstructor         // 全参构造
@Builder                    // 建造者模式
@Accessors(chain = true)    // 链式调用(setter 返回 this)
public class User {
    private Long id;
    private String name;
    private Integer age;
}

@TableField(fill = FieldFill.INSERT)

@TableField(fill = FieldFill.INSERT) 是 MyBatis-Plus 提供的字段自动填充功能,用于在插入数据时自动为某个字段赋值,无需手动设置。

部分说明
@TableFieldMyBatis-Plus 的字段注解
fill指定自动填充策略
FieldFill.INSERT仅在插入(INSERT)时自动填充该字段

如何生效?——需要配合 MetaObjectHandler

package com.xx.xx.common.mybatis.config;

import com.baomidou.mybatisplus.autoconfigure.ConfigurationCustomizer;
import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.purvar.ezgo.common.mybatis.injector.EzgoSqlInjector;
import com.purvar.ezgo.common.mybatis.properties.TenantProperties;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.Date;


/**
 * mybatis-plus配置文件
 */
@Configuration
@EnableConfigurationProperties(TenantProperties.class)
public class MybatisPlusConfiguration {


    /**
     * 配置全局map的key转小写
     *
     * @return ConfigurationCustomizer
     */
    public ConfigurationCustomizer configurationCustomizer() {
        return new ConfigurationCustomizer() {
            @Override
            public void customize(MybatisConfiguration configuration) {
                configuration.setObjectWrapperFactory(new MapWrapperFactory());
            }
        };
    }

    /**
     * 自定义sql注入器
     *
     * @return
     */
    @Bean
    public EzgoSqlInjector ezgoSqlInjector() {
        return new EzgoSqlInjector();
    }

    /**
     * mybatis-plus插件配置
     *
     * @return MybatisPlusInterceptor
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // mybatis-plus分页插件
        interceptor.addInnerInterceptor(paginationInnerInterceptor());

        //添加乐观锁插件
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());

        return interceptor;
    }

    public PaginationInnerInterceptor paginationInnerInterceptor() {
        PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();
        // 设置最大单页限制数量,默认 500 条,-1 不受限制
        paginationInnerInterceptor.setMaxLimit(-1L);
        // 分页合理化
        paginationInnerInterceptor.setOverflow(true);
        return paginationInnerInterceptor;
    }

    /**
     * mybatis-plus自动填充策略
     *
     * @return MetaObjectHandler
     */
    @Bean
    public MetaObjectHandler mybatisObjectHandler() {
        return new MetaObjectHandler() {
            @Override
            public void insertFill(MetaObject metaObject) {
                Date now = new Date();
                Long userId = 1L;
                this.strictInsertFill(metaObject, "createBy", Long.class, userId);
                this.strictInsertFill(metaObject, "updateBy", Long.class, userId);
                this.strictInsertFill(metaObject, "createDt", Date.class, now);
                this.strictInsertFill(metaObject, "updateDt", Date.class, now);
                this.strictInsertFill(metaObject, "isDelete", String.class, "0");
            }

            @Override
            public void updateFill(MetaObject metaObject) {
                Date now = new Date();
                Long userId = 1L;
                this.strictUpdateFill(metaObject, "updateBy", Long.class, userId);
                this.strictUpdateFill(metaObject, "updateDt", Date.class, now);
            }
        };
    }
}

策略说明适用字段
FieldFill.DEFAULT默认不填充
FieldFill.INSERT仅插入时填充create_timecreate_by
FieldFill.UPDATE仅更新时填充update_timeupdate_by
FieldFill.INSERT_UPDATE插入和更新都填充version(乐观锁版本号)
  1. 必须注册 MetaObjectHandler:只加注解不写处理器,填充不会生效。
  2. 手动赋值会覆盖自动填充:如果你在代码中手动设置了字段值,会以手动值为准。
  3. 字段名匹配:strictInsertFill 第一个参数传的是实体类的属性名(驼峰),MyBatis-Plus 会自动映射到数据库列名(下划线)。
  4. Spring Boot 自动扫描:MetaObjectHandler 实现类需要加上 @Component,让 Spring 管理。

@TableField(fill = FieldFill.XXX) 的自动填充功能,只有在使用 MyBatis-Plus
提供的内置方法时才会生效。如果你自己写自定义 SQL(XML 或 @Select/@Insert 等注解),自动填充不会触发。

使用 MyBatis-Plus 内置的 CRUD 方法时,自动填充会生效:

// ✅ 这些方法会触发自动填充
userMapper.insert(user);
userMapper.updateById(user);
userService.save(user);
userService.updateById(user);

哪些情况不会生效?

  1. 自定义 XML SQL
自定义注解 SQL
  1. 自定义注解 SQL
@Mapper
public interface UserMapper extends BaseMapper<User> {

    // ❌ 不会触发自动填充
    @Insert("INSERT INTO user (name, age) VALUES (#{name}, #{age})")
    void insertUser(User user);
}
// ❌ 不会触发自动填充
@Update("UPDATE user SET name = #{name} WHERE id = #{id}")
void updateName(@Param("id") Long id, @Param("name") String name);
使用方式自动填充是否生效
mapper.insert(entity)✅ 生效
service.save(entity)✅ 生效
mapper.updateById(entity)✅ 生效
自定义 XML SQL❌ 不生效
@Insert / @Update 注解 SQL❌ 不生效

@JSONField(serializeUsing = BigDecimalSerializer.class)

@JSONField(serializeUsing = BigDecimalSerializer.class) 是 FastJSON(阿里巴巴的 JSON 序列化/反序列化库)中的注解,用于自定义某个字段的序列化方式。

部分说明
@JSONFieldFastJSON 提供的字段级注解,控制 JSON 序列化/反序列化行为
serializeUsing指定自定义序列化器(Serializer),控制该字段输出为 JSON 时的格式
BigDecimalSerializer.class一个实现了 FastJSON 序列化接口的类,专门处理 BigDecimal 类型的格式化输出

为什么需要它?
BigDecimal 默认的 JSON 序列化输出可能不符合业务需求,常见问题:

  1. 科学计数法:new BigDecimal(“10000000000”) 可能被序列化为 1.0E+10
  2. 多余的尾零:new BigDecimal(“10.50”) 序列化为 10.50,但前端可能只需要 10.5
  3. null 值处理:null 的 BigDecimal 输出为 null,但业务可能希望输出 “0” 或 “”
    通过 serializeUsing 指定自定义序列化器,可以统一控制输出格式。
package com.xxxx.xx.xx.common;

import com.alibaba.fastjson.serializer.JSONSerializer;
import com.alibaba.fastjson.serializer.ObjectSerializer;
import com.alibaba.fastjson.serializer.SerializeWriter;

import java.io.IOException;
import java.lang.reflect.Type;
import java.math.BigDecimal;

public class BigDecimalSerializer implements ObjectSerializer {
    @Override
    public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features) throws IOException {
        SerializeWriter out = serializer.out;
        if (object == null) {
            out.writeNull();
            return;
        }
        if (object != null) {
            BigDecimal value = (BigDecimal) object;
            serializer.write(value.stripTrailingZeros().toPlainString());
        }
    }
}

测试效果:

Order order = new Order();
order.setId(1L);
order.setOrderNo("NO20260715001");
order.setAmount(new BigDecimal("10000000000"));  // 10位数字
order.setPrice(new BigDecimal("10.50"));

String json = JSON.toJSONString(order);
// 输出:{"id":1,"orderNo":"NO20260715001","amount":"10000000000","price":"10.5"}
// ✅ 没有科学计数法,price 去掉了尾零

其他常见的 @JSONField 用法

public class User {

    // 指定 JSON 中的字段名
    @JSONField(name = "user_name")
    private String userName;

    // 格式化日期
    @JSONField(format = "yyyy-MM-dd HH:mm:ss")
    private Date createTime;

    // 序列化时忽略该字段(不输出到 JSON)
    @JSONField(serialize = false)
    private String password;

    // 反序列化时忽略该字段(不从 JSON 读取)
    @JSONField(deserialize = false)
    private String internalFlag;

    // 控制字段输出顺序
    @JSONField(ordinal = 1)
    private Long id;
}

@DateTimeFormat(pattern = “yyyy-MM-dd”)

方向:反序列化(请求参数 → Java 对象)
用于将前端传来的字符串解析为 Java 的日期类型(Date、LocalDate、LocalDateTime 等)。

@GetMapping("/list")
public List<Order> list(
    @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate startDate,
    @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate endDate) {
    // 前端请求:/list?startDate=2026-07-01&endDate=2026-07-15
    // Spring 会自动把字符串 "2026-07-01" 解析为 LocalDate 对象
}

注意:@DateTimeFormat 只影响请求参数的解析,对返回 JSON 没有任何作用。

@JsonFormat(timezone = “GMT+8”,pattern = “yyyy-MM-dd”)

这是 Jackson 提供的注解,控制 Java 日期对象输出为 JSON 时的格式。

@Data
public class Order {
    private Long id;

    @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd")
    private Date createTime;
}
Order order = new Order();
order.setId(1L);
order.setCreateTime(new Date());

String json = new ObjectMapper().writeValueAsString(order);
// 输出:{"id":1,"createTime":"2026-07-15"}

@JSONField(format = “yyyy-MM-dd HH:mm:ss”)

方向:序列化 + 反序列化(双向)
这是 FastJSON 提供的注解,format 属性同时控制序列化和反序列化的日期格式。

import com.alibaba.fastjson.annotation.JSONField;

@Data
public class Order {
    private Long id;

    @JSONField(format = "yyyy-MM-dd HH:mm:ss")
    private Date createTime;
}
// 序列化:Java → JSON
Order order = new Order();
order.setId(1L);
order.setCreateTime(new Date());
String json = JSON.toJSONString(order);
// 输出:{"id":1,"createTime":"2026-07-15 10:30:45"}

// 反序列化:JSON → Java
String jsonInput = "{\"id\":2,\"createTime\":\"2026-07-14 08:00:00\"}";
Order order2 = JSON.parseObject(jsonInput, Order.class);
// createTime 会被正确解析为 Date 对象
注解所属框架作用方向典型场景
@DateTimeFormatSpring Framework入参:字符串 → Java 日期对象前端传日期字符串给后端
@JsonFormatJackson出参:Java 日期对象 → JSON 字符串后端返回 JSON 给前端
@JSONFieldFastJSON出参/入参:Java 日期对象 ↔ JSON 字符串后端返回 JSON 给前端(使用 FastJSON 时)

实际项目中常见的组合

Spring Boot + Jackson(默认)

@Data
public class Order {
    private Long id;

    @DateTimeFormat(pattern = "yyyy-MM-dd")          // 接收前端参数
    @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd")  // 返回给前端
    private LocalDate orderDate;
}

或:
Spring Boot + FastJSON(替换了默认 JSON 处理器)

@Data
public class Order {
    private Long id;

    @DateTimeFormat(pattern = "yyyy-MM-dd")          // 接收前端参数(Spring 负责)
    @JSONField(format = "yyyy-MM-dd")                // 返回给前端(FastJSON 负责)
    private LocalDate orderDate;
}

只设置 @JSONField(format = “yyyy-MM-dd HH:mm:ss”)

  1. 作为响应返回给前端(✅ 完全够用)

  2. 通过 @RequestBody 接收前端 JSON(✅ 够用)
    效果:FastJSON 会根据 @JSONField(format = …) 自动将字符串解析为 LocalDateTime。✅ 没问题

  3. 通过 URL 参数或表单接收(❌ 不够用)

前端请求:/list?startTime=2026-07-15 10:00:00&endTime=2026-07-15 18:00:00
效果:❌ 会报错。因为 URL 参数的解析是 Spring MVC 负责的,它用的是 @DateTimeFormat,完全不会看 @JSONField。

场景是否需要 @DateTimeFormat是否需要 @JSONField
返回 JSON 给前端❌ 不需要✅ 需要
@RequestBody 接收 JSON❌ 不需要(@JSONField 同时管反序列化)✅ 需要
@RequestParam / @ModelAttribute 接收参数✅ 需要❌ 不起作用
  • 如果你的日期字段只出现在请求体和响应体中(前后端通过 JSON 交互),单独用 @JSONField(format = …) 就够了。
  • 如果日期字段还会作为 URL 参数或表单参数传入,就必须额外加上 @DateTimeFormat。

我们可以理解一下
后端给前端, 前端给后端, 如果传递的是一个对象, 那他们必须通过序列化和反序列化处理
用 对象传值 @JSONField(format = “yyyy-MM-dd HH:mm:ss”)是可以的, 但过不是对象呢? 前端路由上传了一个日期 @RequestParam 则需要手动加上 才能解析@DateTimeFormat(pattern = “yyyy-MM-dd HH:mm:ss”)

下面的例子更直观一些.
场景一:
只写@JSONField(format = “yyyy-MM-dd”)

// 接收 JSON 请求体
@PostMapping("/order")
public void create(@RequestBody Order order) {
    // 前端传 {"orderDate": "2026-07-15"}
    // FastJSON 自动按 format 解析 → ✅ 生效
}

// 返回 JSON 响应
@GetMapping("/order/{id}")
public Order get(@PathVariable Long id) {
    return orderService.getById(id);
    // FastJSON 自动按 format 格式化 → ✅ 生效
}

这种情况下,orderDate 的进出都走 JSON,@JSONField 同时管序列化和反序列化,确实不需要 @DateTimeFormat。

场景二:Order 用于接收表单或 URL 参数(❌ 只写 @JSONField 不够)

@Data
public class OrderQuery {
    private String name;

    @JSONField(format = "yyyy-MM-dd")  // ❌ 这里不起作用!
    private LocalDate startDate;
}
// 前端请求:/list?name=张三&startDate=2026-07-01
@GetMapping("/list")
public List<Order> list(OrderQuery query) {  // Spring 用 @ModelAttribute 方式绑定
    // ❌ startDate 会绑定失败,报类型转换错误
}

原因:OrderQuery query 这种写法,Spring 是通过 @ModelAttribute 机制(表单/URL 参数绑定)来填充对象的,这个过程由 Spring 的 DataBinder 负责,它完全不认识 @JSONField,只看 @DateTimeFormat。

属性上什么也不写

普通属性什么都不写,默认就能正常序列化和反序列化。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐