所以我有2个实体 - 发布和标记,并且它们之间存在多对多关系。我还有一个CSV文件,需要预先填充数据库(它包含帖子详细信息,包括匹配的标签实体的ID)。此外,在DDD之后,Post实体是具有PostRepository的Aggregate根。以下是java类: -
@Entity
public class Post{
@Id
@GeneratedValue
private long id;
@ManyToMany(cascadeType = {MERGE, PERSIST})
@JoinTable(name = "post_tag",
joinColumns = @JoinColumn(name = "post_id"),
inverseJoinColumns = @JoinColumn(name = "tag_id"))
private List<Tag> tags = new ArrayList<>();
// getters, setters ommited.
public void addTag(Tag tag){
tags.add(tag);
tag.getPosts().add(this);
}
}
@Entity
public class Tag{
@Id
private long id;
@ManyToMany(mappedBy = "tags")
private List<Post> posts = new ArrayList<>();
// getters, setters ommited.
}
}
下面的存储库界面: -
public interface PostRepo extends Repository<Post, Long>{
void save(Post p);
}
下面的数据库预加载代码: -
@Service
public class Service {
@Inject
private PostRepo repo;
@PostConstruct
void init(){
// parse CSV file and get a Stream<String[]> stream
stream.foreach( splits -> {
long tagId = Long.parseLong(splits[0]);
Post post = new Post();
post.add(new Tag(tagId);
repo.save(post);
}
}
}
使用
预填充数据库时PostRepo ::保存(后)
方法我遇到了主键约束验证错误,我认为在添加Post实体时背后的原因我也在尝试添加一个与预先存在的Tag实体具有相同ID的新Tag。 / p>
所以我的问题是如何在不添加TagRepository并使用TagRepository的saveOrUpdate方法的情况下正确执行此操作?
答案 0 :(得分:0)
尝试这种方法:
@Component
public class DataLoader implements ApplicationRunner {
@Autoware
private PostRepo repo;
@Override
public void run(ApplicationArguments args) throws Exception {
// parse CSV file and get a Stream<String[]> stream
Map<Long, Tag> tagCache = new HashMap<>(); // The cache for persisted tags
/* Outer loop by posts */ {
Post post = new Post();
stream.forEach(splits -> {
Long tagId = Long.parseLong(splits[0]);
// Get already persisted Tag or create new one
Tag tag = tagCache.getOrDefault(tagId, new Tag(tagId));
post.addTag(tag);
});
Post savedPost = repo.save(post);
List<Tag> tags = savedPost.getTags(); // get persisted tags
tags.forEach(tag -> tagCache.put(tag.getId(), tag)); // put it into the cache
}
}
}
如果实体已经持久化(例如某些Tag),我们无法再创建它 - 这将只是另一个实体,所以我们必须从数据库中获取它,然后重用。
所以我们在这里使用一个简单的缓存&#39;存储持久标签。 我认为这应该有用。
稍微info:
如果在SpringApplication启动后需要运行某些特定代码,则可以实现ApplicationRunner或CommandLineRunner接口。两个接口以相同的方式工作,并提供单个run方法,该方法将在SpringApplication.run(...)完成之前调用。