使用抽象超类作为 Spring 数据存储库的参数

2022-09-01 15:58:12

我知道如何实现弹簧数据存储库,

创建一个这样的界面:

public interface CountryRepository extends CrudRepository<Country, Long> {}

现在是一个,我的项目中(很多)有更多的目录。
我想知道我是否只能制作一个适用于所有目录的存储库:CountryAbstractCatalog

public interface AbstractCatalogRepository extends CrudRepository<AbstractCatalog, Long> {}

现在我在保存时没有看到问题,但是如果我想搜索一个,我已经确定我会碰壁,因为存储库将不知道它必须选择哪个子类。AbstractCatalog

摘要目录.class

@MappedSuperclass
public abstract class AbstractCatalog extends PersistentEntity {

    /**
     * The Constant serialVersionUID.
     */
    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;
    /**
     * The code.
     */
    @Column(unique = true, nullable = false, updatable = false)
    private String code;
    /**
     * The description.
     */
    @Column(nullable = false)
    private String description;
    /**
     * The in use.
     */
    @Column(name = "IN_USE", nullable = false, columnDefinition = "bit default 1")
    private Boolean inUse = Boolean.TRUE;

    // getters and setters
}

国家.class

@Entity
@Table(name = "tc_country")
@AttributeOverrides({
    @AttributeOverride(name = "id", column =
            @Column(name = "COUNTRY_SID")),
    @AttributeOverride(name = "code", column =
            @Column(name = "COUNTRY_CODE")),
    @AttributeOverride(name = "description", column =
            @Column(name = "COUNTRY_DESCRIPTION"))})
public class Country extends AbstractCatalog {

    public static final int MAX_CODE_LENGTH = 11;

    @Column(name = "GEONAMEID", nullable = true, unique = false)
    private Long geonameid;

    // getter and setter
}

有没有人想过,我怎么能只对类的所有实现使用一个存储库,而不必一遍又一遍地创建相同的接口,而名称和其他属性的差异最小?AbstractCatalog


答案 1

如果您没有在数据库端使用表继承(例如,带有判别器列的超类表),AFAIK,并且基于阅读JPA教程,则无法完成此操作(即简单地对抽象类使用注释)@MappedSuperclass

映射的超类无法查询,也无法在 EntityManager 或查询操作中使用。必须在实体管理器或查询操作中使用映射超类的实体子类。映射的超类不能成为实体关系的目标

请注意,JPA 存储库抽象在后台使用 EntityManager。我做了一个简单的测试,你会得到什么(在Hibernate实现的情况下)一个”IllegalArgumentException : not an entity AbstractClass"

另一方面,如果确实使用表继承,则可以使用抽象类型。我知道你说“只有最小的变化”(我想我的简短答案是我认为这是不可能的 - 可能是因为你猜到的原因),所以我想这个答案的其余部分是针对其他好奇的头脑;-)

表继承策略的一个例子是这样的(免责声明:这不是erd继承的正确可视化,但MySQL Workbench不支持它,但是我下面的内容将模型向前设计为MYSQL所需的方式)

enter image description here

其中具有对表 pk (id) 的 FK/PK 引用。该表具有 一个,该值将用于确定超类型具体值与哪个子类型相关。CountryCatalogAbstractCatalogAbstractCatalogdescriminatorColumn

就你如何编码而言,它看起来像这样

@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn(name="descriminatorColumn")
@Table(name="AbstractCatalog")
public abstract class AbstractCatalog {
    @Id
    private long id;
    ...
}

@Entity
@Table(name = "CountryCatalog")
public class CountryCatalog extends AbstractCatalog {
    // id is inherited
    ...
}

public interface AbstractCatalogRepository 
                 extends JpaRepository<AbstractCatalog, Long> {

}

@Repository
public class CountryCatalogServiceImpl implements CountryCatalogService {

    @Autowired
    private AbstractCatalogRepository catalogRepository;

    @Override
    public List<CountryCatalog> findAll() {
        return (List<CountryCatalog>)(List<?>)catalogRepository.findAll();
    }

    @Override
    public CountryCatalog findOne(long id) {
        return (CountryCatalog)catalogRepository.findOne(id);
    }   
}

基本上,总而言之,如果您没有表继承,那么您尝试执行的操作将不起作用。存储库的类类型必须是实体。如果您的表不是以这种方式进行继承设置的,则只需考虑是否要更改表。不过,为了避免多个存储库,这可能有点多。

