Dave Pradana
Dave Pradana

Reputation: 145

Retrieve a value from Database and set it as @Table Name value in Spring boot

I have a Class with @Table annotation, currently I directly set the Name value like this :

@Data
@Entity
@NoArgsConstructor
@Table(name = "US_STUDENTS" )
public class Student {

// some properties

}

I have a table in DB named MasterCountries which have a Column name - value = countryPrefix - "US_"

When the Spring boot app run, I want to take this data from the DB and use it as part of the @Table name value to make it like this :

@Data
@Entity
@NoArgsConstructor
@Table(name = countryPrefix + "STUDENTS" ) // so the end result will be "US_STUDENTS"
public class Student {

// some properties

}

I've google'd and still have not found a solution to this, the reason is because I want to make it dynamic, whenever this spring boot service need to be redeployed for other country, just need to retrieve the specific countryPrefix from the DB, so no need to change the code again for each country Deployment.

for the repository I set up an interface that extends JpaRepository as shown below :

public interface MasterCountriesRepository extends JpaRepository<CountriesSetting, Long> {

    CountriesSetting findByCountryCode(String countryCode);

}

and for the CountriesSetting entity :

@Data
@Entity
@NoArgsConstructor
@Table(name = "MasterCountries" )
public class CountriesSetting {

// some properties

}

Is this possible to achieve in spring JPA ? please ignore any mistype or minor syntax problem, as this is not the real code. Thanks in advance.

Upvotes: 2

Views: 1984

Answers (1)

rzwitserloot
rzwitserloot

Reputation: 103018

Annotations require that everything involved in their usage is a compile time constant. That's by java spec, you can't ask the compiler to forget about that restriction just for a moment.

Any expression is a 'compile time constant' if it is:

  • A literal... but null doesn't count!
  • Of the form X ! Y where X and Y are 'compile time constant', and ! is some operator, such as + or -.
  • A reference to a field which is [A] static, [B] final, [C] whose type is String or primitive, [D] whose initializing expression is 'compile time constant' (using this same definition, recursively).

Thus, Foo.x is compile time constant given:

class Foo {
   static final int x = 5 - 3 + Integer.MAX_VALUE - Bar.y;
}
class Bar {
   static final int y = 10;
}

but these aren't:

  • static final long z = System.currentTimeMillis();
  • static final String y = null;
  • static final String a = LocalDate.of(2020, 1, 1).toString();

Note that the third seems constant (the output of that expression is set in stone and will be the same on all VMs, forever), and yet doesn't count, as it isn't explicitly named in the java lang spec.

Given that you want to retrieve this from the database, what you want is impossible - as surely your intent is that the table name is not locked in during compilation, and yet the reason why the expression you feed to an annotation param must be compile time constant is that the compiler must lock it in.

So what is the solution?

Don't use the annotations. Annotations that are queried at runtime are usually a configuration shortcut. Generally there is a more roundabout way to achieve the same goal, calling APIs which default to 'I shall just read out the annotation value'.

Annotations can also be queried at compile time - lombok does that, as do Annotation Processors (e.g. processors that produce XML-based config files during your compilation run). Trivially it should be obvious to you that your entire plan cannot possibly work then - these are fundamentally 'at compile time' concepts.

Simpler suggestion

Thinking out of the box, perhaps:

Have a separate script that will run a CREATE OR REPLACE VIEW statement that creates a view with a singular name (just Students for example), that just mirrors a stated table (Such as US_Students), using ON INSERT and ON UPDATE and ON DELETE to also 'pass through' any attempt to insert/update/delete attempts on this view to end up affecting the US_Students table.

To 'change' the 'country', just run that script one-off and restart the java app, and voila.

How to make views and have them work with delete/insert/update depends on your DB engine. In both MySQL and postgres, a basic view on a simple SELECT without aggregates and joins and such 'just works'.

You're really just looking at:

CREATE OR REPLACE VIEW Students AS SELECT * FROM US_Students;

run that single statement and you're good to go.

Upvotes: 3

Related Questions