Je connais l'implémentation du référentiel de données du printemps:
Créez une interface comme celle-ci:
public interface CountryRepository extends CrudRepository<Country, Long> {}
Maintenant, Country
est un AbstractCatalog
et j'ai (beaucoup) plus de catalogues dans mon projet.
Je me demande si je peux créer un référentiel qui devrait fonctionner pour tous les catalogues:
public interface AbstractCatalogRepository extends CrudRepository<AbstractCatalog, Long> {}
Maintenant, avec save, je ne vois pas directement de problème, mais si je veux rechercher un AbstractCatalog
, je suis déjà sûr que je vais toucher le mur car le repo ne saura pas de quel objet il doit choisir.
@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
}
@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
}
Quelqu'un at-il une idée de comment je pourrais simplement faire 1 Repo pour toutes les implémentations de AbstractCatalog
sans créer la même interface encore et encore avec juste le changement minimal de nom et de classe d'implémentation?
Si vous n'utilisez pas l'héritage de table du côté de la base de données (par exemple, table de super classe avec colonne descriminateur ), AFAIK, et basé sur la lecture de tutoriel JPA , cela ne peut pas être fait (c'est-à-dire en utilisant simplement @MappedSuperclass
annotation pour votre classe abstraite)
Les superclasses mappées ne peuvent pas être interrogées et ne peuvent pas être utilisées dans les opérations EntityManager ou Query. Vous devez utiliser les sous-classes d'entités de la superclasse mappée dans les opérations EntityManager ou Query. Les superclasses mappées ne peuvent pas être des cibles de relations d'entité
Remarque, l'abstraction du référentiel JPA utilise un EntityManager sous le capot. J'ai fait un test simple, et ce que vous obtiendrez (dans le cas de l'implémentation d'Hibernate) un "IllegalArgumentException : not an entity AbstractClass
"
D'un autre côté, si vous faites utilisez l'héritage de table, alors vous pouvez utiliser le type abstrait. Je sais que vous avez dit "avec juste le changement minimal" (et je suppose que ma réponse courte est que je ne pense pas que ce soit possible - probablement pour les raisons que vous avez devinées), donc je suppose que le reste de cette réponse est pour les autres esprits curieux ;-)
Un exemple de stratégie d'héritage de table serait quelque chose comme ceci (avertissement: ce n'est pas le visualisation correcte pour l'héritage erd, mais MySQL Workbench ne le prend pas en charge, mais ce que j'ai ci-dessous avant a conçu le modèle à MYSQL comme il faut)
Où CountryCatalog
a une référence FK/PK à la table AbstractCatalog
pk (id). La table AbstractCatalog
a un descriminatorColumn
qui sera utilisé pour déterminer à quel sous-type l'occurrence de supertype est liée.
En termes de codage, cela ressemblerait à quelque chose comme
@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);
}
}
Fondamentalement, en conclusion, ce que vous essayez de faire ne fonctionnera pas si vous n'avez pas d'héritage de table. Le type de classe du référentiel doit être une entité. Si vos tables ne sont pas configurées de cette façon pour l'héritage, il s'agit simplement de savoir si vous souhaitez ou non modifier les tables. Cependant, cela peut être un peu trop pour éviter plusieurs référentiels.
Certaines références que j'ai utilisées sont ici et ici
Remarque: Tout dans cette réponse est testé par rapport au fournisseur Hibernate
Oke, nouveau projet et je suis un peu en train de suivre cette configuration.
Le problème était: Nous voulons ajouter des pièces jointes, mais une pièce jointe peut être en train de télécharger un fichier, un lien ou un courrier.
Cours de pojo:
@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;
}
}
@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;
}
}
@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;
}
}
@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;
}
}
Repo de données de printemps:
public interface AttachmentRepository extends CustomRepository<Attachment, Long> {
List<Attachment> findByTask(Task task);
}
public interface CustomRepository<E, PK extends Serializable> extends
PagingAndSortingRepository<E, PK>,
JpaSpecificationExecutor<E>,
QueryDslPredicateExecutor<E> {
@Override
List<E> findAll();
}
Et enfin le 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);
}
}
Il en résulte:
Je peux enregistrer dans le référentiel abstrait avec n'importe quelle implémentation que j'ai créée, JPA le fera correctement.
Si j'appelle findByTask(Task task)
j'obtiens un List<Attachment>
De toutes les sous-classes, et elles ont la bonne sous-classe à l'arrière.
Cela signifie que vous pouvez créer un moteur de rendu qui fait instanceof
et vous pouvez personnaliser votre rendu pour chaque sous-classe.
L'inconvénient est que vous devez toujours créer des référentiels spécifiques personnalisés, mais uniquement lorsque vous souhaitez interroger sur une propriété spécifique ce qui se trouve dans la sous-classe ou lorsque vous ne souhaitez qu'une seule implémentation spécifique au lieu de toutes les implémentations.
Quelle base de données utilisez-vous?
Si c'est JPA, jetez un œil à Puis-je utiliser un référentiel générique pour tous les enfants d'une MappedSuperClass avec Spring Data JPA?
Si c'est Mongo, vous devez régler correctement la configuration du polymorphisme de Jackson http://wiki.fasterxml.com/JacksonPolymorphicDeserialization
C'est donc possible.