我使用的一些参考资料在这里这里

注意:这个答案中的所有内容都针对Hibernate提供程序进行了测试


答案 2

Oke,新项目,我正在遵循这个设置。
问题是:我们想要添加附件,但附件可以上传文件,链接或邮件。

波霍类 :

附件.java :

@Entity
@Table(name = "T_ATTACHMENT")
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn(name = "DISCRIMINATOR", discriminatorType = DiscriminatorType.STRING)
public abstract class Attachment {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "ATTACHMENT_SID")
    private Long id;

    @ManyToOne
    @JoinColumn(name = "TASK_SID", referencedColumnName = "TASK_SID", nullable = false, unique = false, insertable = true, updatable = true)
    private Task task;

    @ManyToOne
    @JoinColumn(name = "USER_SID", referencedColumnName = "USER_SID", nullable = false, unique = false, insertable = true, updatable = true)
    private User user;

    public Task getTask() {
        return task;
    }

    public void setTask(Task task) {
        this.task = task;
    }

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }
}

文件附件.java :

@Entity
@Table(name = "T_FILE_ATTACHMENT")
@DiscriminatorValue("FILE")
public class FileAttachment extends Attachment {

    @Column(name = "NAME", nullable = false, unique = false)
    private String fileName;

    @Lob
    @Basic
    @Column(name = "FILE", nullable = false, unique = false)
    private byte[] file;

    public String getFileName() {
        return fileName;
    }

    public void setFileName(String fileName) {
        this.fileName = fileName;
    }

    public byte[] getFile() {
        return file;
    }

    public void setFile(byte[] file) {
        this.file = file;
    }
}

邮件附件.java :

@Entity
@Table(name = "T_MAIL_ATTACHMENT")
@DiscriminatorValue("MAIL")
public class MailAttachment extends Attachment {

    @Column(name = "RECIPIENT", nullable = false, unique = false)
    private String to;
    @Column(name = "CC", nullable = true, unique = false)
    private String cc;
    @Column(name = "BCC", nullable = true, unique = false)
    private String bcc;
    @Column(name = "TITLE", nullable = true, unique = false)
    private String title;
    @Column(name = "MESSAGE", nullable = true, unique = false)
    private String message;

    public String getTo() {
        return to;
    }

    public void setTo(String to) {
        this.to = to;
    }

    public String getCc() {
        return cc;
    }

    public void setCc(String cc) {
        this.cc = cc;
    }

    public String getBcc() {
        return bcc;
    }

    public void setBcc(String bcc) {
        this.bcc = bcc;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}

链接附件.java :

@Entity
@Table(name = "T_LINK_ATTACHMENT")
@DiscriminatorValue("LINK")
public class LinkAttachment extends Attachment {

    @Column(name = "DESCRIPTION", nullable = true, unique = false)
    private String description;

    @Column(name = "LINK", nullable = false, unique = false)
    private String link;

    public String getDescription() {
        return description == null ? getLink() : description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public String getLink() {
        return link;
    }

    public void setLink(String link) {
        this.link = link;
    }
}

春季数据回购:

附件存储库.java:

public interface AttachmentRepository extends CustomRepository<Attachment, Long> {    
    List<Attachment> findByTask(Task task);
}

自定义存储库.java :

public interface CustomRepository<E, PK extends Serializable> extends
                PagingAndSortingRepository<E, PK>,
                JpaSpecificationExecutor<E>, 
                QueryDslPredicateExecutor<E> {
    @Override
    List<E> findAll();
}

最后,服务:

@Service
public class AttachmentServiceImpl implements AttachmentService {

    @Inject
    private AttachmentRepository attachmentRepository;

    @Override
    public List<Attachment> findByTask(Task task) {
        return attachmentRepository.findByTask(task);
    }

    @Override
    @Transactional
    public Attachment save(Attachment attachment) {
        return attachmentRepository.save(attachment);
    }
}

这导致:

我可以使用我创建的任何实现保存到抽象存储库,JPA将正确执行。

如果我调用,我会得到所有子类的一个,并且它们在后面有正确的子类。
这意味着,您可以制作一个渲染器来执行,并且可以为每个子类自定义渲染。findByTask(Task task)List<Attachment>instanceof

缺点是,您仍然需要创建自定义的特定存储库,但仅当您想要查询子类中的特定属性时,或者当您只需要1个特定实现而不是所有实现时。


推荐