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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/25237664/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-10 23:57:55  来源:igfitidea点击:

Use abstract super class as parameter to Spring data repository

javaspringjpaspring-data-jpa

提问by chillworld

I know the implementation of spring data repository's :

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

Create an interface like this :

创建一个这样的界面:

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

Now Countryis an AbstractCatalogand I have (a lot) more catalogs in mine project.
I'm wondering if I can make 1 repository that should work for all the catalogs :

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

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

Now with save I don't see directly a problem but if I want to search an AbstractCatalogI'm already sure I'll hit the wall cause the repo will not know from what object he must choose.

现在使用 save 我没有直接看到问题,但是如果我想搜索一个AbstractCatalog我已经确定我会撞墙,因为 repo 将不知道他必须选择什么对象。

AbstractCatalog.class

抽象目录.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

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
}

Has anyone an idea how I could just make 1 Repo for all the implementations of AbstractCatalogwithout to create the same interface over and over again with just the minimal change of name and implementation class?

有没有人知道我如何为所有实现制作 1 个 Repo,AbstractCatalog而无需一遍又一遍地创建相同的接口,而只需对名称和实现类进行最小的更改?

采纳答案by Paul Samsotha

If you aren't using table inheritance on the database side (e.g. super class table with descriminator column), AFAIK, and based off reading the JPA tutorial, this can't be done (i.e. simply using @MappedSuperclassannotation for your abstract class)

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

Mapped superclasses cannot be queried and cannot be used in EntityManager or Query operations. You must use entity subclasses of the mapped superclass in EntityManager or Query operations. Mapped superclasses can't be targets of entity relationships

映射的超类不能被查询,也不能在 EntityManager 或 Query 操作中使用。您必须在 EntityManager 或 Query 操作中使用映射超类的实体子类。映射的超类不能是实体关系的目标

Note, the JPA repository abstraction uses an EntityManager under the hood. I did a simple test, and what you will get (in the case of Hibernate implementation) an "IllegalArgumentException : not an entity AbstractClass"

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

On the other hand, if you douse table inheritance, then you canuse the abstract type. I know you said "with just the minimal change"(and I guess my short answer is I don't think it's possible - probably for the reasons you guessed), so I guess the rest of this answer is for other inquiring minds ;-)

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

An example of a table inheritance strategy would be something like this (disclaimer: this is not the correct visualizationfor erd inheritance, but MySQL Workbench doesn't support it, but what I have below forward engineered the model to MYSQL the way it needs to be)

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

enter image description here

在此处输入图片说明

Where CountryCataloghas a FK/PK reference to the AbstractCatalogtable pk (id). The AbstractCatalogtable has a descriminatorColumnthat will be used to determine to which subtype the supertype occurrence is related.

哪里CountryCatalog有对AbstractCatalog表 pk (id)的 FK/PK 引用。该AbstractCatalog表具有descriminatorColumn将用于确定超类型出现与哪个子类型相关的 。

In terms of how you would code that, it would look something like

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

@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);
    }   
}


Basically, in conclusion, what you are trying to do won't work if you don't have table inheritance. The class type for the repository needs to be an entity. If your tables aren't set up this way for inheritance, it just comes down to whether or not you want to change the tables. It may be a bit much just to avoid multiple repositories though.

基本上,总而言之,如果您没有表继承,您尝试做的将不起作用。存储库的类类型需要是一个实体。如果您的表没有以这种方式设置用于继承,则归结为是否要更改表。只是为了避免多个存储库可能有点多。

Some references I used are hereand here

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

Note:Everything in this answer is tested against Hibernate provider

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

回答by mavarazy

What DB are you using?

你用的是什么数据库?

If it's JPA, take a look at Can I use a generic Repository for all children of a MappedSuperClass with Spring Data JPA?

如果是 JPA,请查看 我可以使用 Spring Data JPA 的 MappedSuperClass 的所有子项使用通用存储库吗?

If it's Mongo you need to properly tune Hymanson polymorphism configuration http://wiki.fasterxml.com/HymansonPolymorphicDeserialization

如果是 Mongo,您需要正确调整 Hymanson 多态配置 http://wiki.fasterxml.com/HymansonPolymorphicDeserialization

So this is possible.

所以这是可能的。

回答by chillworld

Oke, new project and I'm following this set up a little bit.
The problem was : We want to add attachments, but an attachment can be uploading a file, a link or a mail.

好的,新项目,我正在关注这个设置。
问题是:我们想添加附件,但附件可以是上传文件、链接或邮件。

Pojo classes :

Pojo 类:

Attachment.java :

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

FileAttachment.java :

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

MailAttachment.java :

邮件附件.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 :

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 data repo's :

Spring 数据仓库的:

AttachmentRepository.java:

AttachmentRepository.java:

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

CustomRepository.java :

CustomRepository.java :

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

And at last the service :

最后是服务:

@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);
    }
}

This results in :

这导致:

I can save to the abstract repo with any implementation I created, JPA will do it correct.

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

If I call findByTask(Task task)I get a List<Attachment>of all the subclasses, and they have the correct subclass in the back.
This means, you can make a renderer who do instanceofand you can customize your rendering for each subclass.

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

Downside is, you still need to create custom specific repository's, but only when you want to query on a specific property what is in the subclassor when you only want 1 specific implementation in stead of all implementations.

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