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 :
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 data repo's :
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();
}
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.
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 instanceof
and you can customize your rendering for each subclass.
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 subclass or when you only want 1 specific implementation in stead of all implementations.