Hibernate多对多关联场景下MOVIES与BOOKS实体继承抽取方案
实体层公共代码抽取方案
使用JPA提供的@MappedSuperclass注解定义抽象父类,该注解不会生成额外表结构,完全兼容现有只读库规则,父类仅存放公共属性和方法:
import javax.persistence.Column; import javax.persistence.Id; import javax.persistence.MappedSuperclass; import java.util.List; @MappedSuperclass public abstract class BaseMedia { @Id @Column(name = "ID") private Long id; public Long getId() { return id; } public void setId(Long id) { this.id = id; } // 定义抽象方法,统一获取关联评审文档的入口 public abstract List<ReviewDocuments> getReviewDocuments(); }
改造原有实体类继承抽象父类,仅保留自身特有属性和多对多关联配置:
Movies子类改造
@Entity(name = "MOVIES") @Table(name = "MOVIES") public class Movies extends BaseMedia { @Column(name = "MOVIE_KEY") private String movieKey; @Column(name = "TYPE_ANIMATED") private String typeAnim; @ManyToMany(fetch = FetchType.LAZY) @JoinTable(name = "MOVIES_REVIEWDOCUMENTS", joinColumns = @JoinColumn(name = "MOVIE_KEY"), inverseJoinColumns = @JoinColumn(name = "DOCUMENT_KEY")) private List<ReviewDocuments> reviewDocuments; @Override public List<ReviewDocuments> getReviewDocuments() { return this.reviewDocuments; } // 自有属性的getter/setter省略 }
Books子类改造
@Entity(name = "BOOKS") @Table(name = "BOOKS") public class Books extends BaseMedia { @Column(name = "BOOK_KEY") private String bookKey; @Column(name = "TYPE_WRITTEN") private String typeWritten; @ManyToMany(fetch = FetchType.LAZY) @JoinTable(name = "BOOKS_REVIEWDOCUMENTS", joinColumns = @JoinColumn(name = "BOOK_KEY"), inverseJoinColumns = @JoinColumn(name = "DOCUMENT_KEY")) private List<ReviewDocuments> reviewDocuments; @Override public List<ReviewDocuments> getReviewDocuments() { return this.reviewDocuments; } // 自有属性的getter/setter省略 }
泛型Service层复用方案
定义泛型抽象Service,统一实现公共导出逻辑,差异化逻辑由子类实现:
import org.apache.commons.io.FileUtils; import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.Set; public abstract class BaseMediaService<T extends BaseMedia> { // 子类实现对应实体的查询逻辑 protected abstract Set<T> findMediaByParam(String param); // 子类实现不同实体的差异化导出逻辑 protected abstract void exportDataToFile(ReviewDocuments document); // 公共抽取逻辑完全复用 public void extractData(String extractionParam) throws IOException { Path tempDirectory = Files.createTempDirectory("zip_"); tempDirectory.toFile().deleteOnExit(); Set<T> mediaList = findMediaByParam(extractionParam); for (T media : mediaList) { for (ReviewDocuments document : media.getReviewDocuments()) { exportDataToFile(document); } directoryToZip(tempDirectory.toFile(), media.getId()); FileUtils.cleanDirectory(tempDirectory.toFile()); } FileUtils.deleteDirectory(tempDirectory.toFile()); } // 通用打包逻辑直接实现复用 private void directoryToZip(File directory, Long id) { // 原有打包逻辑直接迁移到此处 } }
子类Service实现示例
// 书籍业务Service @Service public class BooksService extends BaseMediaService<Books> { @Resource private BookRepository bookRepository; @Override protected Set<Books> findMediaByParam(String param) { return bookRepository.findByRidKey(param); } @Override protected void exportDataToFile(ReviewDocuments document) { // 实现书籍类的导出逻辑 } } // 电影业务Service @Service public class MoviesService extends BaseMediaService<Movies> { @Resource private MovieRepository movieRepository; @Override protected Set<Movies> findMediaByParam(String param) { return movieRepository.findByRidKey(param); } @Override protected void exportDataToFile(ReviewDocuments document) { // 实现电影类的导出逻辑 } }
注意事项
- 现有
ReviewDocuments实体的双向关联配置不需要修改,完全兼容抽取后的实体逻辑 - 懒加载关联需要注意会话有效期,如果导出逻辑在事务外执行,需要在查询时提前fetch关联的
reviewDocuments避免懒加载异常 - 后续新增同类型实体仅需要继承
BaseMedia和BaseMediaService即可,不需要重复编写公共逻辑
内容的提问来源于stack exchange,提问作者Suule
相关产品推荐
相关产品推荐

