Автор оригинала: Vlad Mihalcea.
Hibernate поставляется со многими дополнениями к стандартной спецификации JPA. Одним из таких примеров является аннотация @GeneratorType , которая позволяет настроить способ автоматического создания значения свойства заданной сущности.
Если вы используете данные Spring, вы можете просто использовать @CreatedBy и @LastModifiedBy аннотации, и свойства аннотированной сущности будут заполнены текущим зарегистрированным пользователем.
Если вы не используете данные Spring, то вы можете легко эмулировать то же поведение, используя специфичную для гибернации @GeneratorType аннотацию и Генератор значений механизм обратного вызова.
Предполагая, что у нас есть следующая таблица датчик в нашей реляционной базе данных:
Мы хотим отобразить это как объект JPA. Если имя может быть отображено как сущность @Id , а значение является просто @базовым свойством, как мы можем автоматизировать столбцы created_by и updated_by с использованием текущего зарегистрированного пользователя?
Для проверки предположим, что у нас есть следующая утилита ThreadLocal , в которой хранится текущий зарегистрированный пользователь:
public class LoggedUser {
private static final ThreadLocal userHolder =
new ThreadLocal<>();
public static void logIn(String user) {
userHolder.set(user);
}
public static void logOut() {
userHolder.remove();
}
public static String get() {
return userHolder.get();
}
}
В сервлете веб-приложения Filter метод LoggedUser.logIn может быть вызван с использованием текущего аутентифицированного пользователя , а метод LoggedUser.logOut вызывается сразу после возврата из внутренней цепочки Filter.doFilter вызов.
public class LoggedUserFilter implements Filter {
@Override
public void init(
FilterConfig filterConfig)
throws ServletException {
}
@Override
public void doFilter(
ServletRequest request,
ServletResponse response,
FilterChain filterChain)
throws IOException, ServletException {
try {
HttpServletRequest httpServletRequest =
(HttpServletRequest) request;
LoggedUser.logIn(
httpServletRequest.getRemoteUser()
);
filterChain.doFilter(request, response);
}
finally {
LoggedUser.logOut();
}
}
@Override
public void destroy() {
}
}
Теперь мы хотим передать текущего зарегистрированного пользователя в CreatedBy и UpdatedBy свойства нашей Сенсорной сущности, для этого мы создадим следующую утилиту ValueGenerator Hibernate:
public class LoggedUserGenerator
implements ValueGenerator {
@Override
public String generateValue(
Session session, Object owner) {
return LoggedUser.get();
}
}
С помощью интерфейса Генератор значений Hibernate позволяет нам настраивать способ создания данного свойства сущности. Теперь нам нужно только указать Hibernate использовать генератор Зарегистрированных пользователей для Созданных и Обновленных свойств нашей Сенсорной сущности.
@Entity(name = "Sensor")
@Table(name = "sensor")
public class Sensor {
@Id
@Column(name = "sensor_name")
private String name;
@Column(name = "sensor_value")
private String value;
@Column(name = "created_by")
@GeneratorType(
type = LoggedUserGenerator.class,
when = GenerationTime.INSERT
)
private String createdBy;
@Column(name = "updated_by")
@GeneratorType(
type = LoggedUserGenerator.class,
when = GenerationTime.ALWAYS
)
private String updatedBy;
//Getters and setters omitted for brevity
}
Как вы можете видеть, тип @generator позволяет нам сопоставлять свойства CreatedBy и UpdatedBy , чтобы Hibernate использовал LoggedUserGenerator для назначения свойства аннотированной сущности с использованием текущего зарегистрированного пользователя.
Атрибут when аннотации @GeneratorType указывает, следует ли назначать свойство сущности, когда сущность сохраняется (например, Время создания.ВСТАВИТЬ ) или изменить (например, Время создания.ВСЕГДА ).
Чтобы увидеть, как заполняется свойство CreatedBy при сохранении сущности, рассмотрим следующий тестовый случай:
LoggedUser.logIn("Alice");
doInJPA(entityManager -> {
Sensor ip = new Sensor();
ip.setName("ip");
ip.setValue("192.168.0.101");
entityManager.persist(ip);
executeSync(() -> {
LoggedUser.logIn("Bob");
doInJPA(_entityManager -> {
Sensor temperature = new Sensor();
temperature.setName("temperature");
temperature.setValue("32");
_entityManager.persist(temperature);
});
LoggedUser.logOut();
});
});
LoggedUser.logOut();
В основном потоке Алиса входит в систему и вставляет датчик ip , в то время как в другом потоке Боб входит в систему и вставляет датчик температуры .
При выполнении приведенного выше тестового случая Hibernate генерирует следующие инструкции SQL INSERT:
INSERT INTO sensor (
created_by,
updated_by,
sensor_value,
sensor_name
)
VALUES (
'Bob',
'Bob',
'32',
'temperature'
)
INSERT INTO sensor (
created_by,
updated_by,
sensor_value,
sensor_name
)
VALUES (
'Alice',
'Alice',
'192.168.0.101',
'ip'
)
Здесь мы можем сделать несколько замечаний:
- ВСТАВКА Боба выполняется первой, так как он сначала зафиксировал (и сбросил) свои изменения.
- Время
Поколение.ВСЕГДАстратегия свойстваUpdatedByзапускаетГенератор значенийкак для ВСТАВКИ, так и для ОБНОВЛЕНИЯ.
При изменении сущностей:
LoggedUser.logIn("Alice");
doInJPA(entityManager -> {
Sensor temperature = entityManager.find(
Sensor.class,
"temperature"
);
temperature.setValue("36");
executeSync(() -> {
LoggedUser.logIn("Bob");
doInJPA(_entityManager -> {
Sensor ip = _entityManager.find(
Sensor.class,
"ip"
);
ip.setValue("192.168.0.102");
});
LoggedUser.logOut();
});
});
LoggedUser.logOut();
Hibernate создает следующие инструкции обновления SQL:
UPDATE sensor
SET
created_by = 'Alice',
updated_by = 'Bob',
sensor_value = '192.168.0.102'
WHERE
sensor_name = 'ip'
UPDATE sensor
SET
created_by = 'Bob',
updated_by = 'Alice',
sensor_value = '36'
WHERE
sensor_name = 'temperature'
Отлично! Записи датчика были обновлены должным образом, и в столбце updated_by отображается пользователь, внесший изменения.
Как было продемонстрировано, Hibernate очень гибок, позволяя настраивать способ автоматического создания свойств сущностей. Используя аннотацию @GeneratorType и механизм обратного вызова Генератора значений , вы можете легко заполнить столбцы created_by и update_by таблицы без необходимости вручную заполнять эти свойства сущности самостоятельно.
Если вы используете данные Spring, вы можете сделать то же самое с аннотациями @CreatedBy и @LastModifiedBy , поскольку эта функция может быть интегрирована с механизмом аутентификации пользователей, определенным Spring Security, с помощью механизма AuditorAware .