Spring+MySQL从文本加载数据时建立艺术家与艺术品的实体关系
解决方案:修正实体关联+分阶段加载关联数据
看起来你已经完成了基础的解析和持久化工作,核心问题出在实体关联映射的定义和数据加载的顺序/关联逻辑上,我给你梳理一套可行的实现方案:
一、先修正JPA实体的一对多关联映射
你的Artwork类当前用String存储artist,这只是存储了艺术家的标识,没法建立JPA的对象级关联。需要调整实体类,定义正确的双向一对多关系:
1. Artist实体修改
添加@OneToMany关联到Artwork,并维护双向关联的方法:
@Entity @Table(name = "artists") public class Artist { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(nullable = false, unique = true) // 确保艺术家名字唯一,避免重复插入 private String name; private LocalDate dob; private String sex; private String country; // 双向关联:一个艺术家对应多个艺术品,mappedBy指定由Artwork的artist字段维护外键 @OneToMany(mappedBy = "artist", cascade = CascadeType.ALL, orphanRemoval = true) private List<Artwork> artworks = new ArrayList<>(); // 构造器(无参+全参)、getter/setter省略 // 维护双向关联的辅助方法 public void addArtwork(Artwork artwork) { this.artworks.add(artwork); if (artwork.getArtist() != this) { artwork.setArtist(this); } } }
2. Artwork实体修改
将String类型的artist替换为@ManyToOne关联到Artist实体:
@Entity @Table(name = "artworks") public class Artwork { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; // 多对一关联:一个艺术品属于一个艺术家 @ManyToOne(fetch = FetchType.LAZY) // 懒加载优化性能 @JoinColumn(name = "artist_id", nullable = false) // 指定外键列名 private Artist artist; private String title; private Integer year; private String country; // 构造器、getter/setter省略 // 维护双向关联的setter方法 public void setArtist(Artist artist) { this.artist = artist; if (!artist.getArtworks().contains(this)) { artist.addArtwork(this); } } }
二、调整DataLoader的加载逻辑(分两个阶段)
核心思路是先加载所有艺术家并缓存到内存,再加载艺术品时直接关联缓存中的艺术家对象,避免重复查询数据库,同时保证关联正确:
代码示例(假设使用Spring Data JPA)
@Component public class DataLoader { private final ArtistRepository artistRepository; private final ArtworkRepository artworkRepository; private static final String FILE_PATH = "path/to/your/data.txt"; // 构造注入Repository public DataLoader(ArtistRepository artistRepository, ArtworkRepository artworkRepository) { this.artistRepository = artistRepository; this.artworkRepository = artworkRepository; } @PostConstruct // Spring启动时自动执行数据加载 public void loadData() { // 阶段1:加载所有艺术家,存入内存Map(键为艺术家名字,值为持久化后的Artist对象) Map<String, Artist> artistMap = new HashMap<>(); try (Stream<String> lines = Files.lines(Paths.get(FILE_PATH))) { // 先处理所有艺术家条目 lines.filter(line -> line.startsWith("A|")) .forEach(line -> { String[] parts = line.split("\\|"); // 注意|是正则特殊字符,需要转义 // 处理分割后的空格(比如" A| Van Gogh| ...") String name = parts[1].trim(); LocalDate dob = LocalDate.parse(parts[2].trim()); String sex = parts[3].trim(); String country = parts[4].trim(); // 先查询是否已存在该艺术家(避免重复插入) Artist artist = artistRepository.findByName(name) .orElseGet(() -> { Artist newArtist = new Artist(); newArtist.setName(name); newArtist.setDob(dob); newArtist.setSex(sex); newArtist.setCountry(country); return artistRepository.save(newArtist); }); artistMap.put(name, artist); }); // 阶段2:重新读取文件处理艺术品条目 Files.lines(Paths.get(FILE_PATH)) .filter(line -> line.startsWith("W|")) .forEach(line -> { String[] parts = line.split("\\|"); String artistName = parts[1].trim(); String title = parts[2].trim(); Integer year = Integer.parseInt(parts[3].trim()); String country = parts[4].trim(); // 从缓存Map获取艺术家 Artist artist = artistMap.get(artistName); if (artist != null) { Artwork artwork = new Artwork(); artwork.setArtist(artist); artwork.setTitle(title); artwork.setYear(year); artwork.setCountry(country); artworkRepository.save(artwork); } else { // 处理异常情况:记录日志或跳过 System.err.printf("Warning: Artist '%s' not found, skipping artwork '%s'%n", artistName, title); } }); } catch (IOException | DateTimeParseException | NumberFormatException e) { e.printStackTrace(); throw new RuntimeException("Failed to load data from file", e); } } }
三、关键注意事项
- 数据唯一性:通过
@Column(unique = true)确保艺术家名字唯一,加载时先查询再创建,处理文件中重复的艺术家条目。 - 双向关联维护:通过
addArtwork和setArtist方法保证双向关联的一致性,避免出现“艺术家有艺术品列表,但艺术品的artist为null”的情况。 - 性能优化:使用内存Map缓存艺术家,避免每次加载艺术品都查询数据库;对于大数据量,建议使用批量保存(比如
artworkRepository.saveAll(List<Artwork>)),并配置JPA的批量插入属性(比如spring.jpa.properties.hibernate.jdbc.batch_size=50)。 - 异常处理:添加了对文件IO、日期解析、数字转换的异常捕获,避免程序崩溃,同时记录错误信息便于排查。
内容的提问来源于stack exchange,提问作者roboticcrow
相关产品推荐
相关产品推荐

