SST*_*SST 11 java hibernate hibernate-envers
我们正在使用Hibernate-envers 3.6.3.Final,我正在使用@Audited注释正确生成表.我使用CustomRevisionEntity存储用户信息,CustomRevisionListenner也存储用户信息.但是,如果我尝试检索"用户名",它将返回以下错误.
org.hibernate.HibernateException: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here
at org.springframework.orm.hibernate3.SpringSessionContext.currentSession(SpringSessionContext.java:64) ~[spring-orm-3.2.6.RELEASE.jar:3.2.6.RELEASE]
Run Code Online (Sandbox Code Playgroud)
我的CustomRevisionEntity类,
@Entity
@Table(name = "revision_info")
@RevisionEntity(CustomEnversListener.class)
public class CustomRevisionEntity implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue
@RevisionNumber
private int id;
@RevisionTimestamp
private long timestamp;
private String username;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public long getTimestamp() {
return timestamp;
}
public void setTimestamp(long timestamp) {
this.timestamp = timestamp;
}
@Column(name = "username")
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
Run Code Online (Sandbox Code Playgroud)
CustomRevisionListener.java
public class CustomEnversListener implements RevisionListener {
public void newRevision(Object revisionEntity) {
CustomRevisionEntity customRevisionEntity = (CustomRevisionEntity) revisionEntity;
Authentication authentication = SecurityContextHolder.getContext()
.getAuthentication();
customRevisionEntity.setUsername(authentication.getName());
}
}
Run Code Online (Sandbox Code Playgroud)
我的表格如下,
mysql> select * from revision_info;
+----+---------------+-----------------+
| id | timestamp | username |
+----+---------------+-----------------+
| 1 | 1431693146030 | sky@test.com |
| 2 | 1431693150805 | ram@test.com |
| 3 | 1431693164895 | bobo@test.com |
+----+---------------+-----------------+
3 rows in set (0.02 sec)
Run Code Online (Sandbox Code Playgroud)
我能够使用"timeStamp"和"timeStamp"使用"rev"使用以下代码检索"rev",
AuditReader reader = AuditReaderFactory.get(session);
Date timestamp = reader.getRevisionDate(rev);
Number revision = reader.getRevisionNumberForDate(timestamp);
Run Code Online (Sandbox Code Playgroud)
但我无法使用hibernate查询使用自定义字段"username"值检索整行.
Criteria criteria = sessionFactory.getCurrentSession()
.createCriteria(CustomRevisionEntity.class)
.add(Restrictions.eq("id", rev));
Run Code Online (Sandbox Code Playgroud)
以上查询返回上述错误..如何解决?如何从revision_info表中检索值?
我的错误的完整堆栈跟踪是,
org.hibernate.HibernateException: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here
at org.springframework.orm.hibernate3.SpringSessionContext.currentSession(SpringSessionContext.java:64) ~[spring-orm-3.2.6.RELEASE.jar:3.2.6.RELEASE]
at org.hibernate.impl.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:687) ~[hibernate-core-3.6.3.Final.jar:3.6.3.Final]
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204) ~[spring-core-3.2.6.RELEASE.jar:3.2.6.RELEASE]
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:701) ~[spring-aop-3.2.6.RELEASE.jar:3.2.6.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150) ~[spring-aop-3.2.6.RELEASE.jar:3.2.6.RELEASE]
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:91) ~[spring-aop-3.2.6.RELEASE.jar:3.2.6.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) ~[spring-aop-3.2.6.RELEASE.jar:3.2.6.RELEASE]
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:634) ~[spring-aop-3.2.6.RELEASE.jar:3.2.6.RELEASE]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.6.0_31]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) ~[na:1.6.0_31]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.6.0_31]
at java.lang.reflect.Method.invoke(Method.java:622) ~[na:1.6.0_31]
at ognl.OgnlRuntime.invokeMethod(OgnlRuntime.java:870) ~[ognl-3.0.6.jar:na]
at ognl.OgnlRuntime.callAppropriateMethod(OgnlRuntime.java:1293) ~[ognl-3.0.6.jar:na]
at ognl.ObjectMethodAccessor.callMethod(ObjectMethodAccessor.java:68) ~[ognl-3.0.6.jar:na]
at com.opensymphony.xwork2.ognl.accessor.XWorkMethodAccessor.callMethodWithDebugInfo(XWorkMethodAccessor.java:117) [xwork-core-2.3.20.jar:2.3.20]
at com.opensymphony.xwork2.ognl.accessor.XWorkMethodAccessor.callMethod(XWorkMethodAccessor.java:108) [xwork-core-2.3.20.jar:2.3.20]
at ognl.OgnlRuntime.callMethod(OgnlRuntime.java:1369) ~[ognl-3.0.6.jar:na]
at ognl.ASTMethod.getValueBody(ASTMethod.java:90) ~[ognl-3.0.6.jar:na]
at ognl.SimpleNode.evaluateGetValueBody(SimpleNode.java:212) ~[ognl-3.0.6.jar:na]
at ognl.SimpleNode.getValue(SimpleNode.java:258) ~[ognl-3.0.6.jar:na]
at ognl.Ognl.getValue(Ognl.java:494) ~[ognl-3.0.6.jar:na]
at ognl.Ognl.getValue(Ognl.java:458) ~[ognl-3.0.6.jar:na]
at com.opensymphony.xwork2.ognl.OgnlUtil$2.execute(OgnlUtil.java:309) ~[xwork-core-2.3.20.jar:2.3.20]
at com.opensymphony.xwork2.ognl.OgnlUtil.compileAndExecute(OgnlUtil.java:340) ~[xwork-core-2.3.20.jar:2.3.20]
at com.opensymphony.xwork2.ognl.OgnlUtil.getValue(OgnlUtil.java:307) ~[xwork-core-2.3.20.jar:2.3.20]
at com.opensymphony.xwork2.DefaultActionInvocation.invokeAction(DefaultActionInvocation.java:423) ~[xwork-core-2.3.20.jar:2.3.20]
at com.opensymphony.xwork2.DefaultActionInvocation.invokeActionOnly(DefaultActionInvocation.java:287) ~[xwork-core-2.3.20.jar:2.3.20]
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:250) ~[xwork-core-2.3.20.jar:2.3.20]
at org.apache.struts2.interceptor.DeprecationInterceptor.intercept(DeprecationInterceptor.java:41) ~[struts2-core-2.3.20.jar:2.3.20]
Run Code Online (Sandbox Code Playgroud)
我的弹簧配置如下,
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="xxxSessionFactory"/>
</bean>
<tx:advice id="customRevisionEntityAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*" read-only="false" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="crePointcut"
expression="execution(* bla.bla.CustomRevisionEntity.*(..))"/>
<aop:advisor advice-ref="customRevisionEntityAdvice" pointcut-ref="crePointcut"/>
</aop:config>
Run Code Online (Sandbox Code Playgroud)
我的application-content.xml包含以下内容......
<bean id="auditEventListener" class="org.hibernate.envers.event.AuditEventListener" />
<bean id="xxxSessionFactory"
class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="annotatedClasses">
<list>
<value>bla.bla.domain.Myclass</value>
</list>
</property>
<property name="eventListeners">
<map>
<entry key="post-insert" value-ref="auditEventListener"/>
<entry key="post-update" value-ref="auditEventListener"/>
<entry key="post-delete" value-ref="auditEventListener"/>
<entry key="pre-collection-update" value-ref="auditEventListener"/>
<entry key="pre-collection-remove" value-ref="auditEventListener"/>
<entry key="post-collection-recreate" value-ref="auditEventListener"/>
</map>
</property>
Run Code Online (Sandbox Code Playgroud)
你可以查询CustomRevisionEntity使用AuditReader
AuditReader auditReader = AuditReaderFactory.get(entityManager);
//Here you find the revision number that you want
Number revisionNumber = getRevisionNumber(auditReader);
//then you use the auditReader :-)
CustomRevisionEntity cRevEntity = auditReader.findRevision(
CustomRevisionEntity.class, revisionNumber );
//Then you can just get your Username
String userName = cRevEntity.getUsername();
Run Code Online (Sandbox Code Playgroud)
这是方法签名
/**
* A helper method; should be used only if a custom revision entity is used. See also {@link RevisionEntity}.
* @param revisionEntityClass Class of the revision entity. Should be annotated with {@link RevisionEntity}.
* @param revision Number of the revision for which to get the data.
* @return Entity containing data for the given revision.
* @throws IllegalArgumentException If revision is less or equal to 0 or if the class of the revision entity
* is invalid.
* @throws RevisionDoesNotExistException If the revision does not exist.
* @throws IllegalStateException If the associated entity manager is closed.
*/
<T> T findRevision(Class<T> revisionEntityClass, Number revision) throws IllegalArgumentException,
RevisionDoesNotExistException, IllegalStateException;
Run Code Online (Sandbox Code Playgroud)
从Hibernate-envers 3.6.3.Final源代码,这是在AuditReaderImpl.java第193行实现的:
@SuppressWarnings({"unchecked"})
public <T> T findRevision(Class<T> revisionEntityClass, Number revision) throws IllegalArgumentException,
RevisionDoesNotExistException, IllegalStateException {
checkNotNull(revision, "Entity revision");
checkPositive(revision, "Entity revision");
checkSession();
Set<Number> revisions = new HashSet<Number>(1);
revisions.add(revision);
Query query = verCfg.getRevisionInfoQueryCreator().getRevisionsQuery(session, revisions);
try {
T revisionData = (T) query.uniqueResult();
if (revisionData == null) {
throw new RevisionDoesNotExistException(revision);
}
return revisionData;
} catch (NonUniqueResultException e) {
throw new AuditException(e);
}
}
Run Code Online (Sandbox Code Playgroud)
查看堆栈跟踪,您将错过一些Spring事务配置.使用声明性配置或使用注释.
您需要在配置xml中声明事务的使用,这是使用AOP切入点完成的.看一下这个例子,你可以看到它首先设置TransactionManager它DataSource,然后声明每个方法x.y.service.FooService都需要一个事务
<!-- ensure that the above transactional advice runs for any execution
of an operation defined by the FooService interface -->
<aop:config>
<aop:pointcut id="fooServiceOperation" expression="execution(* x.y.service.FooService.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="fooServiceOperation"/>
</aop:config>
Run Code Online (Sandbox Code Playgroud)
您提供的配置缺少 AOP配置.为方便起见,您可以配置特定包中的每个类以使用事务.
<aop:config>
<aop:pointcut id="fooServiceMethods" expression="execution(* x.y.service.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="fooServiceMethods"/>
</aop:config>
Run Code Online (Sandbox Code Playgroud)
看到那只是expression="execution(* x.y.service.*.*(..))"改变了.
Spring幸好提供了一种更简单的方法来声明@Transactional方法的用法,只需将类,接口或方法注释为@Transactional
// the service class that we want to make transactional
@Transactional
public class DefaultFooService implements FooService {
Foo getFoo(String fooName);
Foo getFoo(String fooName, String barName);
void insertFoo(Foo foo);
void updateFoo(Foo foo);
}
Run Code Online (Sandbox Code Playgroud)
之后,我们需要配置spring来扫描我们的代码,寻找@Transactional,以便在需要时生成正确的代理bean实例.
<!-- enable the configuration of transactional behavior based on annotations -->
<tx:annotation-driven transaction-manager="txManager"/><!-- a PlatformTransactionManager is still required -->
Run Code Online (Sandbox Code Playgroud)
以下是@Transactional 的完整示例以及关于事务的spring配置的参考.
Hibernate 3需要一个特殊的配置来使用Envers,你需要在你的persistence.xml.例
<property name="hibernate.ejb.event.post-insert" value="org.hibernate.ejb.event.EJB3PostInsertEventListener,org.hibernate.envers.event.AuditEventListener" />
<property name="hibernate.ejb.event.post-update" value="org.hibernate.ejb.event.EJB3PostUpdateEventListener,org.hibernate.envers.event.AuditEventListener" />
<property name="hibernate.ejb.event.post-delete" value="org.hibernate.ejb.event.EJB3PostDeleteEventListener,org.hibernate.envers.event.AuditEventListener" />
<property name="hibernate.ejb.event.pre-collection-update" value="org.hibernate.envers.event.AuditEventListener" />
<property name="hibernate.ejb.event.pre-collection-remove" value="org.hibernate.envers.event.AuditEventListener" />
<property name="hibernate.ejb.event.post-collection-recreate" value="org.hibernate.envers.event.AuditEventListener" />
Run Code Online (Sandbox Code Playgroud)
如果你没有一个persistence.xml中还是hibernate.cfg.xml,你声明SessionFactory,它只是工作,你需要编辑Spring配置像这样
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
....
<property name="eventListeners">
<map>
<entry key="post-insert" value-ref="auditListener"/>
<entry key="post-update" value-ref="auditListener"/>
<entry key="post-delete" value-ref="auditListener"/>
<entry key="pre-collection-update" value-ref="auditListener"/>
<entry key="pre-collection-remove" value-ref="auditListener"/>
<entry key="post-collection-recreate" value-ref="auditListener"/>
</map>
</property>
...
</bean>
<bean id="auditListener" class="org.hibernate.envers.event.AuditEventListener"/>
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1342 次 |
| 最近记录: |