Использовать абстрактный суперкласс в качестве параметра для хранилища данных Spring
Я знаю реализацию хранилища данных spring:
Создайте такой интерфейс:
public interface CountryRepository extends CrudRepository<Country, Long> {}
Теперь Country
является AbstractCatalog
, и у меня (много) больше каталогов в моем проекте.
Мне интересно, могу ли я сделать 1 репозиторий, который должен работать для всех каталогов:
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
}
Кто-нибудь знает, как я могу просто сделать 1 Repo для всех реализаций AbstractCatalog
, не создавая один и тот же интерфейс снова и снова с минимальным изменением имени и класса реализации?
Ответы
Ответ 1
Если вы не используете наследование таблицы на стороне базы данных (например, таблицу суперкласса с столбец разборки), AFAIK, и на основе чтения учебник JPA, это невозможно сделать (т.е. просто использовать аннотацию @MappedSuperclass
для вашего абстрактного класса)
Соответствующие суперклассы не могут быть запрошены и не могут использоваться в EntityManager или Query. Вы должны использовать сущности-подклассы сопоставленного суперкласса в EntityManager или Query-операциях. Связанные суперклассы не могут быть объектами отношений сущностей
Примечание. Абстракция репозитория JPA использует EntityManager под капотом. Я сделал простой тест и что вы получите (в случае реализации Hibernate) "IllegalArgumentException : not an entity AbstractClass
"
С другой стороны, если вы используете наследование таблицы, вы можете использовать абстрактный тип. Я знаю, что вы сказали "с минимальными изменениями" (и, по-моему, мой короткий ответ - я не думаю, что это возможно - возможно, по причинам, которые вы догадались), поэтому я думаю, что остальная часть этого ответа для других вопрошающих умов; )
Примером стратегии наследования таблицы может быть что-то вроде этого (отказ от ответственности: это не правильная визуализация для наследования erd, но MySQL Workbench не поддерживает его, но то, что у меня ниже, спроектировало модель для MYSQL так, как она должна быть)
![enter image description here]()
Где CountryCatalog
имеет ссылку FK/PK на таблицу AbstractCatalog
pk (id). Таблица 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
Ответ 2
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 конкретная реализация вместо всех реализации.
Ответ 3
Какая БД вы используете?
Если это JPA, взгляните на
Можно ли использовать общий репозиторий для всех дочерних элементов MappedSuperClass с помощью Spring данных JPA?
Если это Mongo, вам нужно правильно настроить конфигурацию полиморфизма Джексона
http://wiki.fasterxml.com/JacksonPolymorphicDeserialization
Так что это возможно.