贾弗斯 "jv_commit" 不存在
Javers "jv_commit" doesn't exist
我正在尝试为我的项目安装 Javer。我正在使用 Hibernate JPA,我相信我已经正确配置了所有内容。
这是我的 Spring 配置:
<bean id="auditDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.audit.url}"/>
<property name="username" value="${jdbc.audit.username}"/>
<property name="password" value="${jdbc.audit.password}"/>
<property name="maxActive" value="100"/>
<property name="maxWait" value="1000"/>
<property name="poolPreparedStatements" value="true"/>
<property name="defaultAutoCommit" value="true"/>
<property name="validationQuery" value="${jdbc.validationQuery}"/>
<property name="testOnBorrow" value="true"/>
</bean>
<bean id="auditSessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean" destroy-method="destroy">
<property name="dataSource" ref="auditDataSource"/>
<property name="configLocation" value="classpath:hibernate.audit.cfg.xml"/>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">${hibernate.dialect}</prop>
<prop key="hibernate.query.substitutions">true 'Y', false 'N'</prop>
<prop key="hibernate.cache.use_second_level_cache">true</prop>
<prop key="hibernate.show_sql">false</prop>
<prop key="hibernate.format_sql">false</prop>
<prop key="hibernate.use_sql_comments">false</prop>
<!--<prop key="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</prop>-->
<prop key="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory</prop>
<!-- Hibernate Search index directory -->
<prop key="hibernate.search.default.indexBase">${app.search.index.basedir}</prop>
</props>
<!-- Turn batching off for better error messages under PostgreSQL -->
<!-- hibernate.jdbc.batch_size=0 -->
</property>
</bean>
<!-- Transaction manager for a single Hibernate SessionFactory (alternative to JTA) -->
<bean id="auditTransactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="auditSessionFactory"/>
</bean>
<bean id="javersRepoConnectionProvider" class="com.dsc.discus.ng.audit.javers.JaversRepoConnectionProvider">
<constructor-arg name="dataSource" ref="auditDataSource"/>
</bean>
<bean id="auditController" class="com.dsc.discus.ng.audit.javers.JaversAuditController">
<constructor-arg name="javersRepoConnectionProvider" ref="javersRepoConnectionProvider"/>
<constructor-arg name="transactionManager" ref="auditTransactionManager"/>
</bean>
JaversAuditController bean:
public class JaversAuditController extends AuditController {
private final JaversSqlRepository javersSqlRepository;
private final Javers javers;
public JaversAuditController(JaversRepoConnectionProvider javersRepoConnectionProvider, PlatformTransactionManager transactionManager) {
javersSqlRepository = SqlRepositoryBuilder.sqlRepository()
.withConnectionProvider(javersRepoConnectionProvider)
.withDialect(DialectName.MYSQL)
.build();
javers = TransactionalJaversBuilder.javers()
.withTxManager(transactionManager)
.withObjectAccessHook(new HibernateUnproxyObjectAccessHook())
.withMappingStyle(MappingStyle.BEAN)
.registerJaversRepository(javersSqlRepository)
.build();
}
@Override
public void addModified(BaseEntity entity) {
addModified(entity, null);
}
@Override
public void addModified(BaseEntity entity, Map<String, String> extraParams) {
if (javers != null && entity != null) {
if (extraParams != null) {
javers.commit(entity.getModifiedBy(), entity, extraParams);
}
else {
javers.commit(entity.getModifiedBy(), entity);
}
}
}
public JaversSqlRepository getJaversSqlRepository() {
return javersSqlRepository;
}
public Javers getJavers() {
return javers;
}
我必须使用 BEAN 映射样式,因为 JPA 注释位于实体的访问器上。
这是 JaversRepoConnectionProvider bean:
public class JaversRepoConnectionProvider implements ConnectionProvider {
private DataSource dataSource;
public JaversRepoConnectionProvider(DataSource dataSource) {
this.dataSource = dataSource;
}
@Override
public Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
}
我正在进行人工审核。也就是说,每次保存实体时,我都会调用 javers.commit() 方法。碰巧的是,我尝试保存的第一件事导致 MySQL 语法错误,指出 table "jv_commit" 不存在。文档指出会自动创建必要的 tables,所以我一定遗漏了一些东西。请提前告知并感谢。
截断的堆栈跟踪:
com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Table 'discus_eng_audit.jv_commit' doesn't exist
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at com.mysql.jdbc.Util.handleNewInstance(Util.java:411)
at com.mysql.jdbc.Util.getInstance(Util.java:386)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1054)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4237)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4169)
at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2617)
at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2778)
at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2825)
at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:2156)
at com.mysql.jdbc.PreparedStatement.executeQuery(PreparedStatement.java:2323)
at org.apache.commons.dbcp.DelegatingPreparedStatement.executeQuery(DelegatingPreparedStatement.java:96)
at org.apache.commons.dbcp.DelegatingPreparedStatement.executeQuery(DelegatingPreparedStatement.java:96)
at org.apache.commons.dbcp.DelegatingPreparedStatement.executeQuery(DelegatingPreparedStatement.java:96)
at org.polyjdbc.core.transaction.Transaction.executeQuery(Transaction.java:59)
at org.polyjdbc.core.query.TransactionalQueryRunner.queryCollection(TransactionalQueryRunner.java:86)
at org.polyjdbc.core.query.TransactionalQueryRunner.queryList(TransactionalQueryRunner.java:76)
at org.javers.repository.sql.PolyUtil.queryForBigDecimalList(PolyUtil.java:27)
at org.javers.repository.sql.PolyUtil.queryForOptionalBigDecimal(PolyUtil.java:36)
at org.javers.repository.sql.repositories.CommitMetadataRepository.selectMaxCommitId(CommitMetadataRepository.java:86)
at org.javers.repository.sql.repositories.CommitMetadataRepository.getCommitHeadId(CommitMetadataRepository.java:75)
at org.javers.repository.sql.JaversSqlRepository.getHeadId(JaversSqlRepository.java:76)
at org.javers.repository.api.JaversExtendedRepository.getHeadId(JaversExtendedRepository.java:137)
at org.javers.core.commit.CommitIdFactory.nextId(CommitIdFactory.java:26)
at org.javers.core.commit.CommitFactory.newCommitMetadata(CommitFactory.java:79)
at org.javers.core.commit.CommitFactory.create(CommitFactory.java:69)
at org.javers.core.JaversCore.commit(JaversCore.java:82)
at org.javers.core.JaversCore.commit(JaversCore.java:67)
at org.javers.spring.jpa.JaversTransactionalDecorator.commit(JaversTransactionalDecorator.java:58)
at com.dsc.discus.ng.audit.javers.JaversAuditController.addModified(JaversAuditController.java:56)
at com.dsc.discus.ng.audit.javers.JaversAuditController.addModified(JaversAuditController.java:46)
at com.dsc.discus.ng.model.persistence.dao.hibernate.BaseDaoHibernate.save(BaseDaoHibernate.java:146)
at com.dsc.discus.ng.model.persistence.dao.hibernate.BaseDaoHibernate.save(BaseDaoHibernate.java:26)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:302)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
事实证明,TransactionalJaversBuilder.javers().build() 方法不能像标准 JaversBuilder.javers().build() 方法那样确保创建表。我不确定这是否是 TransactionalJaversBuilder 中的错误。我的解决方法是在构建 Javers 对象后调用 ensureSchema():
public JaversAuditController(JaversRepoConnectionProvider javersRepoConnectionProvider, PlatformTransactionManager transactionManager) {
javersSqlRepository = SqlRepositoryBuilder.sqlRepository()
.withConnectionProvider(javersRepoConnectionProvider)
.withDialect(DialectName.MYSQL)
.build();
javers = TransactionalJaversBuilder.javers()
.withTxManager(transactionManager)
.withObjectAccessHook(new HibernateUnproxyObjectAccessHook())
.withMappingStyle(MappingStyle.BEAN)
.registerJaversRepository(javersSqlRepository)
.build();
javersSqlRepository.ensureSchema();
}
在 Spring 应用程序中,Javers 实例应注册为 Spring bean。 JaVers 使用 @PostConstruct
注释并且 Spring 在 txManager 就绪时调用 ensureSchema()
。
参见 JaversTransactionalDecorator 实现:
@PostConstruct
public void ensureSchema() {
TransactionTemplate tmpl = new TransactionTemplate(txManager);
tmpl.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
javersSqlRepository.ensureSchema();
}
});
}
在您的情况下,不会调用此方法,因为您在控制器中创建了 Javers 实例,而不是作为 Spring bean。您应该按照文档所述配置您的应用程序 - https://javers.org/documentation/spring-integration/#jpa-entity-manager-integration
我正在尝试为我的项目安装 Javer。我正在使用 Hibernate JPA,我相信我已经正确配置了所有内容。
这是我的 Spring 配置:
<bean id="auditDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.audit.url}"/>
<property name="username" value="${jdbc.audit.username}"/>
<property name="password" value="${jdbc.audit.password}"/>
<property name="maxActive" value="100"/>
<property name="maxWait" value="1000"/>
<property name="poolPreparedStatements" value="true"/>
<property name="defaultAutoCommit" value="true"/>
<property name="validationQuery" value="${jdbc.validationQuery}"/>
<property name="testOnBorrow" value="true"/>
</bean>
<bean id="auditSessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean" destroy-method="destroy">
<property name="dataSource" ref="auditDataSource"/>
<property name="configLocation" value="classpath:hibernate.audit.cfg.xml"/>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">${hibernate.dialect}</prop>
<prop key="hibernate.query.substitutions">true 'Y', false 'N'</prop>
<prop key="hibernate.cache.use_second_level_cache">true</prop>
<prop key="hibernate.show_sql">false</prop>
<prop key="hibernate.format_sql">false</prop>
<prop key="hibernate.use_sql_comments">false</prop>
<!--<prop key="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</prop>-->
<prop key="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory</prop>
<!-- Hibernate Search index directory -->
<prop key="hibernate.search.default.indexBase">${app.search.index.basedir}</prop>
</props>
<!-- Turn batching off for better error messages under PostgreSQL -->
<!-- hibernate.jdbc.batch_size=0 -->
</property>
</bean>
<!-- Transaction manager for a single Hibernate SessionFactory (alternative to JTA) -->
<bean id="auditTransactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="auditSessionFactory"/>
</bean>
<bean id="javersRepoConnectionProvider" class="com.dsc.discus.ng.audit.javers.JaversRepoConnectionProvider">
<constructor-arg name="dataSource" ref="auditDataSource"/>
</bean>
<bean id="auditController" class="com.dsc.discus.ng.audit.javers.JaversAuditController">
<constructor-arg name="javersRepoConnectionProvider" ref="javersRepoConnectionProvider"/>
<constructor-arg name="transactionManager" ref="auditTransactionManager"/>
</bean>
JaversAuditController bean:
public class JaversAuditController extends AuditController {
private final JaversSqlRepository javersSqlRepository;
private final Javers javers;
public JaversAuditController(JaversRepoConnectionProvider javersRepoConnectionProvider, PlatformTransactionManager transactionManager) {
javersSqlRepository = SqlRepositoryBuilder.sqlRepository()
.withConnectionProvider(javersRepoConnectionProvider)
.withDialect(DialectName.MYSQL)
.build();
javers = TransactionalJaversBuilder.javers()
.withTxManager(transactionManager)
.withObjectAccessHook(new HibernateUnproxyObjectAccessHook())
.withMappingStyle(MappingStyle.BEAN)
.registerJaversRepository(javersSqlRepository)
.build();
}
@Override
public void addModified(BaseEntity entity) {
addModified(entity, null);
}
@Override
public void addModified(BaseEntity entity, Map<String, String> extraParams) {
if (javers != null && entity != null) {
if (extraParams != null) {
javers.commit(entity.getModifiedBy(), entity, extraParams);
}
else {
javers.commit(entity.getModifiedBy(), entity);
}
}
}
public JaversSqlRepository getJaversSqlRepository() {
return javersSqlRepository;
}
public Javers getJavers() {
return javers;
}
我必须使用 BEAN 映射样式,因为 JPA 注释位于实体的访问器上。
这是 JaversRepoConnectionProvider bean:
public class JaversRepoConnectionProvider implements ConnectionProvider {
private DataSource dataSource;
public JaversRepoConnectionProvider(DataSource dataSource) {
this.dataSource = dataSource;
}
@Override
public Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
}
我正在进行人工审核。也就是说,每次保存实体时,我都会调用 javers.commit() 方法。碰巧的是,我尝试保存的第一件事导致 MySQL 语法错误,指出 table "jv_commit" 不存在。文档指出会自动创建必要的 tables,所以我一定遗漏了一些东西。请提前告知并感谢。
截断的堆栈跟踪:
com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Table 'discus_eng_audit.jv_commit' doesn't exist
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at com.mysql.jdbc.Util.handleNewInstance(Util.java:411)
at com.mysql.jdbc.Util.getInstance(Util.java:386)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1054)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4237)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4169)
at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2617)
at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2778)
at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2825)
at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:2156)
at com.mysql.jdbc.PreparedStatement.executeQuery(PreparedStatement.java:2323)
at org.apache.commons.dbcp.DelegatingPreparedStatement.executeQuery(DelegatingPreparedStatement.java:96)
at org.apache.commons.dbcp.DelegatingPreparedStatement.executeQuery(DelegatingPreparedStatement.java:96)
at org.apache.commons.dbcp.DelegatingPreparedStatement.executeQuery(DelegatingPreparedStatement.java:96)
at org.polyjdbc.core.transaction.Transaction.executeQuery(Transaction.java:59)
at org.polyjdbc.core.query.TransactionalQueryRunner.queryCollection(TransactionalQueryRunner.java:86)
at org.polyjdbc.core.query.TransactionalQueryRunner.queryList(TransactionalQueryRunner.java:76)
at org.javers.repository.sql.PolyUtil.queryForBigDecimalList(PolyUtil.java:27)
at org.javers.repository.sql.PolyUtil.queryForOptionalBigDecimal(PolyUtil.java:36)
at org.javers.repository.sql.repositories.CommitMetadataRepository.selectMaxCommitId(CommitMetadataRepository.java:86)
at org.javers.repository.sql.repositories.CommitMetadataRepository.getCommitHeadId(CommitMetadataRepository.java:75)
at org.javers.repository.sql.JaversSqlRepository.getHeadId(JaversSqlRepository.java:76)
at org.javers.repository.api.JaversExtendedRepository.getHeadId(JaversExtendedRepository.java:137)
at org.javers.core.commit.CommitIdFactory.nextId(CommitIdFactory.java:26)
at org.javers.core.commit.CommitFactory.newCommitMetadata(CommitFactory.java:79)
at org.javers.core.commit.CommitFactory.create(CommitFactory.java:69)
at org.javers.core.JaversCore.commit(JaversCore.java:82)
at org.javers.core.JaversCore.commit(JaversCore.java:67)
at org.javers.spring.jpa.JaversTransactionalDecorator.commit(JaversTransactionalDecorator.java:58)
at com.dsc.discus.ng.audit.javers.JaversAuditController.addModified(JaversAuditController.java:56)
at com.dsc.discus.ng.audit.javers.JaversAuditController.addModified(JaversAuditController.java:46)
at com.dsc.discus.ng.model.persistence.dao.hibernate.BaseDaoHibernate.save(BaseDaoHibernate.java:146)
at com.dsc.discus.ng.model.persistence.dao.hibernate.BaseDaoHibernate.save(BaseDaoHibernate.java:26)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:302)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
事实证明,TransactionalJaversBuilder.javers().build() 方法不能像标准 JaversBuilder.javers().build() 方法那样确保创建表。我不确定这是否是 TransactionalJaversBuilder 中的错误。我的解决方法是在构建 Javers 对象后调用 ensureSchema():
public JaversAuditController(JaversRepoConnectionProvider javersRepoConnectionProvider, PlatformTransactionManager transactionManager) {
javersSqlRepository = SqlRepositoryBuilder.sqlRepository()
.withConnectionProvider(javersRepoConnectionProvider)
.withDialect(DialectName.MYSQL)
.build();
javers = TransactionalJaversBuilder.javers()
.withTxManager(transactionManager)
.withObjectAccessHook(new HibernateUnproxyObjectAccessHook())
.withMappingStyle(MappingStyle.BEAN)
.registerJaversRepository(javersSqlRepository)
.build();
javersSqlRepository.ensureSchema();
}
在 Spring 应用程序中,Javers 实例应注册为 Spring bean。 JaVers 使用 @PostConstruct
注释并且 Spring 在 txManager 就绪时调用 ensureSchema()
。
参见 JaversTransactionalDecorator 实现:
@PostConstruct
public void ensureSchema() {
TransactionTemplate tmpl = new TransactionTemplate(txManager);
tmpl.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
javersSqlRepository.ensureSchema();
}
});
}
在您的情况下,不会调用此方法,因为您在控制器中创建了 Javers 实例,而不是作为 Spring bean。您应该按照文档所述配置您的应用程序 - https://javers.org/documentation/spring-integration/#jpa-entity-manager-integration