Dear all,
I have a problem with schema based multitenancy:
I am developing a multitenancy enabled application. The multitenancy
is schema based. Frameworks used so far are eclipselink 2.7.5,
Spring data jpa 2.2.0.RELEASE and PostgreSQL. Creating, finding and
deleting single entities works fine so far. A simple "findAll" works
as well. However I have a problem with custom repository methods.
One entity is defined like this:
@Entity
@Table(name = "HISTORY")
@Multitenant(MultitenantType.TABLE_PER_TENANT)
@TenantTableDiscriminator(type = TenantTableDiscriminatorType.SCHEMA, contextProperty = PersistenceUnitProperties.MULTITENANT_PROPERTY_DEFAULT)
@IdClass(SimpleLongId.class)
public class History implements Serializable {
@Id
@TableGenerator(name = "TABLE_GEN_HIST",
table = "SEQUENCE_TABLE",
pkColumnName = "SEQ_NAME",
valueColumnName = "SEQ_COUNT",
pkColumnValue = "HIST_SEQ")
@GeneratedValue(strategy = GenerationType.AUTO)
Long id;
@Column(nullable = false)
Long fkParentId;
@Temporal(TemporalType.TIMESTAMP)
@Column(nullable = true)
private Date lastrun;
@Temporal(TemporalType.TIMESTAMP)
@Column(nullable = false)
private Date startDateTime;
@Temporal(TemporalType.TIMESTAMP)
@Column(nullable = true)
private Date endDateTime;
@Column
private String status;
@Temporal(TemporalType.TIMESTAMP)
@Column(nullable = false)
private Date modified;
@Temporal(TemporalType.TIMESTAMP)
@Column(nullable = false)
private Date created;
@OneToMany(fetch = FetchType.EAGER, mappedBy = "history", cascade = CascadeType.ALL)
@JoinColumns({
@JoinColumn(name = "HISTORY_ID", referencedColumnName = "HISTORY_ID", nullable = false)})
@CascadeOnDelete
List<HistoryRecord> entries;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Date getLastrun() {
return lastrun;
}
public void setLastrun(Date lastrun) {
this.lastrun = lastrun;
}
public Date getStartDateTime() {
return startDateTime;
}
public void setStartDateTime(Date startDateTime) {
this.startDateTime = startDateTime;
}
public Date getEndDateTime() {
return endDateTime;
}
public void setEndDateTime(Date endDateTime) {
this.endDateTime = endDateTime;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public Date getModified() {
return modified;
}
public void setModified(Date modified) {
this.modified = modified;
}
public Date getCreated() {
return created;
}
public void setCreated(Date created) {
this.created = created;
}
public List<HistoryRecord> getEntries() {
return entries;
}
public void setEntries(List<HistoryRecord> entries) {
this.entries = entries;
}
public Long getFkParentId() {
return fkParentId;
}
public void setFkParentId(Long fkParentId) {
this.fkParentId = fkParentId;
}
@PrePersist
public void prePersist() {
Date now = new Date();
created = now;
modified = now;
}
@PreUpdate
public void preUpdate() {
modified = new Date();
}
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("id: ").append(getId());
sb.append(", parentId: ").append(getFkParentId());
sb.append(", created: ").append(getCreated());
sb.append(", modified: ").append(getModified());
sb.append(", startDateTime: ").append(getStartDateTime());
sb.append(", endDateTime: ").append(getEndDateTime());
sb.append(", Status: ").append(getStatus());
return sb.toString();
}
}
As I want to be able to find all History entities by the field fkParentId I created an additional method in the repository:
@Query(value = "SELECT h FROM History h WHERE h.fkParentId = :fkParentId", nativeQuery = true)
public List<History> findAllByFkParentId(@Param("fkParentId") Long fkParentId);
Regardles if I simply use only the method name or add the Query annotation I end up with the following stacktrace:
[EL Finest]: query: 2019-12-04 16:23:16.73--UnitOfWork(2097614581)--Thread(Thread[main,5,main])--Execute query ReadAllQuery(referenceClass=History sql="SELECT h FROM History h WHERE h.fkParentId = :fkParentId")
[EL Warning]: 2019-12-04 16:23:16.741--UnitOfWork(2097614581)--Thread(Thread[main,5,main])--Local Exception Stack:
Exception [EclipseLink-6168] (Eclipse Persistence Services - 2.7.5.v20191016-ea124dd158): org.eclipse.persistence.exceptions.QueryException
Exception Description: Query failed to prepare, unexpected error occurred: [java.lang.NullPointerException].
Internal Exception: java.lang.NullPointerException
Query: ReadAllQuery(referenceClass=History sql="SELECT h FROM History h WHERE h.fkParentId = :fkParentId")
at org.eclipse.persistence.exceptions.QueryException.prepareFailed(QueryException.java:1598)
at org.eclipse.persistence.queries.DatabaseQuery.checkPrepare(DatabaseQuery.java:692)
at org.eclipse.persistence.queries.ObjectLevelReadQuery.checkPrepare(ObjectLevelReadQuery.java:968)
...
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:763)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:463)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:209)
Caused by: java.lang.NullPointerException
at org.eclipse.persistence.internal.helper.NonSynchronizedVector.addAll(NonSynchronizedVector.java:315)
at org.eclipse.persistence.queries.ObjectLevelReadQuery.getSelectionFields(ObjectLevelReadQuery.java:1808)
...
Debugging to the place where the exception is thrown shows me
that in the RelationalDescriptor which appears to be used for the
"getAllSelectionFields" the property allSelectionFields is null
which causes the NullpointerException.
How can solve this? Is there something missing in the annotated
entity class?
Many thanks in advance!
P.S.: I posted this question as well on stackoverflow, I hope it
is OK reposting it to this user mailing list.