You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java含父类对象导出CSV:关联User对象的ID未被记录问题排查

Java对象导出CSV时关联对象ID缺失问题解决

问题概述

将继承自BaseEntity(含id字段)的Order对象导出CSV时,Order自身的id字段正常写入,但关联的User对象(同样继承BaseEntity)的id未出现在导出结果中。

实体类代码

BaseEntity

@Data
@NoArgsConstructor
@AllArgsConstructor
@SuperBuilder
@MappedSuperclass
public class BaseEntity {

    @Id
    @Column(name = "id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @CsvBindByPosition(position = 0)
    protected Integer id;
}

Order类

@Data
@EqualsAndHashCode(callSuper = true)
@SuperBuilder
@NoArgsConstructor
@Entity
@Table(name = "orders")
public class Order extends BaseEntity {
    @Column(name = "price")
    @CsvBindByPosition(position = 1)
    private Float price;

    @Column(name = "date")
    @CsvBindByName
    @CsvBindByPosition(position = 2)
    private LocalDate date;

    @ManyToOne(optional = false)
    @JoinColumn(name = "user_id", nullable = false)
    @CsvBindByName
    @CsvBindByPosition(position = 3)
    private User user;

    @Column(name = "cc_number")
    @CsvBindByPosition(position = 5)
    private String creditCardNumber;

    @Column(name = "shipping_type")
    @CsvBindByPosition(position = 6)
    private String shippingType;

    @Column(name = "shipping_cost")
    @CsvBindByPosition(position = 7)
    private Float shippingCost;

    @Column(name = "code")
    @CsvBindByPosition(position = 8)
    private String code;

    @Column(name = "address")
    @CsvBindByPosition(position = 9)
    private String address;

    @Column(name = "customer_notes")
    @CsvBindByPosition(position = 10)
    private String customerNotes;
}

User类

@Data
@EqualsAndHashCode(callSuper = true)
@SuperBuilder
@NoArgsConstructor
@Entity
@Table(name = "users")
public class User extends BaseEntity{

    @NotNull(message = "Field is null validation error")
    @Email(message = "Field does not satisfy regexp")
    @Column(name = "mail", unique = true)
    @CsvBindByPosition(position = 1)
    private String mail;

    @NotNull(message = "Field is null validation error")
    @Pattern(regexp = "^(?=.*[0-9])(?=.*[!@#$%^&*])[a-zA-Z0-9!@#$%^&*]{6,16}$", message = "Field does not satisfy regexp")
    @Column(name = "password")
    @CsvBindByPosition(position = 2)
    private String password;

    @NotNull(message = "Field is null validation error")
    @Pattern(regexp = "[a-zA-Z ,.'-]+", message = "Field does not satisfy regexp")
    @Column(name = "name")
    @CsvBindByPosition(position = 3)
    private String name;

    @NotNull(message = "Field is null validation error")
    @Pattern(regexp = "[a-zA-Z ,.'-]+", message = "Field does not satisfy regexp")
    @Column(name = "surname")
    @CsvBindByPosition(position = 4)
    private String surname;

    @NotNull(message = "Field is null validation error")
    @Column(name = "date")
    @CsvBindByPosition(position = 5)
    private LocalDate date;

    @Column(name = "balance")
    @CsvBindByPosition(position = 6)
    private Float currentBalance;

    @Column(name = "mobile")
    @CsvBindByPosition(position = 7)
    private String mobile;

    @Column(name = "street")
    @CsvBindByPosition(position = 8)
    private String street;

    @Column(name = "accommodation_number")
    @CsvBindByPosition(position = 9)
    private String accommodationNumber;

    @Column(name = "flat_number")
    @CsvBindByPosition(position = 10)
    private String flatNumber;

    @OneToMany(mappedBy = "user", orphanRemoval = true, cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    @ToString.Exclude
    @EqualsAndHashCode.Exclude
    private List<Order> orders;
}

导出方法代码

public ModelAndView exportUserOrders(User user) throws CSVExportException {
    try (Writer writer  = new FileWriter(FileSystemView.getFileSystemView().getHomeDirectory().getAbsolutePath() + "/orders.csv")) {
        StatefulBeanToCsv<Order> sbc = new StatefulBeanToCsvBuilder<Order>(writer)
                .withSeparator(CSVWriter.DEFAULT_SEPARATOR)
                .build();
        sbc.write(user.getOrders());
    } catch (IOException | CsvRequiredFieldEmptyException | CsvDataTypeMismatchException e) {
        throw new CSVExportException(EshopConstants.errorOrdersExportMessage, PagesPathEnum.ACCOUNT_PAGE.getPath());
    }
    ModelMap model = new ModelMap();
    model.addAttribute("successfulMessage", EshopConstants.successfulExportMessage);
    return new ModelAndView(PagesPathEnum.ACCOUNT_PAGE.getPath(), model);
}

导出结果对比

实际导出结果

"1","800.0","2023-08-07","User(mail=user1@gmail.com, password=hello1@, name=Ivan, surname=Hello, date=2023-08-01, currentBalance=0.0, mobile=+777777777, street=Rokossovskogo, accommodationNumber=44, flatNumber=777)","11111 **** **** 1111","Delivery by courier","10.0","code","Rokossovskogo","Notes"

期望导出结果

"1","800.0","2023-08-07","User(id = 1, mail=user1@gmail.com, password=hello1@, name=Ivan, surname=Hello, date=2023-08-01, currentBalance=0.0, mobile=+777777777, street=Rokossovskogo, accommodationNumber=44, flatNumber=777)","11111 **** **** 1111","Delivery by courier","10.0","code","Rokossovskogo","Notes"

问题原因与解决方案

原因分析

OpenCSV处理关联对象(如Order中的User字段)时,默认调用对象的toString()方法生成CSV内容。当前User类仅通过@Data注解生成toString,但@Data默认不会将父类(BaseEntity)的字段包含到toString输出中,导致User的id未被打印。

解决方案

给User类添加@ToString(callSuper = true)注解,让Lombok生成的toString方法包含父类BaseEntity的id字段:

@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true) // 添加此注解
@SuperBuilder
@NoArgsConstructor
@Entity
@Table(name = "users")
public class User extends BaseEntity{
    // 原有代码不变
}

添加该注解后,User的toString输出会自动包含父类的id字段,导出CSV时就能得到期望的结果。

内容的提问来源于stack exchange,提问作者IvanDx

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 10:54:55