簡體   English   中英

使用抽象超類作為 Spring 數據存儲庫的參數

[英]Use abstract super class as parameter for Spring data repository

我知道如何實現 spring 數據存儲庫,

創建一個這樣的界面:

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

現在Country是一個AbstractCatalog ,我的項目中有(很多)更多的目錄。
我想知道我是否只能制作一個適用於所有目錄的存儲庫:

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
}

Country.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類的所有實現,而不必一遍又一遍地創建相同的接口,而名稱和其他屬性的差異最小?

如果您不在數據庫端使用表繼承(例如,帶有標識符列的超類表),AFAIK,並且基於閱讀JPA 教程,則無法完成此操作(即@MappedSuperclass抽象類使用@MappedSuperclass注釋)

映射的超類不能被查詢,也不能在 EntityManager 或 Query 操作中使用。 您必須在 EntityManager 或 Query 操作中使用映射超類的實體子類。 映射的超類不能是實體關系的目標

請注意,JPA 存儲庫抽象在幕后使用 EntityManager。 我做了一個簡單的測試,你會得到什么(在 Hibernate 實現的情況下)“ IllegalArgumentException : not an entity AbstractClass

另一方面,如果確實使用表繼承,則可以使用抽象類型。 我知道你說的是“只做最小的改變” (我想我的簡短回答是我認為這是不可能的——可能是因為你猜到的原因),所以我猜這個答案的其余部分是為其他好奇的人准備的;- )

表繼承策略的一個例子是這樣的(免責聲明:這不是 erd 繼承的正確可視化,但 MySQL Workbench 不支持它,但我在下面將模型前向工程到 MYSQL 需要的方式是)

在此處輸入圖片說明

其中CountryCatalog具有對AbstractCatalog表 pk (id) 的 FK/PK 引用。 AbstractCatalog表有一個descriminatorColumn ,用於確定超類型出現與哪個子類型相關。

就你如何編碼而言,它看起來像

@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 提供程序進行了測試

好的,新項目,我正在關注這個設置。
問題是:我們想添加附件,但附件可以是上傳文件、鏈接或郵件。

Pojo 類:

附件.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;
    }
}

LinkAttachment.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;
    }
}

Spring 數據倉庫的:

AttachmentRepository.java:

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

CustomRepository.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 個特定實現而不是所有實現時。

你用的是什么數據庫?

如果是 JPA,請查看我可以使用 Spring Data JPA 的 MappedSuperClass 的所有子項使用通用存儲庫嗎?

如果是 Mongo,您需要正確調整 Jackson 多態配置http://wiki.fasterxml.com/JacksonPolymorphicDeserialization

所以這是可能的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM