我们的应用程序过去通过 liquibase
config
或 <insert>
语句填充我们的 <update>
表条目。
这个 config
表是一个 jpa 实体并利用 hibernate-envers @Audit
功能。
也有相应的
config_audit
表格。
为了审核通过 liquibase 插入/更新的条目,我正在考虑切换到
<customChange>
变更集。例如
<changeSet id="my_id" author="author">
<customChange class="com.app.MyCustomTaskChange">
<param name="field1" value="val1" />
<param name="field2" value="val2" />
</customChange>
</changeSet>
public class MyCustomTaskChange implements CustomTaskChange, CustomTaskRollback {
@Setter
private String field1;
@Setter
private String field2;
@Setter
private static ConfigJpaRepository configJpaRepository;
@Override
public void execute(Database database) throws CustomChangeException {
configJpaRepository.save(new ConfigEntity(field1, field2));
}
//other methods excluded for clarity
明确地说,上述方法工作正常,条目按预期添加到
config
和 config_audit
表中;但是,我想知道是否有更优雅和面向未来的方式允许 liquibase <insert>
或 <update>
(或类似)语句锁定到 @Audit 功能中?
首先,我建议不要在数据库迁移中使用
spring
或 hibernate
的想法,无论它是否有吸引力,它都会使很多事情复杂化,主要问题是一旦您修改,此类迁移就会过时领域模型,所以,迟早hibernate
可能无法将数据写入数据库。在你的情况下,你能够以某种方式将存储库实例“注入”到 CustomTaskChange
类中只是因为 spring
在运行 liquibase 迁移之前初始化了实体管理器和存储库,但是在最近的 spring
版本中 spring-data-jpa
团队在 liquibase
和 hibernate
这样的方法应该行不通,但是可以做类似的事情(抱歉,不记得如何启用 envers
):
public class HibernateCustomTaskChange implements CustomTaskChange {
@Override
public void execute(Database database) throws CustomChangeException {
EntityManagerFactory entityManagerFactory = null;
EntityManager entityManager = null;
try {
entityManagerFactory = getEntityManagerFactory(database, MyEntity.class);
entityManager = entityManagerFactory.createEntityManager();
// some logic here
} finally {
if (entityManager != null) {
entityManager.close();
}
if (entityManagerFactory != null) {
entityManagerFactory.close();
}
}
}
protected EntityManagerFactory getEntityManagerFactory(Database database, Class<?>... entityClasses) {
DataSource dataSource = createDataSource(database);
ClassLoader classLoader = Scope.getCurrentScope().getClassLoader();
MutablePersistenceUnitInfo pui = new MutablePersistenceUnitInfo() {
@Override
public ClassLoader getNewTempClassLoader() {
return classLoader;
}
};
pui.setNonJtaDataSource(dataSource);
for (Class<?> entityClass : entityClasses) {
pui.addManagedClassName(entityClass.getName());
}
Map<String, Object> settings = new HashMap<>();
settings.put(AvailableSettings.CONNECTION_PROVIDER_DISABLES_AUTOCOMMIT, true);
pui.setPersistenceUnitName("liquibase");
return new EntityManagerFactoryBuilderImpl(
new PersistenceUnitInfoDescriptor(pui),
settings,
classLoader
).build();
}
protected DataSource createDataSource(Database database) {
Connection connection = ((JdbcConnection) database.getConnection()).getUnderlyingConnection();
connection = createConnectionProxy(connection);
return new SingleConnectionDataSource(connection, true);
}
protected Connection createConnectionProxy(Connection con) {
return (Connection) Proxy.newProxyInstance(
ConnectionProxy.class.getClassLoader(),
new Class<?>[]{ConnectionProxy.class},
new CommitSuppressInvocationHandler(con));
}
static class CommitSuppressInvocationHandler implements InvocationHandler {
private final Connection target;
public CommitSuppressInvocationHandler(Connection target) {
this.target = target;
}
@Override
@Nullable
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (ReflectionUtils.isEqualsMethod(method)) {
return (proxy == args[0]);
} else if (ReflectionUtils.isHashCodeMethod(method)) {
return System.identityHashCode(proxy);
} else if (method.getName().equals("unwrap")) {
if (((Class<?>) args[0]).isInstance(proxy)) {
return proxy;
}
} else if (method.getName().equals("isWrapperFor")) {
if (((Class<?>) args[0]).isInstance(proxy)) {
return true;
}
} else if (method.getName().equals("close")) {
return null;
} else if (method.getName().equals("commit")) {
return null;
} else if (method.getName().equals("isClosed")) {
return false;
} else if (method.getName().equals("getTargetConnection")) {
return this.target;
}
try {
return method.invoke(this.target, args);
} catch (InvocationTargetException ex) {
throw ex.getTargetException();
}
}
}
// some liquibase method omitted
}