0

I have an entity mapped to a table. This table usually has some columns on top of the mapped properties. For example if the entity is defined as:

@Entity
class MyEntity {
  private String a;
}

Then the table has column a. In one schema it may also have columns b and c, in another d, e and f. I thought it would be simple to override EntityManager's load method to load all the properties defined in MyEntity and subsequently load all the others into a Map<String, Object>, but I can't find a way to do it. Here's what I considered so far:

  1. I found this SO question, which led to this post which maybe solves the issue but requires loading the mapping on startup, which doesn't solve my problem since I'm constantly switching between schemas and therefore would have to reload the mapping over and over.
  2. Overriding RelationalMappingContext also assumes a constant schema.
  3. There should be a way to supply EntityManager as a bean but I can't figure out how and I'm not sure if that wouldn't run into the same issues as 1 & 2.
  4. I can do something complicated with bytecode manipulation but this would require having an Entity per schema, which is too complicated.
  5. So I'm forced to consider overriding all methods in CrudRepository and providing my own implementation. That would be very hacky and unwieldy.

Any other ideas?

Shadow
  • 33,525
  • 10
  • 51
  • 64
Johnny
  • 7,073
  • 9
  • 46
  • 72
  • How about the inheritance? Base class BaseEntity with property a. Another class extends BaseEntity and adds properties b and c. And so on. – Mar-Z Mar 20 '23 at 15:31
  • The issue is that the schema is not known in advance, only on application start. – Johnny Mar 20 '23 at 15:33
  • I think something like Hibernate is the wrong tool for your use case. Use plain JDBC, check via metadata which columns are actually present and map the columns manually. Hibernate/JPA has the requirement to know the database structure in advance. – vanje Mar 20 '23 at 16:13
  • Maybe MyBatis is able to process generic result sets. Look here: https://stackoverflow.com/questions/26918434/mybatis-query-result-in-a-hashmap – vanje Mar 20 '23 at 16:15

2 Answers2

0

you have two Option in this situation

  1. Use a NoSQL database: If you need to add columns dynamically and frequently, you might consider using a NoSQL database that supports dynamic schemas. NoSQL databases like MongoDB is designed to handle dynamic data and allow you to store documents or key-value pairs without enforcing a fixed schema.

  2. Use a JSON or XML column: Some databases like PostgreSQL or MySQL support JSON or XML columns that can store semi-structured data. You can store your dynamic data as a JSON or XML document in a single column and query it using SQL or NoSQL-like queries.

For example, if you are using PostgreSQL, you can define a JSON column in your my_table table like this:

ALTER TABLE my_table ADD COLUMN dynamic_data jsonb;

You can then insert JSON documents into the dynamic_data column and query them using PostgreSQL's JSON functions.

Alternatively, you can use Hibernate's @Type annotation to map a JSON or XML column to a Java object. For example:

@Entity
@Table(name = "my_table")
public class MyEntity {

   @Id
   @GeneratedValue(strategy = GenerationType.IDENTITY)
   private Long id;

   @Type(type = "jsonb")
   @Column(columnDefinition = "jsonb")
   private Map<String, Object> dynamicData;

}
Ramkumar Pillai
  • 154
  • 2
  • 8
0

In the end I went with Spring AOP. The cutpoint intercepts every repository method decorated with a custom annotation. When that happens the cutpoint method issues another query of SELECT * (in the case of reads) and populates the entity with the extra properties.

Johnny
  • 7,073
  • 9
  • 46
  • 72