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

chi*_*rld 20 java spring jpa spring-data-jpa

我知道spring数据存储库的实现:

创建一个这样的界面:

public interface CountryRepository extends CrudRepository<Country, Long> {}
Run Code Online (Sandbox Code Playgroud)

现在Country是一个AbstractCatalog,我在矿山项目中有很多(很多)目录.
我想知道我是否可以创建一个适用于所有目录的存储库:

public interface AbstractCatalogRepository extends CrudRepository<AbstractCatalog, Long> {}
Run Code Online (Sandbox Code Playgroud)

现在有了保存,我没有直接看到一个问题,但如果我想搜索一个AbstractCatalog我已经确定我会碰壁,因为回购将不知道他必须选择什么对象.

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
}
Run Code Online (Sandbox Code Playgroud)

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
}
Run Code Online (Sandbox Code Playgroud)

有没有人知道如何只AbstractCatalog为名称和实现类的最小变化一次又一次地创建相同的接口,为所有实现做出1回购?

Pau*_*tha 14

如果您没有在数据库端使用表继承(例如,具有descriminator列的超类表),AFAIK,并且基于阅读JPA教程,则无法完成此操作(即只使用@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);
    }   
}
Run Code Online (Sandbox Code Playgroud)

基本上,总而言之,如果您没有表继承,那么您尝试执行的操作将无法正常工作.存储库的类类型需要是实体.如果您的表没有以这种方式设置继承,那么它只取决于您是否要更改表.但是,避免多个存储库可能有点多.

我使用的一些参考文献在这里和这里

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


chi*_*rld 5

好的,新项目,我将对此进行一些设置。
问题是:我们想添加附件,但是附件可以上传文件,链接或邮件。

Pojo课程:

Attachment.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;
    }
}
Run Code Online (Sandbox Code Playgroud)

FileAttachment.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;
    }
}
Run Code Online (Sandbox Code Playgroud)

MailAttachment.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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

Spring数据仓库:

AttachmentRepository.java:

public interface AttachmentRepository extends CustomRepository<Attachment, Long> {    
    List<Attachment> findByTask(Task task);
}
Run Code Online (Sandbox Code Playgroud)

CustomRepository.java:

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

最后的服务:

@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);
    }
}
Run Code Online (Sandbox Code Playgroud)

结果是:

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

如果我打电话给findByTask(Task task)我,我得到了List<Attachment>所有子类的一个,并且它们的后面都有正确的子类。
这意味着,您可以让一个渲染器来做,instanceof并且可以为每个子类自定义渲染。

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