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
相关产品推荐
相关产品推荐

