envers
Spring Data JPA,Spring Security和Envers是我个人喜欢使用的库(我倾向于认为它们在各自的类别中被认为是同类中最好的)。 无论如何,我想实现一个我认为简单的用例:实体必须经过Envers审核,但是修订必须包含发起该操作的用户的身份。 尽管看起来很简单,但要实现这一目标我仍然要克服一些挑战。 本文列出了它们,并提供了可能的解决方案。
软件架构
我使用Spring MVC作为Web框架,配置为使用Spring Security。 为了简化我的开发,Spring Security配置了可识别的登录名/密码。 可以轻松地将其连接到更合适的后端。 本着同样的精神,我在H2内存数据库上的驱动程序管理器连接周围使用了数据源包装器。 只需通过Spring配置更改即可。
典型的流程由我的Spring MVC控制器处理,并传递到注入的服务,该服务管理Spring Data JPA存储库(访问数据库的组件)。
事实
在开发过程中,以下事实已被证明是一个障碍(阅读要克服的障碍):
- Spring Security 3.1.1.RELEASE(最新)使用Spring 3.0.7.RELEASE(不是最新的)
- Spring Data JPA使用(等待它)JPA2。因此,您必须使用
LocalContainerEntityManagerFactoryBean
参数化Spring配置,然后使用要使用的实现来配置EntityManager工厂bean(在本例中为Hibernate)。 一些传递的参数可跨不同的JPA实现移植,而其他地址则专门针对Envers,因此是完全不可移植的。
<beanid="transactionManager"class="org.springframework.orm.jpa.JpaTransactionManager">
<propertyname="jpaDialect">
<beanclass="org.springframework.orm.jpa.vendor.HibernateJpaDialect"/>
</property>
</bean>
<beanclass="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<propertyname="dataSource"ref="dataSource"/>
<propertyname="jpaVendorAdapter">
<beanclass="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<propertyname="generateDdl"value="true"/>
<propertyname="database"value="H2"/>
<propertyname="showSql"value="true"/>
</bean>
</property>
<propertyname="jpaProperties">
<props>
<propkey="org.hibernate.envers.auditTablePrefix"></prop>
<propkey="org.hibernate.envers.auditTableSuffix"> _HISTORY </prop>
</props>
</property>
</bean>
- Spring Data JPA确实以
Auditable
接口的形式提供了一些审计功能。 Auditables有createdBy
,createdDate
,modifiedBy
和modifiedDate
性能。 不幸的是,该库没有存储实体的先前状态:我们必须使用提供此功能的Envers - 疯狂地战斗之后,我意识到将Envers集成到Spring Data JPA中并不是一件容易的事,并且偶然发现了Spring Data Envers模块,它确实完成了这项工作。 我没有在任何存储库中找到可用的Maven工件,因此我克隆了Git存储库并构建了当前版本(然后是0.1.0.BUILD-SNAPSHOT)。 它为我提供了对该项目做出非常微薄贡献的机会。
从好的方面来说,尽管我搜索了所有示例,但最新版本的Hibernate不仅随Envers一起提供,而且由于使用了智能服务提供商 ,因此不需要注册Envers侦听器的任何配置。 您只需要在类路径上提供JAR,JAR本身就负责注册。 这使Envers集成更加简单。
如何
除了添加Spring Data Envers,几乎没有想象力,因为它是Envers的基本用法。
- 创建具有所需属性(用户)的修订实体:
@RevisionEntity(AuditingRevisionListener.class) @Entity publicclassAuditedRevisionEntityextendsDefaultRevisionEntity{ privatestaticfinallongserialVersionUID=1L; privateStringuser; publicStringgetUser(){ returnuser; } publicvoidsetUser(Stringuser){ this.user=user; } }
- 创建侦听器以从Spring Security上下文获取身份,并在修订实体上进行设置:
publicclassAuditingRevisionListenerimplementsRevisionListener{ @Override publicvoidnewRevision(ObjectrevisionEntity){ AuditedRevisionEntityauditedRevisionEntity=(AuditedRevisionEntity)revisionEntity; StringuserName=SecurityContextHolder.getContext().getAuthentication().getName(); auditedRevisionEntity.setUser(userName); } }
Presto,您完成了...
坏消息
……不是吗? 当写入数据库时,上述解决方案可以完美地工作。 麻烦之处在于尝试从那里获取信息,因为Spring Data API不允许这样做(还可以吗?)。 因此,基本上有三个选项(假设您关心检索:用户可能看不到这种信息,并且在需要访问数据时始终可以连接到数据库以进行查询):
- 创建代码以检索数据。 显然,您不能使用Spring Data JPA(您的实体缺少`user`字段,它是在运行时“创建”的)。 恕我直言,最好的方法是创建一个侦听器,该侦听器在已审核实体的读取查询中获取数据并返回增强型实体。 性能和设计方面的考虑因素都不支持这一点。
- 通过使用使其依赖于Spring Data内部的反射来修改API。 与以前一样,这是一个糟糕的选择,但出于教育目的,我这样做是:
Revisionsrevisions=stuffRepository.findRevisions(stuff.getId()); ListauditedRevisionEntities=newArrayList(); for(Revisionrevision:revisions.getContent()){ Fieldfield=ReflectionUtils.findField(Revision.class,"metadata"); // Oh, it's ugly! ReflectionUtils.makeAccessible(field); @SuppressWarnings("rawtypes") RevisionMetadatametadata=(RevisionMetadata)ReflectionUtils.getField(field,revision); AuditedRevisionEntityauditedRevisionEntity=(AuditedRevisionEntity)metadata.getDelegate(); // Do what your want with auditedRevisionEntity... }
- 最后但并非最不重要的一点是,您可以为Spring Data的代码做出贡献。 当然,您需要了解API以及内部知识,技能和时间,但这是值得的worth
结论
集成异构库几乎不是一个过渡。 对于Spring Data JPA和Envers,由于使用了Spring Data Envers库,因此就像馅饼一样容易。 但是,如果需要使审核数据可访问,则需要进一步集成。
这篇文章的来源是在这里 ,在Eclipse / Maven的格式。
翻译自: https://blog.frankel.ch/spring-data-spring-security-and-envers-integration/
envers