I was working on a project recently where I hit a peculiar problem. The problem was I wanted to map the same entity to different tables dynamically. In my case, I was using Postgres and I had an entity like the one below.
@Entity(name = "form_entry")
public class FormEntry {
// Other Stuff
@NotNull
private ObjectNode data;
// Other Stuff
}
As you can see the entity is mapped to a static table name form_entry
. What I wanted was to have a form_entry
table
for each form that was created, say form_1_entry
, form_2_entry
etc.
The problem however is that JPA doesn't provide any mechanism to do this. To top it off I was using Spring JPA Repositories. So unless absolutely required I wasn't writing any queries or even using the entity manager directly.
Of course I could write the queries but I didn't want to do that either. So I began prodding.
Interceptors to the rescue
public class HibernateInterceptor extends EmptyInterceptor {
@Override
public String onPrepareStatement(String sql) {
Long formId = RequestContextHolder.getFormId();
if (formId != null) {
return sql.replaceAll("form_entry",
"form_" + formId + "_entry");
}
return sql;
}
}
As you can see all you need to do is use the HibernateInterceptor and change the prepared statement to replace
form_entry
to form_1_entry
etc. and JPA wouldn't even know what hit it.
Ok, but what is the RequestContextHolder
@GetMapping(value = "/{formEntryId}")
public FormEntry getFormEntry(@PathVariable Long formId,
@PathVariable Long formEntryId) {
RequestContextHolder.setFormId(formId);
return formEntryService.getFormEntry(formId, formEntryId);
}
RequestContextHolder is just a thread-safe class that holds the form id (amongst other things). So you will have to set the form id before you call any JPA methods or use the Entity Manager. The name because i was using this in a Web App.
Please be advised though I did not end up using this solution, I decided to stick with a single table and instead choosing to add an indexed form id column.