Как написать статический блок инициализации в суперклассе, чтобы в наследниках использовать статические поля
Для этого потребуется в родительском классе:
- Объявить статическое поле String tableName.
- Написать статический блок инициализации, в котором инициализируется это поле. Для этого потребуется ссылка на класс наследника. Как в статическом контексте получить ссылку на класс наследника? Ведь я же не могу применять слово this. Мне нужно получить ссылку на класс наследника. Затем с помощью рефлексии получить аннотацию @Table у класса наследника, чтобы получить значение параметров name и schema у этой аннотации. Конкатенация значений параметров schema + name у аннотации @Table у класса наследника будет присвоена полю tableName. Эту реализацию хочу написать в родительском классе для того, чтобы просто обращаться к полю класса наследника. Например вот так: MonitorinCertificate.tableName. Я не знаю, как в статическом блоке инициализации в родительском классе получить ссылку на класс наследника.
Родительский класс
@MappedSuperclass
public class BaseEntity {
public static String tableName;
static {
// code
}
}
Ответы (2 шт):
Если вы по какой-то причине хотите на уровне интерфейса обязать классы знать свою таблицу, лучше выразить это через паттерн Шаблонный метод, а сами значения вынести в костанты во избежание дублирования.
abstract class BaseEntity {
@NotNull abstract String getName();
@NotNull abstract String getSchema();
public String getTableName() { return getSchema() + getName(); }
}
@Table(schema = MonitoringCertificate.SCHEMA, name = MonitoringCertificate.TABLE)
class MonitoringCertificate extends BaseEntity {
static final String SCHEMA = "foo";
static final String TABLE = "bar";
@NotNull @Override String getName() { return TABLE; }
@NotNull @Override String getSchema() { return SCHEMA;}
}
Но, с большой вероятностью, гораздо полезнее будет один раз просканировать аннотации и сложить все в Map<Class<?>, String>, где у каждого класса будет уже склеенная строка.
Вы формулируете условие вашей задачи так, что она становится бессмысленной. Причина простая: статика и наследование не пересекаются от слова совсем. Разумеется, невыполнимых задач нет. И если уж из статики вы никак не можете обратиться к нестатическим методам и полям, то наоборот это вполне возможно. Если предположить, что для всех сущностей у вас будет единственный супер класс (чтобы в этом был хоть какой-то смысл), то можно сделать примерно так:
abstract class BaseEntity {
private final static String BASE_PACKAGE_NAME = "base.package.name";
private final static String SCHEMA_NAME_DELIMETER = ".";
private final static Map<Class, String> MAP = scan();
public String getTableName() {
return MAP.get(this.getClass());
}
private static Map<Class, String> scan() {
Map<Class, String> result = new HashMap<>();
Set<Class<?>> classes = new Reflections(BASE_PACKAGE_NAME).getTypesAnnotatedWith(Table.class);
for (Class clazz : classes) {
Table annotation = (Table) clazz.getAnnotation(Table.class);
if (annotation == null) continue;
String tableName = annotation.schema().isEmpty() ?
annotation.name() : annotation.schema() + SCHEMA_NAME_DELIMETER + annotation.name();
result.put(clazz, tableName);
}
return result;
}
}
@Table(schema = "test_schema", name = "b_name")
class A extends BaseEntity{}
@Table(name = "b_name")
class B extends BaseEntity{}
Лично для меня это выглядит как костыль)) Даже если что-то подобное нужно реализовать, то уж точно этого не нужно делать в супер классе. Гораздо проще и логичнее делинировать это стороннему классу, который просканирует весь проект на старте, подготовит все данные и будет единым источником, где это можно запросить для любого объекта. Кроме того, можно 2навешать" перегруженные методы (в зависимоти от задачи), где, к примеру, будет подставляться дефолтная схема в случае ее отсутствия в аннотации. Выглядит это примерно так:
import com.geeks.rea.model.History;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import javax.persistence.Table;
import org.reflections.Reflections;
public class ReflectionUtil {
private final static String BASE_PACKAGE_NAME = "base.package.name";
private final static String SCHEMA_NAME_DELIMETER = ".";
private final static Map<Class, String> MAP = scan();
public static void main(String[] args) {
System.out.println(getTableName(History.class));
}
public static Optional<String> getTableName(Object entity) {
return Optional.ofNullable(MAP.get(entity.getClass()));
}
public static Optional<String> getTableName(Object entity, String defaultSchemaName) {
if (defaultSchemaName == null || defaultSchemaName.trim().isEmpty()) {
throw new RuntimeException("Default schemas name can't be NULL");
}
else return getTableName(entity).map(name -> name.contains(SCHEMA_NAME_DELIMETER) ?
name : defaultSchemaName.trim() + SCHEMA_NAME_DELIMETER + name);
}
private static Map<Class, String> scan() {
Map<Class, String> result = new HashMap<>();
Set<Class<?>> classes = new Reflections(BASE_PACKAGE_NAME).getTypesAnnotatedWith(Table.class);
for (Class clazz : classes) {
Table annotation = (Table) clazz.getAnnotation(Table.class);
if (annotation == null) continue;
String tableName = annotation.schema().isEmpty() ?
annotation.name() : annotation.schema() + SCHEMA_NAME_DELIMETER + annotation.name();
result.put(clazz, tableName);
}
return result;
}
}
P.S. для упрощения реализации рефлексии использовалась библиотека, которую можно найти здесь:
https://mvnrepository.com/artifact/org.reflections/reflections
Не забудьте указать в переменной BASE_PACKAGE_NAME базовое имя своего пакета