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

时间:2014-08-11 07:13:45

标签: java spring jpa spring-data-jpa

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

创建一个这样的界面:

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

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

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

现在有了保存,我没有直接看到问题,但如果我想搜索一个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
}

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的所有实现做一次回购而不用一次又一次地创建相同的接口只需要对名称和实现类进行最小的更改?

3 个答案:

答案 0 :(得分:13)

如果您没有在数据库端使用表继承(例如,带有descriminator column的超类表),AFAIK,并且基于读取JPA tutorial,则无法完成(即使用@MappedSuperclass注释为您的抽象类)

  

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

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

另一方面,如果你使用表继承,那么你可以使用抽象类型。我知道你说“只有最小的改变”(我想我的简短回答是我认为这不可能 - 可能是因为你猜对了的原因),所以我想这个答案的其余部分是为了其他探究的思想;-)

表继承策略的一个例子是这样的(免责声明:这不是correct visualization的erd继承,但是MySQL Workbench不支持它,但我在下面将模型设计为MYSQL需要它的方式)

enter image description here

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

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

我使用的一些参考文献是herehere

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

答案 1 :(得分:5)

Oke,新项目,我跟着这个设置了一点点 问题是: 我们想要添加附件,但附件可以上传文件,链接或邮件。

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

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

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

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个特定实现时而不是所有的实施。

答案 2 :(得分:1)

你在使用什么数据库?

如果是JPA,请看一下 Can I use a generic Repository for all children of a MappedSuperClass with Spring Data JPA?

如果是Mongo,你需要正确调整Jackson多态性配置 http://wiki.fasterxml.com/JacksonPolymorphicDeserialization

所以这是可能的。